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