From: Matt S Trout Date: Wed, 25 Sep 2019 18:56:06 +0000 (+0000) Subject: extra definitions entries, examples for ident type hashpairs X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=29f96af045b2d52916f3d9a6e2cd2494b19d2b54;p=scpubgit%2FQ-Branch.git extra definitions entries, examples for ident type hashpairs --- diff --git a/lib/SQL/Abstract/Reference.pm b/lib/SQL/Abstract/Reference.pm index e700b44..d8f6763 100644 --- a/lib/SQL/Abstract/Reference.pm +++ b/lib/SQL/Abstract/Reference.pm @@ -33,11 +33,49 @@ plus bind values for passing to DBI, ala: If you see a comment before a code block saying C<# query>, the SQL + bind array is what's being described. +=head2 Expander + +An expander subroutine is written as: + + sub { + my ($sqla, $name, $value, $k) = @_; + ... + return $aqt; + } + +$name is the expr node type for node expanders, the op name for op +expanders, and the clause name for clause expanders. + +$value is the body of the thing being expanded + +If an op expander is being called as the binary operator in a L +expression, $k will be the hash key to be used as the left hand side +identifier. + +This can trivially be converted to an C type AQT node with: + + my $ident = $sqla->expand_expr({ -ident => $k }); + +=head2 Renderer + +A renderer subroutine looks like: + + sub { + my ($sqla, $type, $value) = @_; + ... + $sqla->join_query_parts($join, @parts); + } + +and can be registered on a per-type, per-op or per-clause basis. + =head1 AQT node types An AQT node consists of a hashref with a single key, whose name is C<-type> where 'type' is the node type, and whose value is the data for the node. +The following is an explanation of the built-in AQT type renderers; +additional renderers can be registered as part of the extension system. + =head2 literal # expr @@ -198,6 +236,8 @@ C. These types are handled by the clauses system as discussed later. =head1 Expressions +=head2 node expr + The simplest expression is just an AQT node: # expr @@ -223,4 +263,179 @@ an expander has been registered for that node type: foo.bar [] -=head2 +=head2 identifier hashpair types + +=head3 hashtriple + + # expr + { id => { op => 'value' } } + + # aqt + { -op => + [ 'op', { -ident => [ 'id' ] }, { -bind => [ 'id', 'value' ] } ] + } + + # query + id OP ? + [ 'value' ] + +=head3 identifier hashpair w/simple value + +Equivalent to a hashtriple with an op of '='. + + # expr + { id => 'value' } + + # aqt + { + -op => [ '=', { -ident => [ 'id' ] }, { -bind => [ 'id', 'value' ] } ] + } + + # query + id = ? + [ 'value' ] + +(an object value will also follow this code path) + +=head3 identifier hashpair w/undef RHS + +Converted to IS NULL : + + # expr + { id => undef } + + # aqt + { -op => [ 'is_null', { -ident => [ 'id' ] } ] } + + # query + id IS NULL + [] + +=head3 identifier hashpair w/literal RHS + +Directly appended to the key, remember you need to provide an operator: + + # expr + { id => \"= dont_try_this_at_home" } + + # aqt + { -literal => [ 'id = dont_try_this_at_home' ] } + + # query + id = dont_try_this_at_home + [] + + # expr + { id => \[ + "= seriously(?, ?, ?, ?, ?)", + "use", + "-ident", + "and", + "-func", + ] + } + + # aqt + { -literal => + [ 'id = seriously(?, ?, ?, ?, ?)', 'use', -ident => 'and', '-func' ] + } + + # query + id = seriously(?, ?, ?, ?, ?) + [ 'use', -ident => 'and', '-func' ] + +(you may absolutely use this when there's no built-in expression type for +what you need and registering a custom one would be more hassle than it's +worth, but, y'know, do try and avoid it) + +=head3 identifier hashpair w/arrayref value + +Becomes equivalent to a -or over an arrayref of hashrefs with the identifier +as key and the member of the original arrayref as the value: + + # expr + { id => [ 3, 4, { '>' => 12 } ] } + + # aqt + { -op => [ + 'or', + { -op => [ '=', { -ident => [ 'id' ] }, { -bind => [ 'id', 3 ] } ] }, + { -op => [ '=', { -ident => [ 'id' ] }, { -bind => [ 'id', 4 ] } ] }, + { + -op => [ '>', { -ident => [ 'id' ] }, { -bind => [ 'id', 12 ] } ] + }, + ] } + + # query + ( id = ? OR id = ? OR id > ? ) + [ 3, 4, 12 ] + + # expr + { -or => [ { id => 3 }, { id => 4 }, { id => { '>' => 12 } } ] } + + # aqt + { -op => [ + 'or', + { -op => [ '=', { -ident => [ 'id' ] }, { -bind => [ 'id', 3 ] } ] }, + { -op => [ '=', { -ident => [ 'id' ] }, { -bind => [ 'id', 4 ] } ] }, + { + -op => [ '>', { -ident => [ 'id' ] }, { -bind => [ 'id', 12 ] } ] + }, + ] } + + # query + ( id = ? OR id = ? OR id > ? ) + [ 3, 4, 12 ] + +Special Case: If the first element of the arrayref is -or or -and, that's +used as the top level logic op: + + # expr + { id => [ -and => { '>' => 3 }, { '<' => 6 } ] } + + # aqt + { -op => [ + 'and', + { -op => [ '>', { -ident => [ 'id' ] }, { -bind => [ 'id', 3 ] } ] }, + { -op => [ '<', { -ident => [ 'id' ] }, { -bind => [ 'id', 6 ] } ] }, + ] } + + # query + ( id > ? AND id < ? ) + [ 3, 6 ] + +=head3 identifier hashpair w/hashref value + +Becomes equivalent to a -and over an arrayref of hashtriples constructed +with the identifier as the key and each key/value pair of the original +hashref as the vlue: + + # expr + { id => { '<' => 4, '>' => 3 } } + + # aqt + { -op => [ + 'and', + { -op => [ '<', { -ident => [ 'id' ] }, { -bind => [ 'id', 4 ] } ] }, + { -op => [ '>', { -ident => [ 'id' ] }, { -bind => [ 'id', 3 ] } ] }, + ] } + + # query + ( id < ? AND id > ? ) + [ 4, 3 ] + + # expr + { -and => [ { id => { '<' => 4 } }, { id => { '>' => 3 } } ] } + + # aqt + { -op => [ + 'and', + { -op => [ '<', { -ident => [ 'id' ] }, { -bind => [ 'id', 4 ] } ] }, + { -op => [ '>', { -ident => [ 'id' ] }, { -bind => [ 'id', 3 ] } ] }, + ] } + + # query + ( id < ? AND id > ? ) + [ 4, 3 ] + +=cut