X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=scpubgit%2FQ-Branch.git;a=blobdiff_plain;f=lib%2FSQL%2FAbstract%2FReference.pm;h=257fd735ed7313bfef805798b244b4243a449108;hp=ff020b3276c0a02b6057b1eb3660fbd67e1909e8;hb=f583cadcb747f7193d919b9861bf9c2cf5cfa662;hpb=dee79057e5d03dd6c0715d577eb22b7a057ef146 diff --git a/lib/SQL/Abstract/Reference.pm b/lib/SQL/Abstract/Reference.pm index ff020b3..257fd73 100644 --- a/lib/SQL/Abstract/Reference.pm +++ b/lib/SQL/Abstract/Reference.pm @@ -371,7 +371,7 @@ Directly appended to the key, remember you need to provide an operator: # expr { id => \[ - "= seriously(?, ?, ?, ?, ?)", + "= seriously(?, ?, ?, ?)", "use", "-ident", "and", @@ -381,11 +381,11 @@ Directly appended to the key, remember you need to provide an operator: # aqt { -literal => - [ 'id = seriously(?, ?, ?, ?, ?)', 'use', -ident => 'and', '-func' ] + [ 'id = seriously(?, ?, ?, ?)', 'use', -ident => 'and', '-func' ] } # query - id = seriously(?, ?, ?, ?, ?) + id = seriously(?, ?, ?, ?) [ 'use', -ident => 'and', '-func' ] (you may absolutely use this when there's no built-in expression type for @@ -744,6 +744,31 @@ treated as rows: VALUES (?, ?), (?, ?) [ 1, 2, 3, 4 ] +=head2 list + +Expects a value or an arrayref of values, expands them, and returns just +the expanded aqt for a single entry or a comma operator for multiple: + + # expr + { -list => [ { -ident => 'foo' } ] } + + # aqt + { -op => [ ',', { -ident => [ 'foo' ] } ] } + + # query + foo + [] + + # expr + { -list => [ { -ident => 'foo' }, { -ident => 'bar' } ] } + + # aqt + { -op => [ ',', { -ident => [ 'foo' ] }, { -ident => [ 'bar' ] } ] } + + # query + foo, bar + [] + =head2 between op The RHS of between must either be a pair of exprs/plain values, or a single @@ -790,7 +815,7 @@ literal expr: ( size BETWEEN 3 AND 7 ) [] -C is also expanded: +not_between is also expanded: # expr { size => { -not_between => [ 3, 7 ] } } @@ -805,4 +830,322 @@ C is also expanded: ( 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 => { -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 => { -ident => [ 'other' ] }, + select => { -op => + [ ',', { -ident => [ 'bar' ] }, { -ident => [ 'baz' ] } ] + }, + } }, + target => { -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 => { -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