bomb.status = ?
[ 'unexploded' ]
-Not:
+
+Prefix unop:
+
+ # expr
+ { -op => [ '-', { -ident => 'foo' } ] }
+
+ # query
+ - foo
+ []
+
+Not as special case parenthesised unop:
# expr
{ -op => [ 'not', { -ident => 'explosive' } ] }
VALUES (1, 2), (3, 4)
[]
+=head2 keyword
+
+ # expr
+ { -keyword => 'insert_into' }
+
+ # query
+ INSERT INTO
+ []
+
=head2 statement types
AQT node types are also provided for C<select>, C<insert>, C<update> and
id OP ?
[ 'value' ]
+If the value is undef, attempts to convert equality and like ops to IS NULL,
+and inequality and not like to IS NOT NULL:
+
+ # expr
+ { id => { '!=' => undef } }
+
+ # aqt
+ { -op => [ 'is_not_null', { -ident => [ 'id' ] } ] }
+
+ # query
+ id IS NOT NULL
+ []
+
=head3 identifier hashpair w/simple value
Equivalent to a hashtriple with an op of '='.
=head2 arrayref expr
+An arrayref becomes a C<-or> over its contents. Arrayrefs, hashrefs and
+literals are all expanded and added to the clauses of the C<-or>. If the
+arrayref contains a scalar it's treated as the key of a hashpair and the
+next element as the value.
+
+ # expr
+ [ { x => 1 }, [ { y => 2 }, { z => 3 } ], 'key', 'value', \"lit()" ]
+
+ # aqt
+ { -op => [
+ 'or',
+ { -op => [ '=', { -ident => [ 'x' ] }, { -bind => [ 'x', 1 ] } ] },
+ { -op => [
+ 'or', {
+ -op => [ '=', { -ident => [ 'y' ] }, { -bind => [ 'y', 2 ] } ]
+ }, {
+ -op => [ '=', { -ident => [ 'z' ] }, { -bind => [ 'z', 3 ] } ]
+ },
+ ] }, { -op =>
+ [
+ '=', { -ident => [ 'key' ] },
+ { -bind => [ 'key', 'value' ] },
+ ]
+ },
+ { -literal => [ 'lit()' ] },
+ ] }
+
+ # query
+ ( x = ? OR ( y = ? OR z = ? ) OR key = ? OR lit() )
+ [ 1, 2, 3, 'value' ]
+
+=head1 Default Expanders
+
+=head2 bool
+
+Turns the old -bool syntax into the value expression, i.e.
+
+ # expr
+ { -bool => { -ident => 'foo' } }
+
+ # aqt
+ { -ident => [ 'foo' ] }
+
+ # query
+ foo
+ []
+
+behaves the same way as the now-directly-supported
+
+ # expr
+ { -ident => 'foo' }
+
+ # aqt
+ { -ident => [ 'foo' ] }
+
+ # query
+ foo
+ []
+
+=head2 row
+
+Expands the elements of the value arrayref:
+
+ # expr
+ { -row => [ 1, { -ident => 'foo' }, 2, 3 ] }
+
+ # aqt
+ { -row => [
+ { -bind => [ undef, 1 ] }, { -ident => [ 'foo' ] },
+ { -bind => [ undef, 2 ] }, { -bind => [ undef, 3 ] },
+ ] }
+
+ # query
+ (?, foo, ?, ?)
+ [ 1, 2, 3 ]
+
+=head2 op
+
+If an expander is registered for the op name, delegates to the expander; if
+not, expands the argument values:
+
+ # expr
+ { -op => [ 'ident', 'foo.bar' ] }
+
+ # aqt
+ { -ident => [ 'foo', 'bar' ] }
+
+ # query
+ foo.bar
+ []
+
+ # expr
+ { -op => [ '=', { -ident => 'foo' }, 3 ] }
+
+ # aqt
+ { -op => [ '=', { -ident => [ 'foo' ] }, { -bind => [ undef, 3 ] } ] }
+
+ # query
+ foo = ?
+ [ 3 ]
+
+=head2 func
+
+Expands the argument values:
+
+ # expr
+ { -func => [ 'coalesce', { -ident => 'thing' }, 'fallback' ] }
+
+ # aqt
+ { -func => [
+ 'coalesce', { -ident => [ 'thing' ] },
+ { -bind => [ undef, 'fallback' ] },
+ ] }
+
+ # query
+ COALESCE(thing, ?)
+ [ 'fallback' ]
+
+=head2 values
+
+A hashref value is expanded as an expression:
+
+ # expr
+ { -values => { -row => [ 1, 2 ] } }
+
+ # aqt
+ { -values => [
+ { -row => [ { -bind => [ undef, 1 ] }, { -bind => [ undef, 2 ] } ] }
+ ] }
+
+ # query
+ VALUES (?, ?)
+ [ 1, 2 ]
+
+An arrayref value's elements are either expressions or arrayrefs to be
+treated as rows:
+
+ # expr
+ { -values => [ { -row => [ 1, 2 ] }, [ 3, 4 ] ] }
+
+ # aqt
+ { -values => [
+ { -row => [ { -bind => [ undef, 1 ] }, { -bind => [ undef, 2 ] } ] },
+ { -row => [ { -bind => [ undef, 3 ] }, { -bind => [ undef, 4 ] } ] },
+ ] }
+
+ # query
+ VALUES (?, ?), (?, ?)
+ [ 1, 2, 3, 4 ]
+
+=head2 between op
+
+The RHS of between must either be a pair of exprs/plain values, or a single
+literal expr:
+
+ # expr
+ { -between => [ 'size', 3, { -ident => 'max_size' } ] }
+
+ # aqt
+ { -op => [
+ 'between', { -ident => [ 'size' ] }, { -bind => [ undef, 3 ] },
+ { -ident => [ 'max_size' ] },
+ ] }
+
+ # query
+ ( size BETWEEN ? AND max_size )
+ [ 3 ]
+
+ # expr
+ { size => { -between => [ 3, { -ident => 'max_size' } ] } }
+
+ # aqt
+ { -op => [
+ 'between', { -ident => [ 'size' ] }, { -bind => [ 'size', 3 ] },
+ { -ident => [ 'max_size' ] },
+ ] }
+
+ # query
+ ( size BETWEEN ? AND max_size )
+ [ 3 ]
+
+ # expr
+ { size => { -between => \"3 AND 7" } }
+
+ # aqt
+ { -op =>
+ [
+ 'between', { -ident => [ 'size' ] },
+ { -literal => [ '3 AND 7' ] },
+ ]
+ }
+
+ # query
+ ( size BETWEEN 3 AND 7 )
+ []
+
+not_between is also expanded:
+
+ # expr
+ { size => { -not_between => [ 3, 7 ] } }
+
+ # aqt
+ { -op => [
+ 'not_between', { -ident => [ 'size' ] },
+ { -bind => [ 'size', 3 ] }, { -bind => [ 'size', 7 ] },
+ ] }
+
+ # query
+ ( size NOT BETWEEN ? AND ? )
+ [ 3, 7 ]
+
+=head2 in op
+
+The RHS of in/not_in is either an expr/value or an arrayref of
+exprs/values:
+
+ # expr
+ { foo => { -in => [ 1, 2 ] } }
+
+ # aqt
+ { -op => [
+ 'in', { -ident => [ 'foo' ] }, { -bind => [ 'foo', 1 ] },
+ { -bind => [ 'foo', 2 ] },
+ ] }
+
+ # query
+ foo IN ( ?, ? )
+ [ 1, 2 ]
+
+ # expr
+ { bar => { -not_in => \"(1, 2)" } }
+
+ # aqt
+ { -op =>
+ [ 'not_in', { -ident => [ 'bar' ] }, { -literal => [ '1, 2' ] } ]
+ }
+
+ # query
+ bar NOT IN ( 1, 2 )
+ []
+
+A non-trivial LHS is expanded with ident as the default rather than value:
+
+ # expr
+ { -in => [
+ { -row => [ 'x', 'y' ] }, { -row => [ 1, 2 ] },
+ { -row => [ 3, 4 ] },
+ ] }
+
+ # aqt
+ { -op => [
+ 'in', { -row => [ { -ident => [ 'x' ] }, { -ident => [ 'y' ] } ] },
+ { -row => [ { -bind => [ undef, 1 ] }, { -bind => [ undef, 2 ] } ] },
+ { -row => [ { -bind => [ undef, 3 ] }, { -bind => [ undef, 4 ] } ] },
+ ] }
+
+ # query
+ (x, y) IN ( (?, ?), (?, ?) )
+ [ 1, 2, 3, 4 ]
+
+=head2 and/or ops
+
+expands the same way as a plain arrayref/hashref expression but with the
+logic type set to the op name.
+
+=head2 is op
+
+Expands is and is_not to null checks, RHS value must be undef:
+
+ # expr
+ { -is => [ 'foo', undef ] }
+
+ # aqt
+ { -op => [ 'is_null', { -ident => [ 'foo' ] } ] }
+
+ # query
+ foo IS NULL
+ []
+
+ # expr
+ { bar => { -is_not => undef } }
+
+ # aqt
+ { -op => [ 'is_not_null', { -ident => [ 'bar' ] } ] }
+
+ # query
+ bar IS NOT NULL
+ []
+
+=head2 ident op
+
+Expands a string ident to an arrayref by splitting on the configured
+separator, almost always '.':
+
+ # expr
+ { -ident => 'foo.bar' }
+
+ # aqt
+ { -ident => [ 'foo', 'bar' ] }
+
+ # query
+ foo.bar
+ []
+
+=head2 value op
+
+Expands to a bind node with the currently applicable column name if known:
+
+ # expr
+ { foo => { '=' => { -value => 3 } } }
+
+ # aqt
+ { -op => [ '=', { -ident => [ 'foo' ] }, { -bind => [ 'foo', 3 ] } ] }
+
+ # query
+ foo = ?
+ [ 3 ]
+
+=head1 Query Types
+
+=head2 select
+
+A select node accepts select, from, where and order_by clauses.
+
+The select clause is expanded as a list expression with a -ident default:
+
+ # expr
+ { -select => { _ => [ 'foo', 'bar', { -count => 'baz' } ] } }
+
+ # aqt
+ { -select => { select => { -op => [
+ ',', { -ident => [ 'foo' ] }, { -ident => [ 'bar' ] },
+ { -func => [ 'count', { -ident => [ 'baz' ] } ] },
+ ] } } }
+
+ # query
+ SELECT foo, bar, COUNT(baz)
+ []
+
+The from clause is expanded as a list expression with a -ident default:
+
+ # expr
+ { -select => {
+ from => [ 'schema1.table1', { -ident => [ 'schema2', 'table2' ] } ]
+ } }
+
+ # aqt
+ { -select => { from => { -from_list => [
+ { -ident => [ 'schema1', 'table1' ] },
+ { -ident => [ 'schema2', 'table2' ] },
+ ] } } }
+
+ # query
+ FROM schema1.table1, schema2.table2
+ []
+
+The where clause is expanded as a plain expression:
+
+ # expr
+ { -select => { where => { foo => 3 } } }
+
+ # aqt
+ { -select => { where => {
+ -op => [ '=', { -ident => [ 'foo' ] }, { -bind => [ 'foo', 3 ] } ]
+ } } }
+
+ # query
+ WHERE foo = ?
+ [ 3 ]
+
+The order_by clause expands as a list expression at top level, but a hashref
+element may be either an expr or a hashpair with key -asc or -desc to indicate
+an order by direction:
+
+ # expr
+ { -select =>
+ { order_by => [ 'foo', { -desc => 'bar' }, { -max => 'baz' } ] }
+ }
+
+ # aqt
+ { -select => { order_by => { -op => [
+ ',', { -ident => [ 'foo' ] }, {
+ -op => [ ',', { -op => [ 'desc', { -ident => [ 'bar' ] } ] } ]
+ }, { -func => [ 'max', { -ident => [ 'baz' ] } ] },
+ ] } } }
+
+ # query
+ ORDER BY foo, bar DESC, MAX(baz)
+ []
+
+=head2
+
+An insert node accepts an into/target clause, a fields clause, a values/from
+clause, and a returning clause.
+
+The target clause is expanded with an ident default.
+
+The fields clause is expanded as a list expression if an arrayref, and
+otherwise passed through.
+
+The from clause may either be an expr, a literal, an arrayref of column
+values, or a hashref mapping colum names to values.
+
+The returning clause is expanded as a list expr with an ident default.
+
+ # expr
+ { -insert => {
+ into => 'foo',
+ returning => 'id',
+ values => { bar => 'yay', baz => 'argh' },
+ } }
+
+ # aqt
+ { -insert => {
+ fields =>
+ { -row => [ { -ident => [ 'bar' ] }, { -ident => [ 'baz' ] } ] },
+ from => { -values => [ { -row => [
+ { -bind => [ 'bar', 'yay' ] },
+ { -bind => [ 'baz', 'argh' ] },
+ ] } ] },
+ returning => { -op => [ ',', { -ident => [ 'id' ] } ] },
+ target => { -op => [ ',', { -ident => [ 'foo' ] } ] },
+ } }
+
+ # query
+ INSERT INTO foo (bar, baz) VALUES (?, ?) RETURNING id
+ [ 'yay', 'argh' ]
+
+ # expr
+ { -insert => {
+ fields => [ 'bar', 'baz' ],
+ from => { -select => { _ => [ 'bar', 'baz' ], from => 'other' } },
+ into => 'foo',
+ } }
+
+ # aqt
+ { -insert => {
+ fields => { -row => [ { -op =>
+ [ ',', { -ident => [ 'bar' ] }, { -ident => [ 'baz' ] } ]
+ } ] },
+ from => { -select => {
+ from => { -from_list => [ { -ident => [ 'other' ] } ] },
+ select => { -op =>
+ [ ',', { -ident => [ 'bar' ] }, { -ident => [ 'baz' ] } ]
+ },
+ } },
+ target => { -op => [ ',', { -ident => [ 'foo' ] } ] },
+ } }
+
+ # query
+ INSERT INTO foo (bar, baz) SELECT bar, baz FROM other
+ []
+
+=head2 update
+
+An update node accepts update/target (either may be used at expansion time),
+set, where, and returning clauses.
+
+The target clause is expanded with an ident default.
+
+The set clause (if not already a list expr) is expanded as a hashref where
+the keys are identifiers to be set and the values are exprs/values.
+
+The where clauses is expanded as a normal expr.
+
+The returning clause is expanded as a list expr with an ident default.
+
+ # expr
+ { -update => {
+ _ => 'foo',
+ returning => [ 'id', 'baz' ],
+ set => { bar => 3, baz => { baz => { '+' => 1 } } },
+ where => { -not => { -ident => 'quux' } },
+ } }
+
+ # aqt
+ { -update => {
+ returning =>
+ {
+ -op => [ ',', { -ident => [ 'id' ] }, { -ident => [ 'baz' ] } ]
+ },
+ set => { -op => [
+ ',', { -op =>
+ [ '=', { -ident => [ 'bar' ] }, { -bind => [ 'bar', 3 ] } ]
+ }, { -op => [
+ '=', { -ident => [ 'baz' ] }, { -op => [
+ '+', { -ident => [ 'baz' ] },
+ { -bind => [ 'baz', 1 ] },
+ ] },
+ ] },
+ ] },
+ target => { -from_list => [ { -ident => [ 'foo' ] } ] },
+ where => { -op => [ 'not', { -ident => [ 'quux' ] } ] },
+ } }
+
+ # query
+ UPDATE foo SET bar = ?, baz = baz + ? WHERE (NOT quux) RETURNING id, baz
+ [ 3, 1 ]
+
+=head2 delete
+
+delete accepts from/target, where, and returning clauses.
+
+The target clause is expanded with an ident default.
+
+The where clauses is expanded as a normal expr.
+
+The returning clause is expanded as a list expr with an ident default.
+
+ # expr
+ { -delete => {
+ from => 'foo',
+ returning => 'id',
+ where => { bar => { '<' => 10 } },
+ } }
+
+ # aqt
+ { -delete => {
+ returning => { -op => [ ',', { -ident => [ 'id' ] } ] },
+ target => { -op => [ ',', { -ident => [ 'foo' ] } ] },
+ where => { -op =>
+ [ '<', { -ident => [ 'bar' ] }, { -bind => [ 'bar', 10 ] } ]
+ },
+ } }
+
+ # query
+ DELETE FROM foo WHERE bar < ? RETURNING id
+ [ 10 ]
+
=cut