doc is op expander
[scpubgit/Q-Branch.git] / lib / SQL / Abstract / Reference.pm
index ad27a77..343f60a 100644 (file)
@@ -146,7 +146,17 @@ Standard binop:
   bomb.status = ?
   [ 'unexploded' ]
 
-Not:
+
+Prefix unop:
+
+  # expr
+  { -op => [ '-', { -ident => 'foo' } ] }
+
+  # query
+  - foo
+  []
+
+Not as special case parenthesised unop:
 
   # expr
   { -op => [ 'not', { -ident => 'explosive' } ] }
@@ -229,6 +239,15 @@ Comma (use -row for parens):
   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
@@ -279,6 +298,19 @@ an expander has been registered for that node type:
   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 '='.
@@ -562,4 +594,293 @@ is short hand for:
 
 =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
+  []
+
 =cut