doc improvements, restructuring and adding explanations for the -and/-or stuff, undoc...
[dbsrgits/SQL-Abstract.git] / lib / SQL / Abstract.pm
index c0de4d5..6437528 100644 (file)
@@ -499,11 +499,17 @@ sub _where_hashpair_ARRAYREF {
     $self->_debug("ARRAY($k) means distribute over elements");
 
     # put apart first element if it is an operator (-and, -or)
-    my $op = $v[0] =~ /^-/ ? shift @v : undef;
-    $self->_debug("OP($op) reinjected into the distributed array") if $op;
-
+    my $op = ($v[0] =~ /^ - (?: AND|OR ) $/ix
+      ? shift @v
+      : ''
+    );
     my @distributed = map { {$k =>  $_} } @v;
-    unshift @distributed, $op if $op;
+
+    if ($op) {
+      $self->_debug("OP($op) reinjected into the distributed array");
+      unshift @distributed, $op;
+    }
+
     my $logic = $op ? substr($op, 1) : '';
 
     return $self->_recurse_where(\@distributed, $logic);
@@ -1313,7 +1319,8 @@ By default these are C<1=1> and C<1=0>.
 =item logic
 
 This determines the default logical operator for multiple WHERE
-statements in arrays. By default it is "or", meaning that a WHERE
+statements in arrays or hashes. If absent, the default logic is "or"
+for arrays, and "and" for hashes. This means that a WHERE
 array of the form:
 
     @where = (
@@ -1321,7 +1328,7 @@ array of the form:
         event_date => {'<=', '4/24/03'}, 
     );
 
-Will generate SQL like this:
+will generate SQL like this:
 
     WHERE event_date >= '2/13/99' OR event_date <= '4/24/03'
 
@@ -1335,10 +1342,10 @@ Which will change the above C<WHERE> to:
     WHERE event_date >= '2/13/99' AND event_date <= '4/24/03'
 
 The logic can also be changed locally by inserting
-an extra first element in the array :
+a modifier in front of an arrayref :
 
-    @where = (-and => event_date => {'>=', '2/13/99'}, 
-                      event_date => {'<=', '4/24/03'} );
+    @where = (-and => [event_date => {'>=', '2/13/99'}, 
+                       event_date => {'<=', '4/24/03'} ]);
 
 See the L</"WHERE CLAUSES"> section for explanations.
 
@@ -1623,8 +1630,8 @@ This simple code will create the following:
     $stmt = "WHERE user = ? AND ( status = ? OR status = ? OR status = ? )";
     @bind = ('nwiger', 'assigned', 'in-progress', 'pending');
 
-An empty arrayref will be considered a logical false and
-will generate 0=1.
+A field associated to an empty arrayref will be considered a 
+logical false and will generate 0=1.
 
 =head2 Key-value pairs
 
@@ -1733,29 +1740,6 @@ Here is a quick list of equivalencies, since there is some overlap:
     status => [ -or => {'=', 'assigned'}, {'=', 'in-progress'}]
     status => [ {'=', 'assigned'}, {'=', 'in-progress'} ]
 
-In addition to C<-and> and C<-or>, there is also a special C<-nest>
-operator which adds an additional set of parens, to create a subquery.
-For example, to get something like this:
-
-    $stmt = "WHERE user = ? AND ( workhrs > ? OR geo = ? )";
-    @bind = ('nwiger', '20', 'ASIA');
-
-You would do:
-
-    my %where = (
-         user => 'nwiger',
-        -nest => [ workhrs => {'>', 20}, geo => 'ASIA' ],
-    );
-
-If you need several nested subexpressions, you can number
-the C<-nest> branches :
-
-    my %where = (
-         user => 'nwiger',
-        -nest1 => ...,
-        -nest2 => ...,
-        ...
-    );
 
 
 =head2 Special operators : IN, BETWEEN, etc.
@@ -1793,7 +1777,7 @@ Would give you:
 These are the two builtin "special operators"; but the 
 list can be expanded : see section L</"SPECIAL OPERATORS"> below.
 
-=head2 Nested conditions
+=head2 Nested conditions, -and/-or prefixes
 
 So far, we've seen how multiple conditions are joined with a top-level
 C<AND>.  We can change this by putting the different conditions we want in
@@ -1816,15 +1800,32 @@ This data structure would create the following:
                 OR ( user = ? AND status = ? ) )";
     @bind = ('nwiger', 'pending', 'dispatched', 'robot', 'unassigned');
 
