X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FAbstract.pm;h=95867df028bf9e070f2a67199a7fd413107e3b63;hb=5c5cb82f4776a8fd2f0c43230fd5a85d74b9aebc;hp=476ca94e1acfd5d8d8d618a0d466e4f3575850c1;hpb=28605008bc0a369d4da57ef504679e52a50953a9;p=scpubgit%2FQ-Branch.git diff --git a/lib/SQL/Abstract.pm b/lib/SQL/Abstract.pm index 476ca94..95867df 100644 --- a/lib/SQL/Abstract.pm +++ b/lib/SQL/Abstract.pm @@ -8,7 +8,7 @@ use List::Util (); use Scalar::Util (); use Exporter 'import'; -our @EXPORT_OK = qw(is_plain_value is_literal_value); +our @EXPORT_OK = qw(is_plain_value is_literal_value is_undef_value); BEGIN { if ($] < 5.009_005) { @@ -146,15 +146,14 @@ our %Defaults = ( values => '_expand_values', }, expand_op => { - 'between' => '_expand_between', - 'not_between' => '_expand_between', - 'in' => '_expand_in', - 'not_in' => '_expand_in', - 'nest' => '_expand_nest', + (map +($_ => __PACKAGE__->make_binop_expander('_expand_between')), + qw(between not_between)), + (map +($_ => __PACKAGE__->make_binop_expander('_expand_in')), + qw(in not_in)), (map +($_ => '_expand_op_andor'), ('and', 'or')), (map +($_ => '_expand_op_is'), ('is', 'is_not')), - 'ident' => '_expand_ident', - 'value' => '_expand_value', + (map +($_ => __PACKAGE__->make_unop_expander("_expand_${_}")), + qw(ident value nest)), }, render => { (map +($_, "_render_$_"), @@ -307,22 +306,39 @@ sub _ext_rw { return $self; } +sub make_unop_expander { + my (undef, $exp) = @_; + sub { + my ($self, $name, $body, $k) = @_; + return $self->_expand_hashpair_cmp($k, { "-${name}" => $body }) + if defined($k); + return $self->$exp($name, $body); + } +} + +sub make_binop_expander { + my (undef, $exp) = @_; + sub { + my ($self, $name, $body, $k) = @_; + $k = shift @{$body = [ @$body ]} unless defined $k; + $k = ref($k) ? $k : { -ident => $k }; + return $self->$exp($name, $body, $k); + } +} + BEGIN { foreach my $type (qw( expand op_expand render op_render clause_expand clause_render )) { my $name = join '_', reverse split '_', $type; my $singular = "${type}er"; - eval qq{sub ${singular} { shift->_ext_rw($name => \@_) }; 1 } + + eval qq{sub ${singular} { shift->${singular}s(\@_) }; 1 } or die "Method builder failed for ${singular}: $@"; eval qq{sub wrap_${singular} { - my (\$self, \$key, \$builder) = \@_; - my \$orig = \$self->_ext_rw('${name}', \$key); - \$self->_ext_rw( - '${name}', \$key, - \$builder->(\$orig, '${name}', \$key) - ); + shift->wrap_${singular}s(\@_) }; 1 } or die "Method builder failed for wrap_${singular}: $@"; + eval qq{sub ${singular}s { my (\$self, \@args) = \@_; while (my (\$this_key, \$this_value) = splice(\@args, 0, 2)) { @@ -344,6 +360,20 @@ BEGIN { eval qq{sub ${singular}_list { sort keys %{\$_[0]->{\$name}} }; 1; } or die "Method builder failed for ${singular}_list: $@"; } + foreach my $singular (qw(unop_expander binop_expander)) { + eval qq{sub ${singular} { shift->${singular}s(\@_) }; 1 } + or die "Method builder failed for ${singular}: $@"; + eval qq{sub ${singular}s { + my (\$self, \@args) = \@_; + while (my (\$this_key, \$this_value) = splice(\@args, 0, 2)) { + \$self->_ext_rw( + expand_op => \$this_key, + \$self->make_${singular}(\$this_value), + ); + } + return \$self; + }; 1 } or die "Method builder failed for ${singular}s: $@"; + } } sub register_op { $_[0]->{is_op}{$_[1]} = 1; $_[0] } @@ -424,7 +454,6 @@ sub _expand_insert_clause_from { if (ref($data) eq 'HASH' and (keys(%$data))[0] =~ /^-/) { return $self->expand_expr($data); } - return $data if ref($data) eq 'HASH' and $data->{-row}; my ($f_aqt, $v_aqt) = $self->_expand_insert_values($data); return ( from => { -values => [ $v_aqt ] }, @@ -1174,10 +1203,7 @@ sub _expand_func { } sub _expand_ident { - my ($self, undef, $body, $k) = @_; - return $self->_expand_hashpair_cmp( - $k, { -ident => $body } - ) if defined($k); + my ($self, undef, $body) = @_; unless (defined($body) or (ref($body) and ref($body) eq 'ARRAY')) { puke "-ident requires a single plain scalar argument (a quotable identifier) or an arrayref of identifier parts"; } @@ -1191,9 +1217,6 @@ sub _expand_ident { } sub _expand_value { - return $_[0]->_expand_hashpair_cmp( - $_[3], { -value => $_[2] }, - ) if defined($_[3]); +{ -bind => [ our $Cur_Col_Meta, $_[2] ] }; } @@ -1288,7 +1311,6 @@ sub _expand_op_is { sub _expand_between { my ($self, $op, $vv, $k) = @_; - $k = shift @{$vv = [ @$vv ]} unless defined $k; my @rhs = map $self->_expand_expr($_), ref($vv) eq 'ARRAY' ? @$vv : $vv; unless ( @@ -1300,14 +1322,13 @@ sub _expand_between { } return +{ -op => [ $op, - $self->expand_expr(ref($k) ? $k : { -ident => $k }), + $self->expand_expr($k), map $self->expand_expr($_, -value), @rhs ] } } sub _expand_in { my ($self, $op, $vv, $k) = @_; - $k = shift @{$vv = [ @$vv ]} unless defined $k; if (my $literal = is_literal_value($vv)) { my ($sql, @bind) = @$literal; my $opened_sql = $self->_open_outer_paren($sql); @@ -2629,6 +2650,10 @@ module: On failure returns C, on success returns an B reference containing the unpacked version of the supplied literal SQL and bind values. +=head2 is_undef_value + +Tests for undef, whether expanded or not. + =head1 WHERE CLAUSES =head2 Introduction @@ -3284,7 +3309,9 @@ forms. Examples: -=head1 SPECIAL OPERATORS +=head1 OLD EXTENSION SYSTEM + +=head2 SPECIAL OPERATORS my $sqlmaker = SQL::Abstract->new(special_ops => [ { @@ -3369,7 +3396,7 @@ of the MATCH .. AGAINST syntax for MySQL ]); -=head1 UNARY OPERATORS +=head2 UNARY OPERATORS my $sqlmaker = SQL::Abstract->new(unary_ops => [ { @@ -3421,6 +3448,105 @@ When supplied with a coderef, it is called as: =back +=head1 NEW EXTENSION SYSTEM + +See L for concepts. + +=head2 expander + +=head2 expanders + +=head2 op_expander + +=head2 op_expanders + +=head2 clause_expander + +=head2 clause_expanders + + $sqla->expander('name' => sub { ... }); + $sqla->expanders('name1' => sub { ... }, 'name2' => sub { ... }); + +=head2 expander_list + +=head2 op_expander_list + +=head2 clause_expander_list + + my @names = $sqla->expander_list; + +=head2 wrap_expander + +=head2 wrap_expanders + +=head2 wrap_op_expander + +=head2 wrap_op_expanders + +=head2 wrap_clause_expander + +=head2 wrap_clause_expanders + + $sqla->wrap_expander('name' => sub { my ($orig) = @_; sub { ... } }); + $sqla->wrap_expanders( + 'name1' => sub { my ($orig1) = @_; sub { ... } }, + 'name2' => sub { my ($orig2) = @_; sub { ... } }, + ); + +=head2 renderer + +=head2 renderers + +=head2 op_renderer + +=head2 op_renderers + +=head2 clause_renderer + +=head2 clause_renderers + + $sqla->renderer('name' => sub { ... }); + $sqla->renderers('name1' => sub { ... }, 'name2' => sub { ... }); + +=head2 renderer_list + +=head2 op_renderer_list + +=head2 clause_renderer_list + + my @names = $sqla->renderer_list; + +=head2 wrap_renderer + +=head2 wrap_renderers + +=head2 wrap_op_renderer + +=head2 wrap_op_renderers + +=head2 wrap_clause_renderer + +=head2 wrap_clause_renderers + + $sqla->wrap_renderer('name' => sub { my ($orig) = @_; sub { ... } }); + $sqla->wrap_renderers( + 'name1' => sub { my ($orig1) = @_; sub { ... } }, + 'name2' => sub { my ($orig2) = @_; sub { ... } }, + ); + +=head2 clauses_of + + my @clauses = $sqla->clauses_of('select'); + $sqla->clauses_of(select => \@new_clauses); + $sqla->clauses_of(select => sub { + my (undef, @old_clauses) = @_; + ... + return @new_clauses; + }); + +=head2 statement_list + + my @list = $sqla->statement_list; =head1 PERFORMANCE