Make sure empty cond collapser works on all positions
Peter Rabbitson [Tue, 2 Sep 2014 02:20:26 +0000 (04:20 +0200)]
Adds another round of sql stabilization (akin to 5268b1da6)

lib/DBIx/Class/Storage/DBIHacks.pm
t/sqlmaker/dbihacks_internals.t

index 90bade8..e172299 100644 (file)
@@ -1076,36 +1076,41 @@ sub _collapse_cond {
     return $fin;
   }
   elsif (ref $where eq 'ARRAY') {
-    my @w = @$where;
 
-    while ( @w and (
-      (ref $w[0] eq 'ARRAY' and ! @{$w[0]} )
-        or
-      (ref $w[0] eq 'HASH' and ! keys %{$w[0]})
-    )) { shift @w };
+    # we are always at top-level here, it is safe to dump empty *standalone* pieces
+    my $fin_idx;
 
-    return unless @w;
+    for (my $i = 0; $i <= $#$where; $i++ ) {
 
-    if ( @w == 1 ) {
-      return ( length ref $w[0] )
-        ? $self->_collapse_cond($w[0])
-        : { $w[0] => undef }
-      ;
-    }
-    elsif ( @w == 2 and ! length ref $w[0]) {
-      if ( ( $w[0]||'' ) =~ /^\-and$/i ) {
-        return (ref $w[1] eq 'HASH' or ref $w[1] eq 'ARRAY')
-          ? $self->_collapse_cond($w[1], (ref $w[1] eq 'ARRAY') )
-          : $self->throw_exception("Unsupported top-level op/arg pair: [ $w[0] => $w[1] ]")
-        ;
+      my $logic_mod = lc ( ($where->[$i] =~ /^(\-(?:and|or))$/i)[0] || '' );
+
+      if ($logic_mod) {
+        $i++;
+        $self->throw_exception("Unsupported top-level op/arg pair: [ $logic_mod => $where->[$i] ]")
+          unless ref $where->[$i] eq 'HASH' or ref $where->[$i] eq 'ARRAY';
+
+        my $sub_elt = $self->_collapse_cond({ $logic_mod => $where->[$i] })
+          or next;
+
+        $fin_idx->{ serialize $sub_elt } = $sub_elt;
+      }
+      elsif (! length ref $where->[$i] ) {
+        $fin_idx->{"$where->[$i]_$i"} = $self->_collapse_cond({ @{$where}[$i, $i+1] }) || next;
+        $i++;
       }
       else {
-        return $self->_collapse_cond({ @w });
+        $fin_idx->{ serialize $where->[$i] } = $self->_collapse_cond( $where->[$i] ) || next;
       }
     }
-    else {
-      return { -or => \@w };
-    }
+
+    return unless $fin_idx;
+
+    return ( keys %$fin_idx == 1 ) ? (values %$fin_idx)[0] : {
+      -or => [ map
+        { ref $fin_idx->{$_} eq 'HASH' ? %{$fin_idx->{$_}} : $fin_idx->{$_} }
+        sort keys %$fin_idx
+      ]
+    };
   }
   else {
     # not a hash not an array
index 1214638..1ad550a 100644 (file)
@@ -179,6 +179,25 @@ for my $t (
       efcc_result => {},
       sql => '',
     },
+    {
+      where => { -or => [ foo => 1, $_ ] },
+      cc_result => { foo => 1 },
+      efcc_result => { foo => 1 },
+      sql => 'WHERE foo = ?',
+    },
+    {
+      where => { -or => [ $_, foo => 1 ] },
+      cc_result => { foo => 1 },
+      efcc_result => { foo => 1 },
+      sql => 'WHERE foo = ?',
+    },
+    {
+      where => { -and => [ fuu => 2, $_, foo => 1 ] },
+      sql => 'WHERE fuu = ? AND foo = ?',
+      collapsed_sql => 'WHERE foo = ? AND fuu = ?',
+      cc_result => { foo => 1, fuu => 2 },
+      efcc_result => { foo => 1, fuu => 2 },
+    },
   } (
     # bare
     [], {},