... In my current project, I needed to extend the 'convert' functionality of SQL-Abstract-1.22 in order to support two kinds of behavior: 1) convert procedures that take multiple parameters, such as: locale_upper( name, 'en_US' ) 2) convert procedures that apply to some columns only, as in the following snippet: WHERE locale_upper(name,'en_US') LIKE locale_upper($1,'en_US') AND age = 32 The simplest interface extension that I could came up with that supports these requirements was as follows: $criteria->{convert} = ['locale_upper', q('en_US') ]; OR $criteria->{convert} = { proc => ['locale_upper', q('en_US') ], labels => [qw( name description )], }; I was able to hack SQL-Abstract-1.22 today by introducing changes to sub _convert($) {...} only, as you can see in the following patch. After applying this patch, SQL-Abstract-1.22 passes the four tests that come with the distribution, which I hope means that I have not balatantly broken major existing behavior. I am likely to have introduced some new bugs, however, as I have just sewn the patch today and the code has not been reviewed. ... --- SQL-Abstract-1.22-orig.pm 2006-11-30 19:12:14.000000000 +0200 +++ SQL-Abstract-1.22-fenL.pm 2007-03-15 17:53:57.000000000 +0200 @@ -209,10 +209,78 @@ sub _quote { # Conversion, if applicable sub _convert ($) { - my $self = shift; - return @_ unless $self->{convert}; - my $conv = $self->_sqlcase($self->{convert}); - my @ret = map { $conv.'('.$_.')' } @_; + my ($self, @args) = @_; + + return @args unless $self->{convert}; + + if (! $self->{convert_has_been_init}) { + $self->{convert_has_been_init} = 1; + + my $conv = $self->{convert}; + my ($params, $labels) = ([], []); + + if (ref( $conv ) eq 'ARRAY') { + if (! @$conv) { + die q(convert arrayref refers to an empty array); + return @args; + } + ($conv, @$params) = @$conv; + } + elsif (ref( $conv ) eq 'HASH') { + my $proc = $conv->{proc}; + if (defined $conv->{labels}) { + if (ref( $conv->{labels} ) ne 'ARRAY') { + die q(val for key 'labels' must be an arrayref); + ## return @args; + } + $labels = $conv->{labels}; + } + if (! $proc) { + die q(required elem 'proc' missing in convert hash); + ## return @args; + } + if (! ref( $proc )) { + $conv = $proc; + } + elsif (ref( $proc ) eq 'ARRAY') { + if (! @$proc) { + die q(convert proc aref refers to an empty arr); + ## return @args; + } + ($conv, @$params) = @$proc; + } + else { + die q(convert hash elem 'proc' must be scalar/aref); + ## return @args; + } + } + $self->{convert} = $conv; + $self->{convert_params} = $params; + $self->{convert_labels} = { map { uc( $_ ) => 1 } @$labels }; + } + my $conv = $self->{convert}; + $conv = $self->_sqlcase( $conv ); + my @ret = (); + + ARG: + for my $arg (@args) { + if ($arg eq '?') { + if (not defined $self->{convert_current_label}) { + die q(request to convert '?' when no label has been seen yet); + } + } + else { + $self->{convert_current_label} = $arg; + } + if (keys %{ $self->{convert_labels} }) { + if (not $self->{convert_labels}->{uc $self->{convert_current_label}}) { + warn 'Ignoring _convert() for label ' . $self->{convert_current_label}; + push @ret, $arg; + next ARG; + } + } + push @ret, sprintf '%s(%s%s)', $conv, $arg, map { ",$_" } @{ $self->{convert_params} }; + } return wantarray ? @ret : $ret[0]; }