Institute a central "load this first in testing" package
[dbsrgits/DBIx-Class.git] / t / sqlmaker / dbihacks_internals.t
1 BEGIN { do "./t/lib/ANFANG.pm" or die ( $@ || $! ) }
2
3 use strict;
4 use warnings;
5 use Test::More;
6 use Test::Warn;
7 use Test::Exception;
8
9
10 use DBICTest ':DiffSQL';
11 use DBIx::Class::_Util 'UNRESOLVABLE_CONDITION';
12
13 use Data::Dumper;
14 BEGIN {
15   if ( eval { require Test::Differences } ) {
16     no warnings 'redefine';
17     *is_deeply = \&Test::Differences::eq_or_diff;
18   }
19 }
20
21 my $schema = DBICTest->init_schema( no_deploy => 1);
22 my $sm = $schema->storage->sql_maker;
23
24 {
25   package # hideee
26     DBICTest::SillyInt;
27
28   use overload
29     fallback => 1,
30     '0+' => sub { ${$_[0]} },
31   ;
32 }
33 my $num = bless( \do { my $foo = 69 }, 'DBICTest::SillyInt' );
34
35 is($num, 69, 'test overloaded object is "sane"');
36 is("$num", 69, 'test overloaded object is "sane"');
37
38 my @tests = (
39   {
40     where => { artistid => 1, charfield => undef },
41     cc_result => { artistid => 1, charfield => undef },
42     sql => 'WHERE artistid = ? AND charfield IS NULL',
43     efcc_result => { artistid => 1 },
44     efcc_n_result => { artistid => 1, charfield => undef },
45   },
46   {
47     where => { -and => [ artistid => 1, charfield => undef, { rank => 13 } ] },
48     cc_result => { artistid => 1, charfield => undef, rank => 13 },
49     sql => 'WHERE artistid = ?  AND charfield IS NULL AND rank = ?',
50     efcc_result => { artistid => 1, rank => 13 },
51     efcc_n_result => { artistid => 1, charfield => undef, rank => 13 },
52   },
53   {
54     where => { -and => [ { artistid => 1, charfield => undef}, { rank => 13 } ] },
55     cc_result => { artistid => 1, charfield => undef, rank => 13 },
56     sql => 'WHERE artistid = ?  AND charfield IS NULL AND rank = ?',
57     efcc_result => { artistid => 1, rank => 13 },
58     efcc_n_result => { artistid => 1, charfield => undef, rank => 13 },
59   },
60   {
61     where => { -and => [ -or => { name => 'Caterwauler McCrae' }, 'rank' ] },
62     cc_result => { name => 'Caterwauler McCrae', rank => undef },
63     sql => 'WHERE name = ? AND rank IS NULL',
64     efcc_result => { name => 'Caterwauler McCrae' },
65     efcc_n_result => { name => 'Caterwauler McCrae', rank => undef },
66   },
67   {
68     where => { -and => [ [ [ artist => {'=' => \'foo' } ] ], { name => \[ '= ?', 'bar' ] } ] },
69     cc_result => { artist => {'=' => \'foo' }, name => \[ '= ?', 'bar' ] },
70     sql => 'WHERE artist = foo AND name = ?',
71     efcc_result => { artist => \'foo' },
72   },
73   {
74     where => { -and => [ -or => { name => 'Caterwauler McCrae', artistid => 2 } ] },
75     cc_result => { -or => [ artistid => 2, name => 'Caterwauler McCrae' ] },
76     sql => 'WHERE artistid = ? OR name = ?',
77     efcc_result => {},
78   },
79   {
80     where => { -or => { name => 'Caterwauler McCrae', artistid => 2 } },
81     cc_result => { -or => [ artistid => 2, name => 'Caterwauler McCrae' ] },
82     sql => 'WHERE artistid = ? OR name = ?',
83     efcc_result => {},
84   },
85   {
86     where => { -and => [ \'foo=bar',  [ { artistid => { '=', $num } } ], { name => 'Caterwauler McCrae'} ] },
87     cc_result => { -and => [ \'foo=bar' ], name => 'Caterwauler McCrae', artistid => $num },
88     sql => 'WHERE foo=bar AND artistid = ? AND name = ?',
89     efcc_result => { name => 'Caterwauler McCrae', artistid => $num },
90   },
91   {
92     where => { -and => [ \'foo=bar',  [ { artistid => { '=', $num } } ], { name => 'Caterwauler McCrae'}, \'buzz=bozz' ] },
93     cc_result => { -and => [ \'foo=bar', \'buzz=bozz' ], name => 'Caterwauler McCrae', artistid => $num },
94     sql => 'WHERE foo=bar AND artistid = ? AND name = ? AND buzz=bozz',
95     collapsed_sql => 'WHERE foo=bar AND buzz=bozz AND artistid = ? AND name = ?',
96     efcc_result => { name => 'Caterwauler McCrae', artistid => $num },
97   },
98   {
99     where => { artistid => [ $num ], rank => [ 13, 2, 3 ], charfield => [ undef ] },
100     cc_result => { artistid => $num, charfield => undef, rank => [13, 2, 3] },
101     sql => 'WHERE artistid = ? AND charfield IS NULL AND ( rank = ? OR rank = ? OR rank = ? )',
102     efcc_result => { artistid => $num },
103     efcc_n_result => { artistid => $num, charfield => undef },
104   },
105   {
106     where => { artistid => { '=' => 1 }, rank => { '>' => 12 }, charfield => { '=' => undef } },
107     cc_result => { artistid => 1, charfield => undef, rank => { '>' => 12 } },
108     sql => 'WHERE artistid = ? AND charfield IS NULL AND rank > ?',
109     efcc_result => { artistid => 1 },
110     efcc_n_result => { artistid => 1, charfield => undef },
111   },
112   {
113     where => { artistid => { '=' => [ 1 ], }, charfield => { '=' => [ -AND => \'1', \['?',2] ] }, rank => { '=' => [ -OR => $num, $num ] } },
114     cc_result => { artistid => 1, charfield => [-and => { '=' => \['?',2] }, { '=' => \'1' } ], rank => { '=' => [$num, $num] } },
115     sql => 'WHERE artistid = ? AND charfield = 1 AND charfield = ? AND ( rank = ? OR rank = ? )',
116     collapsed_sql => 'WHERE artistid = ? AND charfield = ? AND charfield = 1 AND ( rank = ? OR rank = ? )',
117     efcc_result => { artistid => 1, charfield => UNRESOLVABLE_CONDITION },
118   },
119   {
120     where => { -and => [ artistid => 1, artistid => 2 ], name => [ -and => { '!=', 1 }, 2 ], charfield => [ -or => { '=', 2 } ], rank => [-and => undef, { '=', undef }, { '!=', 2 } ] },
121     cc_result => { artistid => [ -and => 1, 2 ], name => [ -and => { '!=', 1 }, 2 ], charfield => 2, rank => [ -and => { '!=', 2 }, undef ] },
122     sql => 'WHERE artistid = ? AND artistid = ? AND charfield = ? AND name != ? AND name = ? AND rank IS NULL AND rank IS NULL AND rank != ?',
123     collapsed_sql => 'WHERE artistid = ? AND artistid = ? AND charfield = ? AND name != ? AND name = ? AND rank != ? AND rank IS NULL',
124     efcc_result => {
125       artistid => UNRESOLVABLE_CONDITION,
126       name => 2,
127       charfield => 2,
128     },
129     efcc_n_result => {
130       artistid => UNRESOLVABLE_CONDITION,
131       name => 2,
132       charfield => 2,
133       rank => undef,
134     },
135   },
136   (map { {
137     where => $_,
138     sql => 'WHERE (rank = 13 OR charfield IS NULL OR artistid = ?) AND (artistid = ? OR charfield IS NULL OR rank != 42)',
139     collapsed_sql => 'WHERE (artistid = ? OR charfield IS NULL OR rank = 13) AND (artistid = ? OR charfield IS NULL OR rank != 42)',
140     cc_result => { -and => [
141       { -or => [ artistid => 1, charfield => undef, rank => { '=' => \13 } ] },
142       { -or => [ artistid => 1, charfield => undef, rank => { '!=' => \42 } ] },
143     ] },
144     efcc_result => {},
145     efcc_n_result => {},
146   } } (
147
148     { -and => [
149       -or => [ rank => { '=' => \13 }, charfield => { '=' => undef }, artistid => 1 ],
150       -or => { artistid => { '=' => 1 }, charfield => undef, rank => { '!=' => \42 } },
151     ] },
152
153     {
154       -OR => [ rank => { '=' => \13 }, charfield => { '=' => undef }, artistid => 1 ],
155       -or => { artistid => { '=' => 1 }, charfield => undef, rank => { '!=' => \42 } },
156     },
157
158   ) ),
159   {
160     where => { -or => [
161       -and => [ foo => { '!=', { -value => undef } }, bar => { -in => [ 69, 42 ] } ],
162       foo => { '=', { -value => undef } },
163       baz => { '!=' => { -ident => 'bozz' } },
164       baz => { -ident => 'buzz' },
165     ] },
166     sql => 'WHERE ( foo IS NOT NULL AND bar IN ( ?, ? ) ) OR foo IS NULL OR baz != bozz OR baz = buzz',
167     collapsed_sql => 'WHERE baz != bozz OR baz = buzz OR foo IS NULL OR ( bar IN ( ?, ? ) AND foo IS NOT NULL )',
168     cc_result => { -or => [
169       baz => { '!=' => { -ident => 'bozz' } },
170       baz => { '=' => { -ident => 'buzz' } },
171       foo => undef,
172       { bar => { -in => [ 69, 42 ] }, foo => { '!=', undef } }
173     ] },
174     efcc_result => {},
175   },
176   {
177     where => { -or => [ rank => { '=' => \13 }, charfield => { '=' => undef }, artistid => { '=' => 1 }, genreid => { '=' => \['?', 2] } ] },
178     sql => 'WHERE rank = 13 OR charfield IS NULL OR artistid = ? OR genreid = ?',
179     collapsed_sql => 'WHERE artistid = ? OR charfield IS NULL OR genreid = ? OR rank = 13',
180     cc_result => { -or => [ artistid => 1, charfield => undef, genreid => { '=' => \['?', 2] }, rank => { '=' => \13 } ] },
181     efcc_result => {},
182     efcc_n_result => {},
183   },
184   {
185     where => { -and => [
186       -or => [ rank => { '=' => \13 }, charfield => { '=' => undef }, artistid => 1 ],
187       -or => { artistid => { '=' => 1 }, charfield => undef, rank => { '=' => \13 } },
188     ] },
189     cc_result => { -and => [
190       { -or => [ artistid => 1, charfield => undef, rank => { '=' => \13 } ] },
191       { -or => [ artistid => 1, charfield => undef, rank => { '=' => \13 } ] },
192     ] },
193     sql => 'WHERE (rank = 13 OR charfield IS NULL OR artistid = ?) AND (artistid = ? OR charfield IS NULL OR rank = 13)',
194     collapsed_sql => 'WHERE (artistid = ? OR charfield IS NULL OR rank = 13) AND (artistid = ? OR charfield IS NULL OR rank = 13)',
195     efcc_result => {},
196     efcc_n_result => {},
197   },
198   {
199     where => { -and => [
200       -or => [ rank => { '=' => \13 }, charfield => { '=' => undef }, artistid => 1 ],
201       -or => { artistid => { '=' => 1 }, charfield => undef, rank => { '!=' => \42 } },
202       -and => [ foo => { '=' => \1 }, bar => 2 ],
203       -and => [ foo => 3, bar => { '=' => \4 } ],
204       -exists => \'(SELECT 1)',
205       -exists => \'(SELECT 2)',
206       -not => { foo => 69 },
207       -not => { foo => 42 },
208     ]},
209     sql => 'WHERE
210           ( rank = 13 OR charfield IS NULL OR artistid = ? )
211       AND ( artistid = ? OR charfield IS NULL OR rank != 42 )
212       AND foo = 1
213       AND bar = ?
214       AND foo = ?
215       AND bar = 4
216       AND (EXISTS (SELECT 1))
217       AND (EXISTS (SELECT 2))
218       AND NOT foo = ?
219       AND NOT foo = ?
220     ',
221     collapsed_sql => 'WHERE
222           ( artistid = ? OR charfield IS NULL OR rank = 13 )
223       AND ( artistid = ? OR charfield IS NULL OR rank != 42 )
224       AND (EXISTS (SELECT 1))
225       AND (EXISTS (SELECT 2))
226       AND NOT foo = ?
227       AND NOT foo = ?
228       AND bar = 4
229       AND bar = ?
230       AND foo = 1
231       AND foo = ?
232     ',
233     cc_result => {
234       -and => [
235         { -or => [ artistid => 1, charfield => undef, rank => { '=' => \13 } ] },
236         { -or => [ artistid => 1, charfield => undef, rank => { '!=' => \42 } ] },
237         { -exists => \'(SELECT 1)' },
238         { -exists => \'(SELECT 2)' },
239         { -not => { foo => 69 } },
240         { -not => { foo => 42 } },
241       ],
242       foo => [ -and => { '=' => \1 }, 3 ],
243       bar => [ -and => { '=' => \4 }, 2 ],
244     },
245     efcc_result => {
246       foo => UNRESOLVABLE_CONDITION,
247       bar => UNRESOLVABLE_CONDITION,
248     },
249     efcc_n_result => {
250       foo => UNRESOLVABLE_CONDITION,
251       bar => UNRESOLVABLE_CONDITION,
252     },
253   },
254   {
255     where => { -and => [
256       [ '_macro.to' => { -like => '%correct%' }, '_wc_macros.to' => { -like => '%correct%' } ],
257       { -and => [ { 'group.is_active' => 1 }, { 'me.is_active' => 1 } ] }
258     ] },
259     cc_result => {
260       'group.is_active' => 1,
261       'me.is_active' => 1,
262       -or => [
263         '_macro.to' => { -like => '%correct%' },
264         '_wc_macros.to' => { -like => '%correct%' },
265       ],
266     },
267     sql => 'WHERE ( _macro.to LIKE ? OR _wc_macros.to LIKE ? ) AND group.is_active = ? AND me.is_active = ?',
268     efcc_result => { 'group.is_active' => 1, 'me.is_active' => 1 },
269   },
270
271   {
272     where => { -and => [
273       artistid => { -value => [1] },
274       charfield => { -ident => 'foo' },
275       name => { '=' => { -value => undef } },
276       rank => { '=' => { -ident => 'bar' } },
277     ] },
278     sql => 'WHERE artistid = ? AND charfield = foo AND name IS NULL AND rank = bar',
279     cc_result => {
280       artistid => { -value => [1] },
281       name => undef,
282       charfield => { '=', { -ident => 'foo' } },
283       rank => { '=' => { -ident => 'bar' } },
284     },
285     efcc_result => {
286       artistid => [1],
287       charfield => { -ident => 'foo' },
288       rank => { -ident => 'bar' },
289     },
290     efcc_n_result => {
291       artistid => [1],
292       name => undef,
293       charfield => { -ident => 'foo' },
294       rank => { -ident => 'bar' },
295     },
296   },
297
298   {
299     where => { artistid => [] },
300     cc_result => { artistid => [] },
301     efcc_result => {},
302   },
303   (map {
304     {
305       where => { -and => $_ },
306       cc_result => undef,
307       efcc_result => {},
308       sql => '',
309     },
310     {
311       where => { -or => $_ },
312       cc_result => undef,
313       efcc_result => {},
314       sql => '',
315     },
316     {
317       where => { -or => [ foo => 1, $_ ] },
318       cc_result => { foo => 1 },
319       efcc_result => { foo => 1 },
320       sql => 'WHERE foo = ?',
321     },
322     {
323       where => { -or => [ $_, foo => 1 ] },
324       cc_result => { foo => 1 },
325       efcc_result => { foo => 1 },
326       sql => 'WHERE foo = ?',
327     },
328     {
329       where => { -and => [ fuu => 2, $_, foo => 1 ] },
330       sql => 'WHERE fuu = ? AND foo = ?',
331       collapsed_sql => 'WHERE foo = ? AND fuu = ?',
332       cc_result => { foo => 1, fuu => 2 },
333       efcc_result => { foo => 1, fuu => 2 },
334     },
335   } (
336     # bare
337     [], {},
338     # singles
339     [ {} ], [ [] ],
340     # doubles
341     [ [], [] ], [ {}, {} ], [ [], {} ], [ {}, [] ],
342     # tripples
343     [ {}, [], {} ], [ [], {}, [] ]
344   )),
345
346   # FIXME legacy compat crap, possibly worth undef/dieing in SQLMaker
347   { where => { artistid => {} }, sql => '', cc_result => undef, efcc_result => {}, efcc_n_result => {} },
348
349   # batshit insanity, just to be thorough
350   {
351     where => { -and => [ [ 'artistid' ], [ -and => [ artistid => { '!=', 69 }, artistid => undef, artistid => { '=' => 200 } ]], artistid => [], { -or => [] }, { -and => [] }, [ 'charfield' ], { name => [] }, 'rank' ] },
352     cc_result => { artistid => [ -and => [], { '!=', 69 }, undef, 200  ], charfield => undef, name => [], rank => undef },
353     sql => 'WHERE artistid IS NULL AND artistid != ? AND artistid IS NULL AND artistid = ? AND 0=1 AND charfield IS NULL AND 0=1 AND rank IS NULL',
354     collapsed_sql => 'WHERE 0=1 AND artistid != ? AND artistid IS NULL AND artistid = ? AND charfield IS NULL AND 0=1 AND rank IS NULL',
355     efcc_result => { artistid => UNRESOLVABLE_CONDITION },
356     efcc_n_result => { artistid => UNRESOLVABLE_CONDITION, charfield => undef, rank => undef },
357   },
358
359   # original test from RT#93244
360   {
361     where => {
362       -and => [
363         \[
364           "LOWER(me.title) LIKE ?",
365           '%spoon%',
366         ],
367         [ { 'me.title' => 'Spoonful of bees' } ],
368     ]},
369     cc_result => {
370       -and => [ \[
371         "LOWER(me.title) LIKE ?",
372         '%spoon%',
373       ]],
374       'me.title' => 'Spoonful of bees',
375     },
376     sql => 'WHERE LOWER(me.title) LIKE ? AND me.title = ?',
377     efcc_result => { 'me.title' => 'Spoonful of bees' },
378   },
379
380   # crazy literals
381   {
382     where => {
383       -or => [
384         \'foo = bar',
385       ],
386     },
387     sql => 'WHERE foo = bar',
388     cc_result => {
389       -and => [
390         \'foo = bar',
391       ],
392     },
393     efcc_result => {},
394   },
395   {
396     where => {
397       -or => [
398         \'foo = bar',
399         \'baz = ber',
400       ],
401     },
402     sql => 'WHERE foo = bar OR baz = ber',
403     collapsed_sql => 'WHERE baz = ber OR foo = bar',
404     cc_result => {
405       -or => [
406         \'baz = ber',
407         \'foo = bar',
408       ],
409     },
410     efcc_result => {},
411   },
412   {
413     where => {
414       -and => [
415         \'foo = bar',
416         \'baz = ber',
417       ],
418     },
419     sql => 'WHERE foo = bar AND baz = ber',
420     cc_result => {
421       -and => [
422         \'foo = bar',
423         \'baz = ber',
424       ],
425     },
426     efcc_result => {},
427   },
428   {
429     where => {
430       -and => [
431         \'foo = bar',
432         \'baz = ber',
433         x => { -ident => 'y' },
434       ],
435     },
436     sql => 'WHERE foo = bar AND baz = ber AND x = y',
437     cc_result => {
438       -and => [
439         \'foo = bar',
440         \'baz = ber',
441       ],
442       x => { '=' => { -ident => 'y' } }
443     },
444     efcc_result => { x => { -ident => 'y' } },
445   },
446 );
447
448 # these die as of SQLA 1.80 - make sure we do not transform them
449 # into something usable instead
450 for my $lhs (undef, '', { -ident => 'foo' }, { -value => 'foo' } ) {
451   no warnings 'uninitialized';
452
453   for my $w (
454     ( map { { -or => $_ }, (ref $lhs ? () : { @$_ } ) }
455       [ $lhs => "foo" ],
456       [ $lhs => { "=" => "bozz" } ],
457       [ $lhs => { "=" => \"bozz" } ],
458       [ $lhs => { -max => \"bizz" } ],
459     ),
460
461     (ref $lhs) ? () : (
462       { -or => [ -and => { $lhs => "baz" }, bizz => "buzz" ] },
463       { -or => [ foo => "bar", { $lhs => "baz" }, bizz => "buzz" ] },
464       { foo => "bar", -or => { $lhs => "baz" } },
465       { foo => "bar", -or => { $lhs => \"baz" }, bizz => "buzz" },
466     ),
467
468     { foo => "bar", -and => [ $lhs => \"baz" ], bizz => "buzz" },
469     { foo => "bar", -or => [ $lhs => \"baz" ], bizz => "buzz" },
470
471     { -or => [ foo => "bar", [ $lhs => \"baz" ], bizz => "buzz" ] },
472     { -or => [ foo => "bar", $lhs => \"baz", bizz => "buzz" ] },
473     { -or => [ foo => "bar", $lhs => \["baz"], bizz => "buzz" ] },
474     { -or => [ $lhs => \"baz" ] },
475     { -or => [ $lhs => \["baz"] ] },
476
477   ) {
478     push @tests, {
479       where => $w,
480       throw => qr/
481         \QSupplying an empty left hand side argument is not supported in \E(?:array|hash)-pairs
482           |
483         \QIllegal use of top-level '-\E(?:value|ident)'
484       /x,
485     }
486   }
487 }
488
489 # these are deprecated as of SQLA 1.79 - make sure we do not transform
490 # them without losing the warning
491 for my $lhs (undef, '') {
492   for my $rhs ( \"baz", \[ "baz" ] ) {
493     no warnings 'uninitialized';
494
495     my $expected_warning = qr/\QHash-pairs consisting of an empty string with a literal are deprecated/;
496
497     push @tests, {
498       where => { $lhs => $rhs },
499       cc_result => { -and => [ $rhs ] },
500       efcc_result => {},
501       sql => 'WHERE baz',
502       warn => $expected_warning,
503     };
504
505     for my $w (
506       { foo => "bar", -and => { $lhs => $rhs }, bizz => "buzz" },
507       { foo => "bar", $lhs => $rhs, bizz => "buzz" },
508     ) {
509       push @tests, {
510         where => $w,
511         cc_result => {
512           -and => [ $rhs ],
513           bizz => "buzz",
514           foo => "bar",
515         },
516         efcc_result => {
517           foo => "bar",
518           bizz => "buzz",
519         },
520         sql => 'WHERE baz AND bizz = ? AND foo = ?',
521         warn => $expected_warning,
522       };
523     }
524   }
525 }
526
527 # lots of extra silly tests with a false column
528 for my $eq (
529   \"= baz",
530   \[ "= baz" ],
531   { '=' => { -ident => 'baz' } },
532   { '=' => \'baz' },
533 ) {
534   for my $where (
535     { foo => "bar", -and => [ 0 => $eq ], bizz => "buzz" },
536     { foo => "bar", -or => [ 0 => $eq ], bizz => "buzz" },
537     { foo => "bar", -and => { 0 => $eq }, bizz => "buzz" },
538     { foo => "bar", -or => { 0 => $eq }, bizz => "buzz" },
539     { foo => "bar", 0 => $eq, bizz => "buzz" },
540   ) {
541     push @tests, {
542       where => $where,
543       cc_result => {
544         0 => $eq,
545         foo => 'bar',
546         bizz => 'buzz',
547       },
548       efcc_result => {
549         foo => 'bar',
550         bizz => 'buzz',
551         ( ref $eq eq 'HASH' ? ( 0 => $eq->{'='} ) : () ),
552       },
553       sql => 'WHERE 0 = baz AND bizz = ? AND foo = ?',
554     };
555
556     push @tests, {
557       where => { -or => $where },
558       cc_result => { -or => [
559         "0" => $eq,
560         bizz => 'buzz',
561         foo => 'bar',
562       ]},
563       efcc_result => {},
564       sql => 'WHERE 0 = baz OR bizz = ? OR foo = ?',
565     }
566
567   }
568
569   for my $where (
570     [ foo => "bar", -and => [ 0 => $eq ], bizz => "buzz" ],
571     [ foo => "bar", -or => [ 0 => $eq ], bizz => "buzz" ],
572     [ foo => "bar", -and => { 0 => $eq }, bizz => "buzz" ],
573     [ foo => "bar", -or => { 0 => $eq }, bizz => "buzz" ],
574     [ foo => "bar", 0 => $eq, bizz => "buzz" ],
575   ) {
576     push @tests, {
577       where => { -or => $where },
578       cc_result => { -or => [
579         "0" => $eq,
580         bizz => 'buzz',
581         foo => 'bar',
582       ]},
583       efcc_result => {},
584       sql => 'WHERE foo = ? OR 0 = baz OR bizz = ?',
585       collapsed_sql => 'WHERE 0 = baz OR bizz = ? OR foo = ?',
586     }
587   }
588
589   for my $where (
590     [ {foo => "bar"}, -and => { 0 => "baz" }, bizz => "buzz" ],
591     [ -or => [ foo => "bar", -or => { 0 => "baz" }, bizz => "buzz" ] ],
592   ) {
593     push @tests, {
594       where => { -or => $where },
595       cc_result => { -or => [
596         "0" => 'baz',
597         bizz => 'buzz',
598         foo => 'bar',
599       ]},
600       efcc_result => {},
601       sql => 'WHERE foo = ? OR 0 = ? OR bizz = ?',
602       collapsed_sql => 'WHERE 0 = ? OR bizz = ? OR foo = ?',
603     };
604   }
605
606 };
607
608 for my $t (@tests) {
609   for my $w (
610     $t->{where},
611     $t->{where},  # do it twice, make sure we didn't destory the condition
612     [ -and => $t->{where} ],
613     [ -AND => $t->{where} ],
614     { -OR => [ -AND => $t->{where} ] },
615     ( ( keys %{$t->{where}} == 1 and length( (keys %{$t->{where}})[0] ) )
616       ? [ %{$t->{where}} ]
617       : ()
618     ),
619     ( (keys %{$t->{where}} == 1 and $t->{where}{-or})
620       ? ( ref $t->{where}{-or} eq 'HASH'
621         ? [ map { $_ => $t->{where}{-or}{$_} } sort keys %{$t->{where}{-or}} ]
622         : $t->{where}{-or}
623       )
624       : ()
625     ),
626   ) {
627     die unless Test::Builder->new->is_passing;
628
629     my $name = do { local ($Data::Dumper::Indent, $Data::Dumper::Terse, $Data::Dumper::Sortkeys) = (0, 1, 1); Dumper $w };
630
631     my ($collapsed_cond, $collapsed_cond_as_sql);
632
633     if ($t->{throw}) {
634       throws_ok {
635         $collapsed_cond = $schema->storage->_collapse_cond($w);
636         ($collapsed_cond_as_sql) = $sm->where($collapsed_cond);
637       } $t->{throw}, "Exception on attempted collapse/render of $name"
638         and
639       next;
640     }
641
642     warnings_exist {
643       $collapsed_cond = $schema->storage->_collapse_cond($w);
644       ($collapsed_cond_as_sql) = $sm->where($collapsed_cond);
645     } $t->{warn} || [], "Expected warning when collapsing/rendering $name";
646
647     is_deeply(
648       $collapsed_cond,
649       $t->{cc_result},
650       "Expected collapsed condition produced on $name",
651     );
652
653     my ($original_sql) = do {
654       local $SIG{__WARN__} = sub {};
655       $sm->where($w);
656     };
657
658     is_same_sql ( $original_sql, $t->{sql}, "Expected original SQL from $name" )
659       if exists $t->{sql};
660
661     is_same_sql(
662       $collapsed_cond_as_sql,
663       ( $t->{collapsed_sql} || $t->{sql} || $original_sql ),
664       "Collapse did not alter *the semantics* of the final SQL based on $name",
665     );
666
667     is_deeply(
668       $schema->storage->_extract_fixed_condition_columns($collapsed_cond),
669       $t->{efcc_result},
670       "Expected fixed_condition produced on $name",
671     );
672
673     is_deeply(
674       $schema->storage->_extract_fixed_condition_columns($collapsed_cond, 'consider_nulls'),
675       $t->{efcc_n_result},
676       "Expected fixed_condition including NULLs produced on $name",
677     ) if $t->{efcc_n_result};
678
679     is_deeply(
680       $collapsed_cond,
681       $t->{cc_result},
682       "Collapsed condition result unaltered by fixed condition extractor",
683     );
684   }
685 }
686
687 done_testing;