-This can be combined with the C<-nest> operator to properly group
-SQL statements:
+
+There is also a special C<-nest>
+operator which adds an additional set of parens, to create a subquery.
+For example, to get something like this:
+
+    $stmt = "WHERE user = ? AND ( workhrs > ? OR geo = ? )";
+    @bind = ('nwiger', '20', 'ASIA');
+
+You would do:
+
+    my %where = (
+         user => 'nwiger',
+        -nest => [ workhrs => {'>', 20}, geo => 'ASIA' ],
+    );
+
+
+Finally, clauses in hashrefs or arrayrefs can be
+prefixed with an C<-and> or C<-or> to change the logic
+inside :
 
     my @where = (
          -and => [
             user => 'nwiger',
             -nest => [
-                ["-and", workhrs => {'>', 20}, geo => 'ASIA' ],
-                ["-and", workhrs => {'<', 50}, geo => 'EURO' ]
+                -and => [workhrs => {'>', 20}, geo => 'ASIA' ],
+                -and => [workhrs => {'<', 50}, geo => 'EURO' ]
             ],
         ],
     );
@@ -1835,6 +1836,37 @@ That would yield:
           ( ( workhrs > ? AND geo = ? )
          OR ( workhrs < ? AND geo = ? ) ) )
 
+
+=head2 Algebraic inconsistency, for historical reasons
+
+C<Important note>: when connecting several conditions, the C<-and->|C<-or>
+operator goes C<outside> of the nested structure; whereas when connecting
+several constraints on one column, the C<-and> operator goes
+C<inside> the arrayref. Here is an example combining both features :
+
+   my @where = (
+     -and => [a => 1, b => 2],
+     -or  => [c => 3, d => 4],
+      e   => [-and => {-like => 'foo%'}, {-like => '%bar'} ]
+   )
+
+yielding
+
+  WHERE ( (    ( a = ? AND b = ? ) 
+            OR ( c = ? OR d = ? ) 
+            OR ( e LIKE ? AND e LIKE ? ) ) )
+
+This difference in syntax is unfortunate but must be preserved for
+historical reasons. So be careful : the two examples below would
+seem algebraically equivalent, but they are not
+
+  {col => [-and => {-like => 'foo%'}, {-like => '%bar'}]} 
+  # yields : WHERE ( ( col LIKE ? AND col LIKE ? ) )
+
+  [-and => {col => {-like => 'foo%'}, {col => {-like => '%bar'}}]] 
+  # yields : WHERE ( ( col LIKE ? OR col LIKE ? ) )
+
+
 =head2 Literal SQL
 
 Finally, sometimes only literal SQL will do. If you want to include
@@ -2178,7 +2210,8 @@ support for the { operator => \["...", @bind] } construct (to embed literal SQL
 
 =item *
 
-added -nest1, -nest2 or -nest_1, -nest_2, ...
+added official support for -nest1, -nest2 or -nest_1, -nest_2, ...
+(undocumented in previous versions)
 
 =item *
 
@@ -2191,21 +2224,12 @@ defensive programming : check arguments
 =item *
 
 fixed bug with global logic, which was previously implemented
-through global variables yielding side-effects. Prior versons would
+through global variables yielding side-effects. Prior versions would
 interpret C<< [ {cond1, cond2}, [cond3, cond4] ] >>
 as C<< "(cond1 AND cond2) OR (cond3 AND cond4)" >>.
 Now this is interpreted
 as C<< "(cond1 AND cond2) OR (cond3 OR cond4)" >>.
 
-=item *
-
-C<-and> / C<-or> operators are no longer accepted
-in the middle of an arrayref : they are
-only admitted if in first position.
-
-=item *
-
-changed logic for distributing an op over arrayrefs
 
 =item *