single fire -nest warning because DBIC expects it, fix an order_by bug
[scpubgit/Q-Branch.git] / t / 01generate.t
1 use strict;
2 use warnings;
3 use Test::More;
4 use Test::Warn;
5 use Test::Exception;
6
7 use SQL::Abstract::Test import => [qw(is_same_sql_bind diag_where dumper)];
8
9 use SQL::Abstract;
10
11 #### WARNING ####
12 #
13 # -nest has been undocumented on purpose, but is still supported for the
14 # foreseable future. Do not rip out the -nest tests before speaking to
15 # someone on the DBIC mailing list or in irc.perl.org#dbix-class
16 #
17 #################
18
19 # DBIx::Class requires a nest warning to be emitted once but the private
20 # method it overrode to do so no longer exists; here we cancel said warning
21 # to avoid disturbing the SQLA tests
22
23 $SQL::Abstract::Nest_Warning_Emitted++;
24
25 my @tests = (
26       {
27               func   => 'select',
28               args   => ['test', '*'],
29               stmt   => 'SELECT * FROM test',
30               stmt_q => 'SELECT * FROM `test`',
31               bind   => []
32       },
33       {
34               func   => 'select',
35               args   => ['test', [qw(one two three)]],
36               stmt   => 'SELECT one, two, three FROM test',
37               stmt_q => 'SELECT `one`, `two`, `three` FROM `test`',
38               bind   => []
39       },
40       {
41               func   => 'select',
42               args   => ['test', '*', { a => 0 }, [qw/boom bada bing/]],
43               stmt   => 'SELECT * FROM test WHERE ( a = ? ) ORDER BY boom, bada, bing',
44               stmt_q => 'SELECT * FROM `test` WHERE ( `a` = ? ) ORDER BY `boom`, `bada`, `bing`',
45               bind   => [0]
46       },
47       {
48               func   => 'select',
49               args   => ['test', '*', [ { a => 5 }, { b => 6 } ]],
50               stmt   => 'SELECT * FROM test WHERE ( ( a = ? ) OR ( b = ? ) )',
51               stmt_q => 'SELECT * FROM `test` WHERE ( ( `a` = ? ) OR ( `b` = ? ) )',
52               bind   => [5,6]
53       },
54       {
55               func   => 'select',
56               args   => ['test', '*', undef, ['id']],
57               stmt   => 'SELECT * FROM test ORDER BY id',
58               stmt_q => 'SELECT * FROM `test` ORDER BY `id`',
59               bind   => []
60       },
61       {
62               func   => 'select',
63               args   => ['test', '*', { a => 'boom' } , ['id']],
64               stmt   => 'SELECT * FROM test WHERE ( a = ? ) ORDER BY id',
65               stmt_q => 'SELECT * FROM `test` WHERE ( `a` = ? ) ORDER BY `id`',
66               bind   => ['boom']
67       },
68       {
69               func   => 'select',
70               args   => ['test', '*', { a => ['boom', 'bang'] }],
71               stmt   => 'SELECT * FROM test WHERE ( ( ( a = ? ) OR ( a = ? ) ) )',
72               stmt_q => 'SELECT * FROM `test` WHERE ( ( ( `a` = ? ) OR ( `a` = ? ) ) )',
73               bind   => ['boom', 'bang']
74       },
75       {
76               func   => 'select',
77               args   => ['test', '*', { a => { '!=', 'boom' } }],
78               stmt   => 'SELECT * FROM test WHERE ( a != ? )',
79               stmt_q => 'SELECT * FROM `test` WHERE ( `a` != ? )',
80               bind   => ['boom']
81       },
82       {
83               # this is maybe wrong but a single arg doesn't get quoted
84               func   => 'select',
85               args   => ['test', 'id', { a => { '!=', 'boom' } }],
86               stmt   => 'SELECT id FROM test WHERE ( a != ? )',
87               stmt_q => 'SELECT id FROM `test` WHERE ( `a` != ? )',
88               bind   => ['boom']
89       },
90       {
91               func   => 'update',
92               args   => ['test', {a => 'boom'}, {a => undef}],
93               stmt   => 'UPDATE test SET a = ? WHERE ( a IS NULL )',
94               stmt_q => 'UPDATE `test` SET `a` = ? WHERE ( `a` IS NULL )',
95               bind   => ['boom']
96       },
97       {
98               func   => 'update',
99               args   => ['test', {a => undef }, {a => 'boom'}],
100               stmt   => 'UPDATE test SET a = ? WHERE ( a = ? )',
101               stmt_q => 'UPDATE `test` SET `a` = ? WHERE ( `a` = ? )',
102               bind   => [undef,'boom']
103       },
104       {
105               func   => 'update',
106               args   => ['test', {a => 'boom'}, { a => {'!=', "bang" }} ],
107               stmt   => 'UPDATE test SET a = ? WHERE ( a != ? )',
108               stmt_q => 'UPDATE `test` SET `a` = ? WHERE ( `a` != ? )',
109               bind   => ['boom', 'bang']
110       },
111       {
112               func   => 'update',
113               args   => ['test', {'a-funny-flavored-candy' => 'yummy', b => 'oops'}, { a42 => "bang" }],
114               stmt   => 'UPDATE test SET a-funny-flavored-candy = ?, b = ? WHERE ( a42 = ? )',
115               stmt_q => 'UPDATE `test` SET `a-funny-flavored-candy` = ?, `b` = ? WHERE ( `a42` = ? )',
116               bind   => ['yummy', 'oops', 'bang']
117       },
118       {
119               func   => 'delete',
120               args   => ['test', {requestor => undef}],
121               stmt   => 'DELETE FROM test WHERE ( requestor IS NULL )',
122               stmt_q => 'DELETE FROM `test` WHERE ( `requestor` IS NULL )',
123               bind   => []
124       },
125       {
126               func   => 'delete',
127               args   => [[qw/test1 test2 test3/],
128                          { 'test1.field' => \'!= test2.field',
129                             user => {'!=','nwiger'} },
130                         ],
131               stmt   => 'DELETE FROM test1, test2, test3 WHERE ( test1.field != test2.field AND user != ? )',
132               stmt_q => 'DELETE FROM `test1`, `test2`, `test3` WHERE ( `test1`.`field` != test2.field AND `user` != ? )',  # test2.field is a literal value, cannnot be quoted.
133               bind   => ['nwiger']
134       },
135       {
136               func   => 'select',
137               args   => [[\'test1', 'test2'], '*', { 'test1.a' => 'boom' } ],
138               stmt   => 'SELECT * FROM test1, test2 WHERE ( test1.a = ? )',
139               stmt_q => 'SELECT * FROM test1, `test2` WHERE ( `test1`.`a` = ? )',
140               bind   => ['boom']
141       },
142       {
143               func   => 'insert',
144               args   => ['test', {a => 1, b => 2, c => 3, d => 4, e => 5}],
145               stmt   => 'INSERT INTO test (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)',
146               stmt_q => 'INSERT INTO `test` (`a`, `b`, `c`, `d`, `e`) VALUES (?, ?, ?, ?, ?)',
147               bind   => [qw/1 2 3 4 5/],
148       },
149       {
150               func   => 'insert',
151               args   => ['test', [1..30]],
152               stmt   => 'INSERT INTO test VALUES ('.join(', ', ('?')x30).')',
153               stmt_q => 'INSERT INTO `test` VALUES ('.join(', ', ('?')x30).')',
154               bind   => [1..30],
155       },
156       {
157               func   => 'insert',
158               args   => ['test', [qw/1 2 3 4 5/, undef]],
159               stmt   => 'INSERT INTO test VALUES (?, ?, ?, ?, ?, ?)',
160               stmt_q => 'INSERT INTO `test` VALUES (?, ?, ?, ?, ?, ?)',
161               bind   => [qw/1 2 3 4 5/, undef],
162       },
163       {
164               func   => 'update',
165               args   => ['test', {a => 1, b => 2, c => 3, d => 4, e => 5}],
166               stmt   => 'UPDATE test SET a = ?, b = ?, c = ?, d = ?, e = ?',
167               stmt_q => 'UPDATE `test` SET `a` = ?, `b` = ?, `c` = ?, `d` = ?, `e` = ?',
168               bind   => [qw/1 2 3 4 5/],
169       },
170       {
171               func   => 'update',
172               args   => ['test', {a => 1, b => 2, c => 3, d => 4, e => 5}, {a => {'in', [1..5]}}],
173               stmt   => 'UPDATE test SET a = ?, b = ?, c = ?, d = ?, e = ? WHERE ( a IN ( ?, ?, ?, ?, ? ) )',
174               stmt_q => 'UPDATE `test` SET `a` = ?, `b` = ?, `c` = ?, `d` = ?, `e` = ? WHERE ( `a` IN ( ?, ?, ?, ?, ? ) )',
175               bind   => [qw/1 2 3 4 5 1 2 3 4 5/],
176       },
177       {
178               func   => 'update',
179               args   => ['test', {a => 1, b => \["to_date(?, 'MM/DD/YY')", '02/02/02']}, {a => {'between', [1,2]}}],
180               stmt   => 'UPDATE test SET a = ?, b = to_date(?, \'MM/DD/YY\') WHERE ( a BETWEEN ? AND ? )',
181               stmt_q => 'UPDATE `test` SET `a` = ?, `b` = to_date(?, \'MM/DD/YY\') WHERE ( `a` BETWEEN ? AND ? )',
182               bind   => [qw(1 02/02/02 1 2)],
183       },
184       {
185               func   => 'insert',
186               args   => ['test.table', {high_limit => \'max(all_limits)', low_limit => 4} ],
187               stmt   => 'INSERT INTO test.table (high_limit, low_limit) VALUES (max(all_limits), ?)',
188               stmt_q => 'INSERT INTO `test`.`table` (`high_limit`, `low_limit`) VALUES (max(all_limits), ?)',
189               bind   => ['4'],
190       },
191       {
192               func   => 'insert',
193               args   => ['test.table', [ \'max(all_limits)', 4 ] ],
194               stmt   => 'INSERT INTO test.table VALUES (max(all_limits), ?)',
195               stmt_q => 'INSERT INTO `test`.`table` VALUES (max(all_limits), ?)',
196               bind   => ['4'],
197       },
198       {
199               func   => 'insert',
200               new    => {bindtype => 'columns'},
201               args   => ['test.table', {one => 2, three => 4, five => 6} ],
202               stmt   => 'INSERT INTO test.table (five, one, three) VALUES (?, ?, ?)',
203               stmt_q => 'INSERT INTO `test`.`table` (`five`, `one`, `three`) VALUES (?, ?, ?)',
204               bind   => [['five', 6], ['one', 2], ['three', 4]],  # alpha order, man...
205       },
206       {
207               func   => 'select',
208               new    => {bindtype => 'columns', case => 'lower'},
209               args   => ['test.table', [qw/one two three/], {one => 2, three => 4, five => 6} ],
210               stmt   => 'select one, two, three from test.table where ( five = ? and one = ? and three = ? )',
211               stmt_q => 'select `one`, `two`, `three` from `test`.`table` where ( `five` = ? and `one` = ? and `three` = ? )',
212               bind   => [['five', 6], ['one', 2], ['three', 4]],  # alpha order, man...
213       },
214       {
215               func   => 'update',
216               new    => {bindtype => 'columns', cmp => 'like'},
217               args   => ['testin.table2', {One => 22, Three => 44, FIVE => 66},
218                                           {Beer => 'is', Yummy => '%YES%', IT => ['IS','REALLY','GOOD']}],
219               stmt   => 'UPDATE testin.table2 SET FIVE = ?, One = ?, Three = ? WHERE '
220                        . '( Beer LIKE ? AND ( ( IT LIKE ? ) OR ( IT LIKE ? ) OR ( IT LIKE ? ) ) AND Yummy LIKE ? )',
221               stmt_q => 'UPDATE `testin`.`table2` SET `FIVE` = ?, `One` = ?, `Three` = ? WHERE '
222                        . '( `Beer` LIKE ? AND ( ( `IT` LIKE ? ) OR ( `IT` LIKE ? ) OR ( `IT` LIKE ? ) ) AND `Yummy` LIKE ? )',
223               bind   => [['FIVE', 66], ['One', 22], ['Three', 44], ['Beer','is'],
224                          ['IT','IS'], ['IT','REALLY'], ['IT','GOOD'], ['Yummy','%YES%']],
225       },
226       {
227               func   => 'select',
228               args   => ['test', '*', {priority => [ -and => {'!=', 2}, { -not_like => '3%'} ]}],
229               stmt   => 'SELECT * FROM test WHERE ( ( ( priority != ? ) AND ( priority NOT LIKE ? ) ) )',
230               stmt_q => 'SELECT * FROM `test` WHERE ( ( ( `priority` != ? ) AND ( `priority` NOT LIKE ? ) ) )',
231               bind   => [qw(2 3%)],
232       },
233       {
234               func   => 'select',
235               args   => ['Yo Momma', '*', { user => 'nwiger',
236                                        -nest => [ workhrs => {'>', 20}, geo => 'ASIA' ] }],
237               stmt   => 'SELECT * FROM Yo Momma WHERE ( ( ( workhrs > ? ) OR ( geo = ? ) ) AND user = ? )',
238               stmt_q => 'SELECT * FROM `Yo Momma` WHERE ( ( ( `workhrs` > ? ) OR ( `geo` = ? ) ) AND `user` = ? )',
239               bind   => [qw(20 ASIA nwiger)],
240       },
241       {
242               func   => 'update',
243               args   => ['taco_punches', { one => 2, three => 4 },
244                                          { bland => [ -and => {'!=', 'yes'}, {'!=', 'YES'} ],
245                                            tasty => { '!=', [qw(yes YES)] },
246                                            -nest => [ face => [ -or => {'=', 'mr.happy'}, {'=', undef} ] ] },
247                         ],
248               warns  => qr/\QA multi-element arrayref as an argument to the inequality op '!=' is technically equivalent to an always-true 1=1/,
249
250               stmt   => 'UPDATE taco_punches SET one = ?, three = ? WHERE ( ( ( ( ( face = ? ) OR ( face IS NULL ) ) ) )'
251                       . ' AND ( ( bland != ? ) AND ( bland != ? ) ) AND ( ( tasty != ? ) OR ( tasty != ? ) ) )',
252               stmt_q => 'UPDATE `taco_punches` SET `one` = ?, `three` = ? WHERE ( ( ( ( ( `face` = ? ) OR ( `face` IS NULL ) ) ) )'
253                       . ' AND ( ( `bland` != ? ) AND ( `bland` != ? ) ) AND ( ( `tasty` != ? ) OR ( `tasty` != ? ) ) )',
254               bind   => [qw(2 4 mr.happy yes YES yes YES)],
255       },
256       {
257               func   => 'select',
258               args   => ['jeff', '*', { name => {'ilike', '%smith%', -not_in => ['Nate','Jim','Bob','Sally']},
259                                        -nest => [ -or => [ -and => [age => { -between => [20,30] }, age => {'!=', 25} ],
260                                                                    yob => {'<', 1976} ] ] } ],
261               stmt   => 'SELECT * FROM jeff WHERE ( ( ( ( ( ( ( age BETWEEN ? AND ? ) AND ( age != ? ) ) ) OR ( yob < ? ) ) ) )'
262                       . ' AND name NOT IN ( ?, ?, ?, ? ) AND name ILIKE ? )',
263               stmt_q => 'SELECT * FROM `jeff` WHERE ( ( ( ( ( ( ( `age` BETWEEN ? AND ? ) AND ( `age` != ? ) ) ) OR ( `yob` < ? ) ) ) )'
264                       . ' AND `name` NOT IN ( ?, ?, ?, ? ) AND `name` ILIKE ? )',
265               bind   => [qw(20 30 25 1976 Nate Jim Bob Sally %smith%)]
266       },
267       {
268               func   => 'update',
269               args   => ['fhole', {fpoles => 4}, [
270                           { race => [qw/-or black white asian /] },
271                           { -nest => { firsttime => [-or => {'=','yes'}, undef] } },
272                           { -and => [ { firstname => {-not_like => 'candace'} }, { lastname => {-in => [qw(jugs canyon towers)] } } ] },
273                         ] ],
274               stmt   => 'UPDATE fhole SET fpoles = ? WHERE ( ( ( ( ( ( ( race = ? ) OR ( race = ? ) OR ( race = ? ) ) ) ) ) )'
275                       . ' OR ( ( ( ( firsttime = ? ) OR ( firsttime IS NULL ) ) ) ) OR ( ( ( firstname NOT LIKE ? ) ) AND ( lastname IN (?, ?, ?) ) ) )',
276               stmt_q => 'UPDATE `fhole` SET `fpoles` = ? WHERE ( ( ( ( ( ( ( `race` = ? ) OR ( `race` = ? ) OR ( `race` = ? ) ) ) ) ) )'
277                       . ' OR ( ( ( ( `firsttime` = ? ) OR ( `firsttime` IS NULL ) ) ) ) OR ( ( ( `firstname` NOT LIKE ? ) ) AND ( `lastname` IN( ?, ?, ? )) ) )',
278               bind   => [qw(4 black white asian yes candace jugs canyon towers)]
279       },
280       {
281               func   => 'insert',
282               args   => ['test', {a => 1, b => \["to_date(?, 'MM/DD/YY')", '02/02/02']}],
283               stmt   => 'INSERT INTO test (a, b) VALUES (?, to_date(?, \'MM/DD/YY\'))',
284               stmt_q => 'INSERT INTO `test` (`a`, `b`) VALUES (?, to_date(?, \'MM/DD/YY\'))',
285               bind   => [qw(1 02/02/02)],
286       },
287       {
288               func   => 'select',
289               args   => ['test', '*', { a => \["= to_date(?, 'MM/DD/YY')", '02/02/02']}],
290               stmt   => q{SELECT * FROM test WHERE ( a = to_date(?, 'MM/DD/YY') )},
291               stmt_q => q{SELECT * FROM `test` WHERE ( `a` = to_date(?, 'MM/DD/YY') )},
292               bind   => ['02/02/02'],
293       },
294       {
295               func   => 'insert',
296               new    => {array_datatypes => 1},
297               args   => ['test', {a => 1, b => [1, 1, 2, 3, 5, 8]}],
298               stmt   => 'INSERT INTO test (a, b) VALUES (?, ?)',
299               stmt_q => 'INSERT INTO `test` (`a`, `b`) VALUES (?, ?)',
300               bind   => [1, [1, 1, 2, 3, 5, 8]],
301       },
302       {
303               func   => 'insert',
304               new    => {bindtype => 'columns', array_datatypes => 1},
305               args   => ['test', {a => 1, b => [1, 1, 2, 3, 5, 8]}],
306               stmt   => 'INSERT INTO test (a, b) VALUES (?, ?)',
307               stmt_q => 'INSERT INTO `test` (`a`, `b`) VALUES (?, ?)',
308               bind   => [[a => 1], [b => [1, 1, 2, 3, 5, 8]]],
309       },
310       {
311               func   => 'update',
312               new    => {array_datatypes => 1},
313               args   => ['test', {a => 1, b => [1, 1, 2, 3, 5, 8]}],
314               stmt   => 'UPDATE test SET a = ?, b = ?',
315               stmt_q => 'UPDATE `test` SET `a` = ?, `b` = ?',
316               bind   => [1, [1, 1, 2, 3, 5, 8]],
317       },
318       {
319               func   => 'update',
320               new    => {bindtype => 'columns', array_datatypes => 1},
321               args   => ['test', {a => 1, b => [1, 1, 2, 3, 5, 8]}],
322               stmt   => 'UPDATE test SET a = ?, b = ?',
323               stmt_q => 'UPDATE `test` SET `a` = ?, `b` = ?',
324               bind   => [[a => 1], [b => [1, 1, 2, 3, 5, 8]]],
325       },
326       {
327               func   => 'select',
328               args   => ['test', '*', { a => {'>', \'1 + 1'}, b => 8 }],
329               stmt   => 'SELECT * FROM test WHERE ( a > 1 + 1 AND b = ? )',
330               stmt_q => 'SELECT * FROM `test` WHERE ( `a` > 1 + 1 AND `b` = ? )',
331               bind   => [8],
332       },
333       {
334               func   => 'select',
335               args   => ['test', '*', { a => {'<' => \["to_date(?, 'MM/DD/YY')", '02/02/02']}, b => 8 }],
336               stmt   => 'SELECT * FROM test WHERE ( a < to_date(?, \'MM/DD/YY\') AND b = ? )',
337               stmt_q => 'SELECT * FROM `test` WHERE ( `a` < to_date(?, \'MM/DD/YY\') AND `b` = ? )',
338               bind   => ['02/02/02', 8],
339       },
340       { #TODO in SQLA >= 2.0 it will die instead (we kept this just because old SQLA passed it through)
341               func   => 'insert',
342               args   => ['test', {a => 1, b => 2, c => 3, d => 4, e => { answer => 42 }}],
343               stmt   => 'INSERT INTO test (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)',
344               stmt_q => 'INSERT INTO `test` (`a`, `b`, `c`, `d`, `e`) VALUES (?, ?, ?, ?, ?)',
345               bind   => [qw/1 2 3 4/, { answer => 42}],
346               warns  => qr/HASH ref as bind value in insert is not supported/i,
347       },
348       {
349               func   => 'update',
350               args   => ['test', {a => 1, b => \["42"]}, {a => {'between', [1,2]}}],
351               stmt   => 'UPDATE test SET a = ?, b = 42 WHERE ( a BETWEEN ? AND ? )',
352               stmt_q => 'UPDATE `test` SET `a` = ?, `b` = 42 WHERE ( `a` BETWEEN ? AND ? )',
353               bind   => [qw(1 1 2)],
354       },
355       {
356               func   => 'insert',
357               args   => ['test', {a => 1, b => \["42"]}],
358               stmt   => 'INSERT INTO test (a, b) VALUES (?, 42)',
359               stmt_q => 'INSERT INTO `test` (`a`, `b`) VALUES (?, 42)',
360               bind   => [qw(1)],
361       },
362       {
363               func   => 'select',
364               args   => ['test', '*', { a => \["= 42"], b => 1}],
365               stmt   => q{SELECT * FROM test WHERE ( a = 42 ) AND (b = ? )},
366               stmt_q => q{SELECT * FROM `test` WHERE ( `a` = 42 ) AND ( `b` = ? )},
367               bind   => [qw(1)],
368       },
369       {
370               func   => 'select',
371               args   => ['test', '*', { a => {'<' => \["42"]}, b => 8 }],
372               stmt   => 'SELECT * FROM test WHERE ( a < 42 AND b = ? )',
373               stmt_q => 'SELECT * FROM `test` WHERE ( `a` < 42 AND `b` = ? )',
374               bind   => [qw(8)],
375       },
376       {
377               func   => 'insert',
378               new    => {bindtype => 'columns'},
379               args   => ['test', {a => 1, b => \["to_date(?, 'MM/DD/YY')", [dummy => '02/02/02']]}],
380               stmt   => 'INSERT INTO test (a, b) VALUES (?, to_date(?, \'MM/DD/YY\'))',
381               stmt_q => 'INSERT INTO `test` (`a`, `b`) VALUES (?, to_date(?, \'MM/DD/YY\'))',
382               bind   => [[a => '1'], [dummy => '02/02/02']],
383       },
384       {
385               func   => 'update',
386               new    => {bindtype => 'columns'},
387               args   => ['test', {a => 1, b => \["to_date(?, 'MM/DD/YY')", [dummy => '02/02/02']]}, {a => {'between', [1,2]}}],
388               stmt   => 'UPDATE test SET a = ?, b = to_date(?, \'MM/DD/YY\') WHERE ( a BETWEEN ? AND ? )',
389               stmt_q => 'UPDATE `test` SET `a` = ?, `b` = to_date(?, \'MM/DD/YY\') WHERE ( `a` BETWEEN ? AND ? )',
390               bind   => [[a => '1'], [dummy => '02/02/02'], [a => '1'], [a => '2']],
391       },
392       {
393               func   => 'select',
394               new    => {bindtype => 'columns'},
395               args   => ['test', '*', { a => \["= to_date(?, 'MM/DD/YY')", [dummy => '02/02/02']]}],
396               stmt   => q{SELECT * FROM test WHERE ( a = to_date(?, 'MM/DD/YY') )},
397               stmt_q => q{SELECT * FROM `test` WHERE ( `a` = to_date(?, 'MM/DD/YY') )},
398               bind   => [[dummy => '02/02/02']],
399       },
400       {
401               func   => 'select',
402               new    => {bindtype => 'columns'},
403               args   => ['test', '*', { a => {'<' => \["to_date(?, 'MM/DD/YY')", [dummy => '02/02/02']]}, b => 8 }],
404               stmt   => 'SELECT * FROM test WHERE ( a < to_date(?, \'MM/DD/YY\') AND b = ? )',
405               stmt_q => 'SELECT * FROM `test` WHERE ( `a` < to_date(?, \'MM/DD/YY\') AND `b` = ? )',
406               bind   => [[dummy => '02/02/02'], [b => 8]],
407       },
408       {
409               func   => 'insert',
410               new    => {bindtype => 'columns'},
411               args   => ['test', {a => 1, b => \["to_date(?, 'MM/DD/YY')", '02/02/02']}],
412               throws => qr/bindtype 'columns' selected, you need to pass: \[column_name => bind_value\]/,
413       },
414       {
415               func   => 'update',
416               new    => {bindtype => 'columns'},
417               args   => ['test', {a => 1, b => \["to_date(?, 'MM/DD/YY')", '02/02/02']}, {a => {'between', [1,2]}}],
418               throws => qr/bindtype 'columns' selected, you need to pass: \[column_name => bind_value\]/,
419       },
420       {
421               func   => 'select',
422               new    => {bindtype => 'columns'},
423               args   => ['test', '*', { a => \["= to_date(?, 'MM/DD/YY')", '02/02/02']}],
424               throws => qr/bindtype 'columns' selected, you need to pass: \[column_name => bind_value\]/,
425       },
426       {
427               func   => 'select',
428               new    => {bindtype => 'columns'},
429               args   => ['test', '*', { a => {'<' => \["to_date(?, 'MM/DD/YY')", '02/02/02']}, b => 8 }],
430               throws => qr/bindtype 'columns' selected, you need to pass: \[column_name => bind_value\]/,
431       },
432       {
433               func   => 'select',
434               args   => ['test', '*', { foo => { '>=' => [] }} ],
435               throws => qr/\Qoperator '>=' applied on an empty array (field 'foo')/,
436       },
437       {
438               func   => 'select',
439               new    => {bindtype => 'columns'},
440               args   => ['test', '*', { a => {-in => \["(SELECT d FROM to_date(?, 'MM/DD/YY') AS d)", [dummy => '02/02/02']]}, b => 8 }],
441               stmt   => 'SELECT * FROM test WHERE ( a IN (SELECT d FROM to_date(?, \'MM/DD/YY\') AS d) AND b = ? )',
442               stmt_q => 'SELECT * FROM `test` WHERE ( `a` IN (SELECT d FROM to_date(?, \'MM/DD/YY\') AS d) AND `b` = ? )',
443               bind   => [[dummy => '02/02/02'], [b => 8]],
444       },
445       {
446               func   => 'select',
447               new    => {bindtype => 'columns'},
448               args   => ['test', '*', { a => {-in => \["(SELECT d FROM to_date(?, 'MM/DD/YY') AS d)", '02/02/02']}, b => 8 }],
449               throws => qr/bindtype 'columns' selected, you need to pass: \[column_name => bind_value\]/,
450       },
451       {
452               func   => 'insert',
453               new    => {bindtype => 'columns'},
454               args   => ['test', {a => 1, b => \["to_date(?, 'MM/DD/YY')", [{dummy => 1} => '02/02/02']]}],
455               stmt   => 'INSERT INTO test (a, b) VALUES (?, to_date(?, \'MM/DD/YY\'))',
456               stmt_q => 'INSERT INTO `test` (`a`, `b`) VALUES (?, to_date(?, \'MM/DD/YY\'))',
457               bind   => [[a => '1'], [{dummy => 1} => '02/02/02']],
458       },
459       {
460               func   => 'update',
461               new    => {bindtype => 'columns'},
462               args   => ['test', {a => 1, b => \["to_date(?, 'MM/DD/YY')", [{dummy => 1} => '02/02/02']], c => { -lower => 'foo' }}, {a => {'between', [1,2]}}],
463               stmt   => "UPDATE test SET a = ?, b = to_date(?, 'MM/DD/YY'), c = LOWER ? WHERE ( a BETWEEN ? AND ? )",
464               stmt_q => "UPDATE `test` SET `a` = ?, `b` = to_date(?, 'MM/DD/YY'), `c` = LOWER ? WHERE ( `a` BETWEEN ? AND ? )",
465               bind   => [[a => '1'], [{dummy => 1} => '02/02/02'], [c => 'foo'], [a => '1'], [a => '2']],
466       },
467       {
468               func   => 'select',
469               new    => {bindtype => 'columns'},
470               args   => ['test', '*', { a => \["= to_date(?, 'MM/DD/YY')", [{dummy => 1} => '02/02/02']]}],
471               stmt   => q{SELECT * FROM test WHERE ( a = to_date(?, 'MM/DD/YY') )},
472               stmt_q => q{SELECT * FROM `test` WHERE ( `a` = to_date(?, 'MM/DD/YY') )},
473               bind   => [[{dummy => 1} => '02/02/02']],
474       },
475       {
476               func   => 'select',
477               new    => {bindtype => 'columns'},
478               args   => ['test', '*', { a => {'<' => \["to_date(?, 'MM/DD/YY')", [{dummy => 1} => '02/02/02']]}, b => 8 }],
479               stmt   => 'SELECT * FROM test WHERE ( a < to_date(?, \'MM/DD/YY\') AND b = ? )',
480               stmt_q => 'SELECT * FROM `test` WHERE ( `a` < to_date(?, \'MM/DD/YY\') AND `b` = ? )',
481               bind   => [[{dummy => 1} => '02/02/02'], [b => 8]],
482       },
483       {
484               func   => 'select',
485               new    => {bindtype => 'columns'},
486               args   => ['test', '*', { -or => [ -and => [ a => 'a', b => 'b' ], -and => [ c => 'c', d => 'd' ]  ]  }],
487               stmt   => 'SELECT * FROM test WHERE ( a = ? AND b = ? ) OR ( c = ? AND d = ?  )',
488               stmt_q => 'SELECT * FROM `test` WHERE ( `a` = ? AND `b` = ?  ) OR ( `c` = ? AND `d` = ? )',
489               bind   => [[a => 'a'], [b => 'b'], [ c => 'c'],[ d => 'd']],
490       },
491       {
492               func   => 'select',
493               new    => {bindtype => 'columns'},
494               args   => ['test', '*', [ { a => 1, b => 1}, [ a => 2, b => 2] ] ],
495               stmt   => 'SELECT * FROM test WHERE ( a = ? AND b = ? ) OR ( a = ? OR b = ? )',
496               stmt_q => 'SELECT * FROM `test` WHERE ( `a` = ? AND `b` = ? ) OR ( `a` = ? OR `b` = ? )',
497               bind   => [[a => 1], [b => 1], [ a => 2], [ b => 2]],
498       },
499       {
500               func   => 'select',
501               new    => {bindtype => 'columns'},
502               args   => ['test', '*', [ [ a => 1, b => 1], { a => 2, b => 2 } ] ],
503               stmt   => 'SELECT * FROM test WHERE ( a = ? OR b = ? ) OR ( a = ? AND b = ? )',
504               stmt_q => 'SELECT * FROM `test` WHERE ( `a` = ? OR `b` = ? ) OR ( `a` = ? AND `b` = ? )',
505               bind   => [[a => 1], [b => 1], [ a => 2], [ b => 2]],
506       },
507       {
508               func   => 'insert',
509               args   => ['test', [qw/1 2 3 4 5/], { returning => 'id' }],
510               stmt   => 'INSERT INTO test VALUES (?, ?, ?, ?, ?) RETURNING id',
511               stmt_q => 'INSERT INTO `test` VALUES (?, ?, ?, ?, ?) RETURNING `id`',
512               bind   => [qw/1 2 3 4 5/],
513       },
514       {
515               func   => 'insert',
516               args   => ['test', [qw/1 2 3 4 5/], { returning => 'id, foo, bar' }],
517               stmt   => 'INSERT INTO test VALUES (?, ?, ?, ?, ?) RETURNING id, foo, bar',
518               stmt_q => 'INSERT INTO `test` VALUES (?, ?, ?, ?, ?) RETURNING `id, foo, bar`',
519               bind   => [qw/1 2 3 4 5/],
520       },
521       {
522               func   => 'insert',
523               args   => ['test', [qw/1 2 3 4 5/], { returning => [qw(id  foo  bar) ] }],
524               stmt   => 'INSERT INTO test VALUES (?, ?, ?, ?, ?) RETURNING id, foo, bar',
525               stmt_q => 'INSERT INTO `test` VALUES (?, ?, ?, ?, ?) RETURNING `id`, `foo`, `bar`',
526               bind   => [qw/1 2 3 4 5/],
527       },
528       {
529               func   => 'insert',
530               args   => ['test', [qw/1 2 3 4 5/], { returning => \'id, foo, bar' }],
531               stmt   => 'INSERT INTO test VALUES (?, ?, ?, ?, ?) RETURNING id, foo, bar',
532               stmt_q => 'INSERT INTO `test` VALUES (?, ?, ?, ?, ?) RETURNING id, foo, bar',
533               bind   => [qw/1 2 3 4 5/],
534       },
535       {
536               func   => 'insert',
537               args   => ['test', [qw/1 2 3 4 5/], { returning => \'id' }],
538               stmt   => 'INSERT INTO test VALUES (?, ?, ?, ?, ?) RETURNING id',
539               stmt_q => 'INSERT INTO `test` VALUES (?, ?, ?, ?, ?) RETURNING id',
540               bind   => [qw/1 2 3 4 5/],
541       },
542       {
543               func   => 'select',
544               new    => {bindtype => 'columns'},
545               args   => ['test', '*', [ Y => { '=' => { -max => { -LENGTH => { -min => 'x' } } } } ] ],
546               stmt   => 'SELECT * FROM test WHERE ( Y = ( MAX( LENGTH( MIN ? ) ) ) )',
547               stmt_q => 'SELECT * FROM `test` WHERE ( `Y` = ( MAX( LENGTH( MIN ? ) ) ) )',
548               bind   => [[Y => 'x']],
549       },
550       {
551               func => 'select',
552               args => ['test', '*', { a => { '=' => undef }, b => { -is => undef }, c => { -like => undef } }],
553               stmt => 'SELECT * FROM test WHERE ( a IS NULL AND b IS NULL AND c IS NULL )',
554               stmt_q => 'SELECT * FROM `test` WHERE ( `a` IS NULL AND `b` IS NULL AND `c` IS NULL )',
555               bind => [],
556               warns => qr/\QSupplying an undefined argument to 'LIKE' is deprecated/,
557       },
558       {
559               func => 'select',
560               args => ['test', '*', { a => { '!=' => undef }, b => { -is_not => undef }, c => { -not_like => undef } }],
561               stmt => 'SELECT * FROM test WHERE ( a IS NOT NULL AND b IS NOT  NULL AND c IS NOT  NULL )',
562               stmt_q => 'SELECT * FROM `test` WHERE ( `a` IS NOT  NULL AND `b` IS NOT  NULL AND `c` IS NOT  NULL )',
563               bind => [],
564               warns => qr/\QSupplying an undefined argument to 'NOT LIKE' is deprecated/,
565       },
566       {
567               func => 'select',
568               args => ['test', '*', { a => { IS => undef }, b => { LIKE => undef } }],
569               stmt => 'SELECT * FROM test WHERE ( a IS NULL AND b IS NULL )',
570               stmt_q => 'SELECT * FROM `test` WHERE ( `a` IS NULL AND `b` IS NULL )',
571               bind => [],
572               warns => qr/\QSupplying an undefined argument to 'LIKE' is deprecated/,
573       },
574       {
575               func => 'select',
576               args => ['test', '*', { a => { 'IS NOT' => undef }, b => { 'NOT LIKE' => undef } }],
577               stmt => 'SELECT * FROM test WHERE ( a IS NOT NULL AND b IS NOT  NULL )',
578               stmt_q => 'SELECT * FROM `test` WHERE ( `a` IS NOT  NULL AND `b` IS NOT  NULL )',
579               bind => [],
580               warns => qr/\QSupplying an undefined argument to 'NOT LIKE' is deprecated/,
581       },
582       {
583               func => 'select',
584               args => ['`test``table`', ['`test``column`']],
585               stmt => 'SELECT `test``column` FROM `test``table`',
586               stmt_q => 'SELECT ```test````column``` FROM ```test````table```',
587               bind => [],
588       },
589       {
590               func => 'select',
591               args => ['`test\\`table`', ['`test`\\column`']],
592               stmt => 'SELECT `test`\column` FROM `test\`table`',
593               stmt_q => 'SELECT `\`test\`\\\\column\`` FROM `\`test\\\\\`table\``',
594               esc  => '\\',
595               bind => [],
596       },
597       {
598               func => 'update',
599               args => ['mytable', { foo => 42 }, { baz => 32 }, { returning => 'id' }],
600               stmt => 'UPDATE mytable SET foo = ? WHERE baz = ? RETURNING id',
601               stmt_q => 'UPDATE `mytable` SET `foo` = ? WHERE `baz` = ? RETURNING `id`',
602               bind => [42, 32],
603       },
604       {
605               func => 'update',
606               args => ['mytable', { foo => 42 }, { baz => 32 }, { returning => \'*' }],
607               stmt => 'UPDATE mytable SET foo = ? WHERE baz = ? RETURNING *',
608               stmt_q => 'UPDATE `mytable` SET `foo` = ? WHERE `baz` = ? RETURNING *',
609               bind => [42, 32],
610       },
611       {
612               func => 'update',
613               args => ['mytable', { foo => 42 }, { baz => 32 }, { returning => ['id','created_at'] }],
614               stmt => 'UPDATE mytable SET foo = ? WHERE baz = ? RETURNING id, created_at',
615               stmt_q => 'UPDATE `mytable` SET `foo` = ? WHERE `baz` = ? RETURNING `id`, `created_at`',
616               bind => [42, 32],
617       },
618       {
619               func   => 'delete',
620               args   => ['test', {requestor => undef}, {returning => 'id'}],
621               stmt   => 'DELETE FROM test WHERE ( requestor IS NULL ) RETURNING id',
622               stmt_q => 'DELETE FROM `test` WHERE ( `requestor` IS NULL ) RETURNING `id`',
623               bind   => []
624       },
625       {
626               func   => 'delete',
627               args   => ['test', {requestor => undef}, {returning => \'*'}],
628               stmt   => 'DELETE FROM test WHERE ( requestor IS NULL ) RETURNING *',
629               stmt_q => 'DELETE FROM `test` WHERE ( `requestor` IS NULL ) RETURNING *',
630               bind   => []
631       },
632       {
633               func   => 'delete',
634               args   => ['test', {requestor => undef}, {returning => ['id', 'created_at']}],
635               stmt   => 'DELETE FROM test WHERE ( requestor IS NULL ) RETURNING id, created_at',
636               stmt_q => 'DELETE FROM `test` WHERE ( `requestor` IS NULL ) RETURNING `id`, `created_at`',
637               bind   => []
638       },
639 );
640
641 # check is( not) => undef
642 for my $op (qw(not is is_not), 'is not') {
643   (my $sop = uc $op) =~ s/_/ /gi;
644
645   $sop = 'IS NOT' if $sop eq 'NOT';
646
647   for my $uc (0, 1) {
648     for my $prefix ('', '-') {
649       push @tests, {
650         func => 'where',
651         args => [{ a => { ($prefix . ($uc ? uc $op : lc $op) ) => undef } }],
652         stmt => "WHERE a $sop NULL",
653         stmt_q => "WHERE `a` $sop NULL",
654         bind => [],
655       };
656     }
657   }
658 }
659
660 # check single-element inequality ops for no warnings
661 for my $op (qw(!= <>)) {
662   for my $val (undef, 42) {
663     push @tests, {
664       func => 'where',
665       args => [ { x => { "$_$op" => [ $val ] } } ],
666       stmt => "WHERE x " . ($val ? "$op ?" : 'IS NOT NULL'),
667       stmt_q => "WHERE `x` " . ($val ? "$op ?" : 'IS NOT NULL'),
668       bind => [ $val || () ],
669     } for ('', '-');  # with and without -
670   }
671 }
672
673 # check single-element not-like ops for no warnings, and NULL exception
674 # (the last two "is not X" are a weird syntax, but mebbe a dialect...)
675 for my $op (qw(not_like not_rlike), 'not like', 'not rlike', 'is not like','is not rlike') {
676   (my $sop = uc $op) =~ s/_/ /gi;
677
678   for my $val (undef, 42) {
679     push @tests, {
680       func => 'where',
681       args => [ { x => { "$_$op" => [ $val ] } } ],
682       $val ? (
683         stmt => "WHERE x $sop ?",
684         stmt_q => "WHERE `x` $sop ?",
685         bind => [ $val ],
686       ) : (
687         stmt => "WHERE x IS NOT NULL",
688         stmt_q => "WHERE `x` IS NOT NULL",
689         bind => [],
690         warns => qr/\QSupplying an undefined argument to '$sop' is deprecated/,
691       ),
692     } for ('', '-');  # with and without -
693   }
694 }
695
696 # check all multi-element inequality/not-like ops for warnings
697 for my $op (qw(!= <> not_like not_rlike), 'not like', 'not rlike', 'is not like','is not rlike') {
698   (my $sop = uc $op) =~ s/_/ /gi;
699
700   push @tests, {
701     func => 'where',
702     args => [ { x => { "$_$op" => [ 42, 69 ] } } ],
703     stmt => "WHERE x $sop ? OR x $sop ?",
704     stmt_q => "WHERE `x` $sop ? OR `x` $sop ?",
705     bind => [ 42, 69 ],
706     warns  => qr/\QA multi-element arrayref as an argument to the inequality op '$sop' is technically equivalent to an always-true 1=1/,
707   } for ('', '-');  # with and without -
708 }
709
710 # check all like/not-like ops for empty-arrayref warnings
711 for my $op (qw(like rlike not_like not_rlike), 'not like', 'not rlike', 'is like', 'is not like', 'is rlike', 'is not rlike') {
712   (my $sop = uc $op) =~ s/_/ /gi;
713
714   push @tests, {
715     func => 'where',
716     args => [ { x => { "$_$op" => [] } } ],
717     stmt => ( $sop =~ /NOT/ ? "WHERE 1=1" : "WHERE 0=1" ),
718     stmt_q => ( $sop =~ /NOT/ ? "WHERE 1=1" : "WHERE 0=1" ),
719     bind => [],
720     warns  => qr/\QSupplying an empty arrayref to '$sop' is deprecated/,
721   } for ('', '-');  # with and without -
722 }
723
724 # check emtpty-lhs in a hashpair and arraypair
725 for my $lhs (undef, '') {
726   no warnings 'uninitialized';
727
728 ##
729 ## hard exceptions - never worked
730   for my $where_arg (
731     ( map { $_, { @$_ } }
732       [ $lhs => "foo" ],
733       [ $lhs => { "=" => "bozz" } ],
734       [ $lhs => { "=" => \"bozz" } ],
735       [ $lhs => { -max => \"bizz" } ],
736     ),
737     [ -and => { $lhs => "baz" }, bizz => "buzz" ],
738     [ foo => "bar", { $lhs => "baz" }, bizz => "buzz" ],
739     { foo => "bar", -or => { $lhs => "baz" } },
740
741     # the hashref forms of these work sadly - check for warnings below
742     { foo => "bar", -and => [ $lhs => \"baz" ], bizz => "buzz" },
743     { foo => "bar", -or => [ $lhs => \"baz" ], bizz => "buzz" },
744     [ foo => "bar", [ $lhs => \"baz" ], bizz => "buzz" ],
745     [ foo => "bar", $lhs => \"baz", bizz => "buzz" ],
746     [ foo => "bar", $lhs => \["baz"], bizz => "buzz" ],
747     [ $lhs => \"baz" ],
748     [ $lhs => \["baz"] ],
749   ) {
750     push @tests, {
751       func => 'where',
752       args => [ $where_arg ],
753       throws  => qr/\QSupplying an empty left hand side argument is not supported/,
754     };
755   }
756
757 ##
758 ## deprecations - sorta worked, likely abused by folks
759   for my $where_arg (
760     # the arrayref forms of this never worked and throw above
761     { foo => "bar", -or => { $lhs => \"baz" }, bizz => "buzz" },
762     { foo => "bar", -and => { $lhs => \"baz" }, bizz => "buzz" },
763     { foo => "bar", $lhs => \"baz", bizz => "buzz" },
764     { foo => "bar", $lhs => \["baz"], bizz => "buzz" },
765   ) {
766     push @tests, {
767       func    => 'where',
768       args    => [ $where_arg ],
769       stmt    => 'WHERE baz AND bizz = ? AND foo = ?',
770       stmt_q  => 'WHERE baz AND `bizz` = ? AND `foo` = ?',
771       bind    => [qw( buzz bar )],
772       warns   => qr/\QHash-pairs consisting of an empty string with a literal are deprecated/,
773     };
774   }
775
776   for my $where_arg (
777     { $lhs => \"baz" },
778     { $lhs => \["baz"] },
779   ) {
780     push @tests, {
781       func    => 'where',
782       args    => [ $where_arg ],
783       stmt    => 'WHERE baz',
784       stmt_q  => 'WHERE baz',
785       bind    => [],
786       warns   => qr/\QHash-pairs consisting of an empty string with a literal are deprecated/,
787     }
788   }
789 }
790
791 # check false lhs, silly but possible
792 {
793   for my $where_arg (
794     [ { 0 => "baz" }, bizz => "buzz", foo => "bar" ],
795     [ -or => { foo => "bar", -or => { 0 => "baz" }, bizz => "buzz" } ],
796   ) {
797     push @tests, {
798       func    => 'where',
799       args    => [ $where_arg ],
800       stmt    => 'WHERE 0 = ? OR bizz = ? OR foo = ?',
801       stmt_q  => 'WHERE `0` = ? OR `bizz` = ? OR `foo` = ?',
802       bind    => [qw( baz buzz bar )],
803     };
804   }
805
806   for my $where_arg (
807     { foo => "bar", -and => [ 0 => \"= baz" ], bizz => "buzz" },
808     { foo => "bar", -or => [ 0 => \"= baz" ], bizz => "buzz" },
809
810     { foo => "bar", -and => { 0 => \"= baz" }, bizz => "buzz" },
811     { foo => "bar", -or => { 0 => \"= baz" }, bizz => "buzz" },
812
813     { foo => "bar", 0 => \"= baz", bizz => "buzz" },
814     { foo => "bar", 0 => \["= baz"], bizz => "buzz" },
815   ) {
816     push @tests, {
817       func    => 'where',
818       args    => [ $where_arg ],
819       stmt    => 'WHERE 0 = baz AND bizz = ? AND foo = ?',
820       stmt_q  => 'WHERE `0` = baz AND `bizz` = ? AND `foo` = ?',
821       bind    => [qw( buzz bar )],
822     };
823   }
824
825   for my $where_arg (
826     [ -and => [ 0 => \"= baz" ], bizz => "buzz", foo => "bar" ],
827     [ -or => [ 0 => \"= baz" ], bizz => "buzz", foo => "bar" ],
828     [ 0 => \"= baz", bizz => "buzz", foo => "bar" ],
829     [ 0 => \["= baz"], bizz => "buzz", foo => "bar" ],
830   ) {
831     push @tests, {
832       func    => 'where',
833       args    => [ $where_arg ],
834       stmt    => 'WHERE 0 = baz OR bizz = ? OR foo = ?',
835       stmt_q  => 'WHERE `0` = baz OR `bizz` = ? OR `foo` = ?',
836       bind    => [qw( buzz bar )],
837     };
838   }
839 }
840
841 for my $t (@tests) {
842   my $new = $t->{new} || {};
843
844   for my $quoted (0, 1) {
845
846     my $maker = SQL::Abstract->new(
847       %$new,
848       ($quoted ? (
849         quote_char => '`',
850         name_sep => '.',
851         ( $t->{esc} ? (
852           escape_char => $t->{esc},
853         ) : ())
854       ) : ())
855     );
856
857     my($stmt, @bind);
858
859     my $cref = sub {
860       my $op = $t->{func};
861       ($stmt, @bind) = $maker->$op(@{ $t->{args} });
862     };
863
864     if (my $e = $t->{throws}) {
865       throws_ok(
866         sub { $cref->() },
867         $e,
868       ) || diag dumper({ args => $t->{args}, result => $stmt });
869     }
870     else {
871       lives_ok(sub {
872         warnings_like(
873           sub { $cref->() },
874           $t->{warns} || [],
875         ) || diag dumper({ args => $t->{args}, result => $stmt });
876       }) || diag dumper({ args => $t->{args}, result => $stmt, threw => $@ });
877
878       is_same_sql_bind(
879         $stmt,
880         \@bind,
881         $quoted ? $t->{stmt_q}: $t->{stmt},
882         $t->{bind}
883       ) || diag dumper({ args => $t->{args}, result => $stmt });;
884     }
885   }
886 }
887
888 done_testing;