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