Commit | Line | Data |
d1f4d28d |
1 | use strict; |
2 | use warnings; |
3 | |
4 | use Test::More; |
5 | use Test::Exception; |
6 | use Storable 'dclone'; |
7 | use lib qw(t/lib); |
8 | use DBICTest ':DiffSQL'; |
9 | |
10 | my $schema = DBICTest->init_schema( |
11 | no_deploy => 1, |
12 | quote_char => '`', |
13 | name_sep => '.', |
14 | ); |
15 | |
f32ef973 |
16 | my $bs = $schema->source('BooksInLibrary'); |
17 | $bs->remove_column('price'); |
18 | $bs->add_columns( |
19 | 'price (#)' => { |
20 | data_type => 'integer', |
21 | is_nullable => 1, |
22 | }, |
70e0e783 |
23 | 'short desc' => { |
24 | data_type => 'text', |
25 | is_nullable => 1, |
26 | }, |
f32ef973 |
27 | ); |
28 | |
d1f4d28d |
29 | my $native_limit_dialect = $schema->storage->sql_maker->{limit_dialect}; |
30 | |
31 | my $where_string = '`me`.`title` = ? AND `source` != ? AND `source` = ?'; |
32 | |
33 | my @where_bind = ( |
34 | [ {} => 'kama sutra' ], |
35 | [ {} => 'Study' ], |
36 | [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' } => 'Library' ], |
37 | ); |
38 | my @select_bind = ( |
39 | [ { sqlt_datatype => 'numeric' } => 11 ], |
40 | [ {} => 12 ], |
41 | [ { sqlt_datatype => 'integer', dbic_colname => 'me.id' } => 13 ], |
42 | ); |
43 | my @group_bind = ( |
44 | [ {} => 21 ], |
45 | ); |
46 | my @having_bind = ( |
47 | [ {} => 31 ], |
48 | ); |
49 | my @order_bind = ( |
50 | [ { sqlt_datatype => 'int' } => 1 ], |
51 | [ { sqlt_datatype => 'varchar', dbic_colname => 'name', sqlt_size => 100 } => 2 ], |
52 | [ {} => 3 ], |
53 | ); |
54 | |
55 | my $tests = { |
56 | |
57 | LimitOffset => { |
58 | limit => [ |
59 | "( |
60 | SELECT `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
61 | FROM `books` `me` |
62 | JOIN `owners` `owner` |
63 | ON `owner`.`id` = `me`.`owner` |
64 | WHERE $where_string |
65 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
66 | HAVING ? |
67 | LIMIT ? |
68 | )", |
69 | [ |
70 | @select_bind, |
71 | @where_bind, |
72 | @group_bind, |
73 | @having_bind, |
74 | [ { sqlt_datatype => 'integer' } => 4 ], |
75 | ], |
76 | ], |
77 | limit_offset => [ |
78 | "( |
79 | SELECT `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
80 | FROM `books` `me` |
81 | JOIN `owners` `owner` |
82 | ON `owner`.`id` = `me`.`owner` |
83 | WHERE $where_string |
84 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
85 | HAVING ? |
86 | LIMIT ? |
87 | OFFSET ? |
88 | )", |
89 | [ |
90 | @select_bind, |
91 | @where_bind, |
92 | @group_bind, |
93 | @having_bind, |
94 | [ { sqlt_datatype => 'integer' } => 4 ], |
95 | [ { sqlt_datatype => 'integer' } => 3 ], |
96 | ], |
97 | ], |
98 | ordered_limit => [ |
99 | "( |
100 | SELECT `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
101 | FROM `books` `me` |
102 | JOIN `owners` `owner` |
103 | ON `owner`.`id` = `me`.`owner` |
104 | WHERE $where_string |
105 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
106 | HAVING ? |
70e0e783 |
107 | ORDER BY ? / ?, ?, `me`.`short desc` |
d1f4d28d |
108 | LIMIT ? |
109 | )", |
110 | [ |
111 | @select_bind, |
112 | @where_bind, |
113 | @group_bind, |
114 | @having_bind, |
115 | @order_bind, |
116 | [ { sqlt_datatype => 'integer' } => 4 ], |
117 | ] |
118 | ], |
119 | ordered_limit_offset => [ |
120 | "( |
121 | SELECT `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
122 | FROM `books` `me` |
123 | JOIN `owners` `owner` |
124 | ON `owner`.`id` = `me`.`owner` |
125 | WHERE $where_string |
126 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
127 | HAVING ? |
70e0e783 |
128 | ORDER BY ? / ?, ?, `me`.`short desc` |
d1f4d28d |
129 | LIMIT ? |
130 | OFFSET ? |
131 | )", |
132 | [ |
133 | @select_bind, |
134 | @where_bind, |
135 | @group_bind, |
136 | @having_bind, |
137 | @order_bind, |
138 | [ { sqlt_datatype => 'integer' } => 4 ], |
139 | [ { sqlt_datatype => 'integer' } => 3 ], |
140 | ], |
141 | ], |
142 | limit_offset_prefetch => [ |
143 | "( |
70e0e783 |
144 | SELECT `me`.`name`, `books`.`id`, `books`.`source`, `books`.`owner`, `books`.`title`, `books`.`price (#)`, `books`.`short desc` |
d1f4d28d |
145 | FROM ( |
146 | SELECT `me`.`name`, `me`.`id` |
147 | FROM `owners` `me` |
148 | LIMIT ? OFFSET ? |
149 | ) `me` |
150 | LEFT JOIN `books` `books` |
151 | ON `books`.`owner` = `me`.`id` |
152 | )", |
153 | [ |
154 | [ { sqlt_datatype => 'integer' } => 3 ], |
155 | [ { sqlt_datatype => 'integer' } => 1 ], |
156 | ] |
157 | ], |
158 | }, |
159 | |
160 | LimitXY => { |
161 | ordered_limit_offset => [ |
162 | "( |
163 | SELECT `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
164 | FROM `books` `me` |
165 | JOIN `owners` `owner` |
166 | ON `owner`.`id` = `me`.`owner` |
167 | WHERE $where_string |
168 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
169 | HAVING ? |
70e0e783 |
170 | ORDER BY ? / ?, ?, `me`.`short desc` |
d1f4d28d |
171 | LIMIT ?, ? |
172 | )", |
173 | [ |
174 | @select_bind, |
175 | @where_bind, |
176 | @group_bind, |
177 | @having_bind, |
178 | @order_bind, |
179 | [ { sqlt_datatype => 'integer' } => 3 ], |
180 | [ { sqlt_datatype => 'integer' } => 4 ], |
181 | ], |
182 | ], |
183 | limit_offset_prefetch => [ |
184 | "( |
70e0e783 |
185 | SELECT `me`.`name`, `books`.`id`, `books`.`source`, `books`.`owner`, `books`.`title`, `books`.`price (#)`, `books`.`short desc` |
d1f4d28d |
186 | FROM ( |
187 | SELECT `me`.`name`, `me`.`id` |
188 | FROM `owners` `me` |
189 | LIMIT ?,? |
190 | ) `me` |
191 | LEFT JOIN `books` `books` |
192 | ON `books`.`owner` = `me`.`id` |
193 | )", |
194 | [ |
195 | [ { sqlt_datatype => 'integer' } => 1 ], |
196 | [ { sqlt_datatype => 'integer' } => 3 ], |
197 | ] |
198 | ], |
199 | }, |
200 | |
201 | SkipFirst => { |
202 | ordered_limit_offset => [ |
203 | "( |
204 | SELECT SKIP ? FIRST ? `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
205 | FROM `books` `me` |
206 | JOIN `owners` `owner` |
207 | ON `owner`.`id` = `me`.`owner` |
208 | WHERE $where_string |
209 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
210 | HAVING ? |
70e0e783 |
211 | ORDER BY ? / ?, ?, `me`.`short desc` |
d1f4d28d |
212 | )", |
213 | [ |
214 | [ { sqlt_datatype => 'integer' } => 3 ], |
215 | [ { sqlt_datatype => 'integer' } => 4 ], |
216 | @select_bind, |
217 | @where_bind, |
218 | @group_bind, |
219 | @having_bind, |
220 | @order_bind, |
221 | ], |
222 | ], |
223 | limit_offset_prefetch => [ |
224 | "( |
70e0e783 |
225 | SELECT `me`.`name`, `books`.`id`, `books`.`source`, `books`.`owner`, `books`.`title`, `books`.`price (#)`, `books`.`short desc` |
d1f4d28d |
226 | FROM ( |
227 | SELECT SKIP ? FIRST ? `me`.`name`, `me`.`id` |
228 | FROM `owners` `me` |
229 | ) `me` |
230 | LEFT JOIN `books` `books` |
231 | ON `books`.`owner` = `me`.`id` |
232 | )", |
233 | [ |
234 | [ { sqlt_datatype => 'integer' } => 1 ], |
235 | [ { sqlt_datatype => 'integer' } => 3 ], |
236 | ] |
237 | ], |
238 | }, |
239 | |
240 | FirstSkip => { |
241 | ordered_limit_offset => [ |
242 | "( |
243 | SELECT FIRST ? SKIP ? `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
244 | FROM `books` `me` |
245 | JOIN `owners` `owner` |
246 | ON `owner`.`id` = `me`.`owner` |
247 | WHERE $where_string |
248 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
249 | HAVING ? |
70e0e783 |
250 | ORDER BY ? / ?, ?, `me`.`short desc` |
d1f4d28d |
251 | )", |
252 | [ |
253 | [ { sqlt_datatype => 'integer' } => 4 ], |
254 | [ { sqlt_datatype => 'integer' } => 3 ], |
255 | @select_bind, |
256 | @where_bind, |
257 | @group_bind, |
258 | @having_bind, |
259 | @order_bind, |
260 | ], |
261 | ], |
262 | limit_offset_prefetch => [ |
263 | "( |
70e0e783 |
264 | SELECT `me`.`name`, `books`.`id`, `books`.`source`, `books`.`owner`, `books`.`title`, `books`.`price (#)`, `books`.`short desc` |
d1f4d28d |
265 | FROM ( |
266 | SELECT FIRST ? SKIP ? `me`.`name`, `me`.`id` |
267 | FROM `owners` `me` |
268 | ) `me` |
269 | LEFT JOIN `books` `books` |
270 | ON `books`.`owner` = `me`.`id` |
271 | )", |
272 | [ |
273 | [ { sqlt_datatype => 'integer' } => 3 ], |
274 | [ { sqlt_datatype => 'integer' } => 1 ], |
275 | ] |
276 | ], |
277 | }, |
278 | |
279 | RowNumberOver => do { |
280 | my $unordered_sql = "( |
281 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
282 | FROM ( |
283 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz`, ROW_NUMBER() OVER() AS `rno__row__index` |
284 | FROM ( |
285 | SELECT `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz` |
286 | FROM `books` `me` |
287 | JOIN `owners` `owner` |
288 | ON `owner`.`id` = `me`.`owner` |
289 | WHERE $where_string |
290 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
291 | HAVING ? |
292 | ) `me` |
293 | ) `me` |
294 | WHERE `rno__row__index` >= ? AND `rno__row__index` <= ? |
295 | )"; |
296 | |
297 | my $ordered_sql = "( |
298 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
299 | FROM ( |
70e0e783 |
300 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz`, ROW_NUMBER() OVER( ORDER BY `ORDER__BY__001`, `ORDER__BY__002`, `ORDER__BY__003` ) AS `rno__row__index` |
d1f4d28d |
301 | FROM ( |
302 | SELECT `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz`, |
70e0e783 |
303 | ? / ? AS `ORDER__BY__001`, ? AS `ORDER__BY__002`, `me`.`short desc` AS `ORDER__BY__003` |
d1f4d28d |
304 | FROM `books` `me` |
305 | JOIN `owners` `owner` |
306 | ON `owner`.`id` = `me`.`owner` |
307 | WHERE $where_string |
308 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
309 | HAVING ? |
310 | ) `me` |
311 | ) `me` |
312 | WHERE `rno__row__index` >= ? AND `rno__row__index` <= ? |
313 | )"; |
314 | |
315 | { |
316 | limit => [$unordered_sql, |
317 | [ |
318 | @select_bind, |
319 | @where_bind, |
320 | @group_bind, |
321 | @having_bind, |
322 | [ { sqlt_datatype => 'integer' } => 1 ], |
323 | [ { sqlt_datatype => 'integer' } => 4 ], |
324 | ], |
325 | ], |
326 | limit_offset => [$unordered_sql, |
327 | [ |
328 | @select_bind, |
329 | @where_bind, |
330 | @group_bind, |
331 | @having_bind, |
332 | [ { sqlt_datatype => 'integer' } => 4 ], |
333 | [ { sqlt_datatype => 'integer' } => 7 ], |
334 | ], |
335 | ], |
336 | ordered_limit => [$ordered_sql, |
337 | [ |
338 | @select_bind, |
339 | @order_bind, |
340 | @where_bind, |
341 | @group_bind, |
342 | @having_bind, |
343 | [ { sqlt_datatype => 'integer' } => 1 ], |
344 | [ { sqlt_datatype => 'integer' } => 4 ], |
345 | ], |
346 | ], |
347 | ordered_limit_offset => [$ordered_sql, |
348 | [ |
349 | @select_bind, |
350 | @order_bind, |
351 | @where_bind, |
352 | @group_bind, |
353 | @having_bind, |
354 | [ { sqlt_datatype => 'integer' } => 4 ], |
355 | [ { sqlt_datatype => 'integer' } => 7 ], |
356 | ], |
357 | ], |
358 | limit_offset_prefetch => [ |
359 | "( |
70e0e783 |
360 | SELECT `me`.`name`, `books`.`id`, `books`.`source`, `books`.`owner`, `books`.`title`, `books`.`price (#)`, `books`.`short desc` |
d1f4d28d |
361 | FROM ( |
362 | SELECT `me`.`name`, `me`.`id` |
363 | FROM ( |
364 | SELECT `me`.`name`, `me`.`id`, ROW_NUMBER() OVER() AS `rno__row__index` |
365 | FROM ( |
366 | SELECT `me`.`name`, `me`.`id` FROM `owners` `me` |
367 | ) `me` |
368 | ) `me` |
369 | WHERE `rno__row__index` >= ? AND `rno__row__index` <= ? |
370 | ) `me` |
371 | LEFT JOIN `books` `books` |
372 | ON `books`.`owner` = `me`.`id` |
373 | )", |
374 | [ |
375 | [ { sqlt_datatype => 'integer' } => 2 ], |
376 | [ { sqlt_datatype => 'integer' } => 4 ], |
377 | ] |
378 | ], |
379 | }; |
380 | }, |
381 | |
382 | RowNum => do { |
383 | my $limit_sql = sub { |
384 | sprintf "( |
385 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
386 | FROM ( |
387 | SELECT `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz` |
388 | FROM `books` `me` |
389 | JOIN `owners` `owner` |
390 | ON `owner`.`id` = `me`.`owner` |
391 | WHERE $where_string |
392 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
393 | HAVING ? |
394 | %s |
395 | ) `me` |
396 | WHERE ROWNUM <= ? |
397 | )", $_[0] || ''; |
398 | }; |
399 | |
400 | { |
401 | limit => [ $limit_sql->(), |
402 | [ |
403 | @select_bind, |
404 | @where_bind, |
405 | @group_bind, |
406 | @having_bind, |
407 | [ { sqlt_datatype => 'integer' } => 4 ], |
408 | ], |
409 | ], |
410 | limit_offset => [ |
411 | "( |
412 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
413 | FROM ( |
414 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz`, ROWNUM AS `rownum__index` |
415 | FROM ( |
416 | SELECT `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz` |
417 | FROM `books` `me` |
418 | JOIN `owners` `owner` |
419 | ON `owner`.`id` = `me`.`owner` |
420 | WHERE $where_string |
421 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
422 | HAVING ? |
423 | ) `me` |
424 | ) `me` |
425 | WHERE `rownum__index` BETWEEN ? AND ? |
426 | )", |
427 | [ |
428 | @select_bind, |
429 | @where_bind, |
430 | @group_bind, |
431 | @having_bind, |
432 | [ { sqlt_datatype => 'integer' } => 4 ], |
433 | [ { sqlt_datatype => 'integer' } => 7 ], |
434 | ], |
435 | ], |
70e0e783 |
436 | ordered_limit => [ $limit_sql->('ORDER BY ? / ?, ?, `me`.`short desc`'), |
d1f4d28d |
437 | [ |
438 | @select_bind, |
439 | @where_bind, |
440 | @group_bind, |
441 | @having_bind, |
442 | @order_bind, |
443 | [ { sqlt_datatype => 'integer' } => 4 ], |
444 | ], |
445 | ], |
446 | ordered_limit_offset => [ |
447 | "( |
448 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
449 | FROM ( |
450 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz`, ROWNUM AS `rownum__index` |
451 | FROM ( |
452 | SELECT `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz` |
453 | FROM `books` `me` |
454 | JOIN `owners` `owner` |
455 | ON `owner`.`id` = `me`.`owner` |
456 | WHERE $where_string |
457 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
458 | HAVING ? |
70e0e783 |
459 | ORDER BY ? / ?, ?, `me`.`short desc` |
d1f4d28d |
460 | ) `me` |
461 | WHERE ROWNUM <= ? |
462 | ) `me` |
463 | WHERE `rownum__index` >= ? |
464 | )", |
465 | [ |
466 | @select_bind, |
467 | @where_bind, |
468 | @group_bind, |
469 | @having_bind, |
470 | @order_bind, |
471 | [ { sqlt_datatype => 'integer' } => 7 ], |
472 | [ { sqlt_datatype => 'integer' } => 4 ], |
473 | ], |
474 | ], |
475 | limit_offset_prefetch => [ |
476 | "( |
70e0e783 |
477 | SELECT `me`.`name`, `books`.`id`, `books`.`source`, `books`.`owner`, `books`.`title`, `books`.`price (#)`, `books`.`short desc` |
d1f4d28d |
478 | FROM ( |
479 | SELECT `me`.`name`, `me`.`id` |
480 | FROM ( |
481 | SELECT `me`.`name`, `me`.`id`, ROWNUM AS `rownum__index` |
482 | FROM ( |
483 | SELECT `me`.`name`, `me`.`id` |
484 | FROM `owners` `me` |
485 | ) `me` |
486 | ) `me` WHERE `rownum__index` BETWEEN ? AND ? |
487 | ) `me` |
488 | LEFT JOIN `books` `books` |
489 | ON `books`.`owner` = `me`.`id` |
490 | )", |
491 | [ |
492 | [ { sqlt_datatype => 'integer' } => 2 ], |
493 | [ { sqlt_datatype => 'integer' } => 4 ], |
494 | ] |
495 | ], |
496 | }; |
497 | }, |
498 | |
499 | FetchFirst => { |
500 | limit => [ |
501 | "( |
502 | SELECT `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
503 | FROM `books` `me` |
504 | JOIN `owners` `owner` |
505 | ON `owner`.`id` = `me`.`owner` |
506 | WHERE $where_string |
507 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
508 | HAVING ? |
509 | FETCH FIRST 4 ROWS ONLY |
510 | )", |
511 | [ |
512 | @select_bind, |
513 | @where_bind, |
514 | @group_bind, |
515 | @having_bind, |
516 | ], |
517 | ], |
518 | limit_offset => [ |
519 | "( |
520 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
521 | FROM ( |
522 | SELECT `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz` |
523 | FROM `books` `me` |
524 | JOIN `owners` `owner` |
525 | ON `owner`.`id` = `me`.`owner` |
526 | WHERE $where_string |
527 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
528 | HAVING ? |
529 | ORDER BY `me`.`id` |
530 | FETCH FIRST 7 ROWS ONLY |
531 | ) `me` |
532 | ORDER BY `me`.`id` DESC |
533 | FETCH FIRST 4 ROWS ONLY |
534 | )", |
535 | [ |
536 | @select_bind, |
537 | @where_bind, |
538 | @group_bind, |
539 | @having_bind, |
540 | ], |
541 | ], |
542 | ordered_limit => [ |
543 | "( |
544 | SELECT `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
545 | FROM `books` `me` |
546 | JOIN `owners` `owner` |
547 | ON `owner`.`id` = `me`.`owner` |
548 | WHERE $where_string |
549 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
550 | HAVING ? |
70e0e783 |
551 | ORDER BY ? / ?, ?, `me`.`short desc` |
d1f4d28d |
552 | FETCH FIRST 4 ROWS ONLY |
553 | )", |
554 | [ |
555 | @select_bind, |
556 | @where_bind, |
557 | @group_bind, |
558 | @having_bind, |
559 | @order_bind, |
560 | ], |
561 | ], |
562 | ordered_limit_offset => [ |
563 | "( |
564 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
565 | FROM ( |
70e0e783 |
566 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz`, `ORDER__BY__001`, `ORDER__BY__002`, `ORDER__BY__003` |
d1f4d28d |
567 | FROM ( |
70e0e783 |
568 | SELECT `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz`, ? / ? AS `ORDER__BY__001`, ? AS `ORDER__BY__002`, `me`.`short desc` AS `ORDER__BY__003` |
d1f4d28d |
569 | FROM `books` `me` |
570 | JOIN `owners` `owner` |
571 | ON `owner`.`id` = `me`.`owner` |
572 | WHERE $where_string |
573 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
574 | HAVING ? |
70e0e783 |
575 | ORDER BY ? / ?, ?, `me`.`short desc` |
d1f4d28d |
576 | FETCH FIRST 7 ROWS ONLY |
577 | ) `me` |
70e0e783 |
578 | ORDER BY `ORDER__BY__001` DESC, `ORDER__BY__002` DESC, `ORDER__BY__003` DESC |
d1f4d28d |
579 | FETCH FIRST 4 ROWS ONLY |
580 | ) `me` |
70e0e783 |
581 | ORDER BY `ORDER__BY__001`, `ORDER__BY__002`, `ORDER__BY__003` |
d1f4d28d |
582 | )", |
583 | [ |
584 | @select_bind, |
585 | @order_bind, |
586 | @where_bind, |
587 | @group_bind, |
588 | @having_bind, |
589 | @{ dclone \@order_bind }, # without this is_deeply throws a fit |
590 | ], |
591 | ], |
592 | limit_offset_prefetch => [ |
593 | "( |
70e0e783 |
594 | SELECT `me`.`name`, `books`.`id`, `books`.`source`, `books`.`owner`, `books`.`title`, `books`.`price (#)`, `books`.`short desc` |
d1f4d28d |
595 | FROM ( |
596 | SELECT `me`.`name`, `me`.`id` |
597 | FROM ( |
598 | SELECT `me`.`name`, `me`.`id` |
599 | FROM `owners` `me` |
600 | ORDER BY `me`.`id` |
601 | FETCH FIRST 4 ROWS ONLY |
602 | ) `me` |
603 | ORDER BY `me`.`id` DESC |
604 | FETCH FIRST 3 ROWS ONLY |
605 | ) `me` |
606 | LEFT JOIN `books` `books` |
607 | ON `books`.`owner` = `me`.`id` |
608 | )", |
609 | [], |
610 | ], |
611 | }, |
612 | |
613 | Top => { |
614 | limit => [ |
615 | "( |
616 | SELECT TOP 4 `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
617 | FROM `books` `me` |
618 | JOIN `owners` `owner` |
619 | ON `owner`.`id` = `me`.`owner` |
620 | WHERE $where_string |
621 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
622 | HAVING ? |
623 | )", |
624 | [ |
625 | @select_bind, |
626 | @where_bind, |
627 | @group_bind, |
628 | @having_bind, |
629 | ], |
630 | ], |
631 | limit_offset => [ |
632 | "( |
633 | SELECT TOP 4 `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
634 | FROM ( |
635 | SELECT TOP 7 `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz` |
636 | FROM `books` `me` |
637 | JOIN `owners` `owner` |
638 | ON `owner`.`id` = `me`.`owner` |
639 | WHERE $where_string |
640 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
641 | HAVING ? |
642 | ORDER BY `me`.`id` |
643 | ) `me` |
644 | ORDER BY `me`.`id` DESC |
645 | )", |
646 | [ |
647 | @select_bind, |
648 | @where_bind, |
649 | @group_bind, |
650 | @having_bind, |
651 | ], |
652 | ], |
653 | ordered_limit => [ |
654 | "( |
655 | SELECT TOP 4 `me`.`id`, `owner`.`id`, `owner`.`name`, ? * ?, ? |
656 | FROM `books` `me` |
657 | JOIN `owners` `owner` |
658 | ON `owner`.`id` = `me`.`owner` |
659 | WHERE $where_string |
660 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
661 | HAVING ? |
70e0e783 |
662 | ORDER BY ? / ?, ?, `me`.`short desc` |
d1f4d28d |
663 | )", |
664 | [ |
665 | @select_bind, |
666 | @where_bind, |
667 | @group_bind, |
668 | @having_bind, |
669 | @order_bind, |
670 | ], |
671 | ], |
672 | ordered_limit_offset => [ |
673 | "( |
674 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
675 | FROM ( |
70e0e783 |
676 | SELECT TOP 4 `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz`, `ORDER__BY__001`, `ORDER__BY__002`, `ORDER__BY__003` |
d1f4d28d |
677 | FROM ( |
70e0e783 |
678 | SELECT TOP 7 `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz`, ? / ? AS `ORDER__BY__001`, ? AS `ORDER__BY__002`, `me`.`short desc` AS `ORDER__BY__003` |
d1f4d28d |
679 | FROM `books` `me` |
680 | JOIN `owners` `owner` |
681 | ON `owner`.`id` = `me`.`owner` |
682 | WHERE $where_string |
683 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
684 | HAVING ? |
70e0e783 |
685 | ORDER BY ? / ?, ?, `me`.`short desc` |
d1f4d28d |
686 | ) `me` |
70e0e783 |
687 | ORDER BY `ORDER__BY__001` DESC, `ORDER__BY__002` DESC, `ORDER__BY__003` DESC |
d1f4d28d |
688 | ) `me` |
70e0e783 |
689 | ORDER BY `ORDER__BY__001`, `ORDER__BY__002`, `ORDER__BY__003` |
d1f4d28d |
690 | )", |
691 | [ |
692 | @select_bind, |
693 | @order_bind, |
694 | @where_bind, |
695 | @group_bind, |
696 | @having_bind, |
697 | @{ dclone \@order_bind }, # without this is_deeply throws a fit |
698 | ], |
699 | ], |
700 | limit_offset_prefetch => [ |
701 | "( |
70e0e783 |
702 | SELECT `me`.`name`, `books`.`id`, `books`.`source`, `books`.`owner`, `books`.`title`, `books`.`price (#)`, `books`.`short desc` |
d1f4d28d |
703 | FROM ( |
704 | SELECT TOP 3 `me`.`name`, `me`.`id` |
705 | FROM ( |
706 | SELECT TOP 4 `me`.`name`, `me`.`id` |
707 | FROM `owners` `me` |
708 | ORDER BY `me`.`id` |
709 | ) `me` |
710 | ORDER BY `me`.`id` DESC |
711 | ) `me` |
712 | LEFT JOIN `books` `books` |
713 | ON `books`.`owner` = `me`.`id` |
714 | )", |
715 | [], |
716 | ], |
717 | }, |
718 | |
719 | GenericSubQ => { |
720 | ordered_limit => [ |
721 | "( |
722 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
723 | FROM ( |
f32ef973 |
724 | SELECT `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz`, `me`.`price (#)` |
d1f4d28d |
725 | FROM `books` `me` |
726 | JOIN `owners` `owner` |
727 | ON `owner`.`id` = `me`.`owner` |
728 | WHERE $where_string |
729 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
730 | HAVING ? |
731 | ) `me` |
732 | WHERE ( |
733 | SELECT COUNT( * ) |
734 | FROM `books` `rownum__emulation` |
735 | WHERE |
f32ef973 |
736 | ( `me`.`price (#)` IS NULL AND `rownum__emulation`.`price (#)` IS NOT NULL ) |
d1f4d28d |
737 | OR |
738 | ( |
f32ef973 |
739 | `rownum__emulation`.`price (#)` > `me`.`price (#)` |
d1f4d28d |
740 | AND |
f32ef973 |
741 | `me`.`price (#)` IS NOT NULL |
d1f4d28d |
742 | AND |
f32ef973 |
743 | `rownum__emulation`.`price (#)` IS NOT NULL |
d1f4d28d |
744 | ) |
745 | OR |
746 | ( |
747 | ( |
f32ef973 |
748 | `me`.`price (#)` = `rownum__emulation`.`price (#)` |
d1f4d28d |
749 | OR |
f32ef973 |
750 | ( `me`.`price (#)` IS NULL AND `rownum__emulation`.`price (#)` IS NULL ) |
d1f4d28d |
751 | ) |
752 | AND |
753 | `rownum__emulation`.`id` < `me`.`id` |
754 | ) |
755 | ) < ? |
f32ef973 |
756 | ORDER BY `me`.`price (#)` DESC, `me`.`id` ASC |
d1f4d28d |
757 | )", |
758 | [ |
759 | @select_bind, |
760 | @where_bind, |
761 | @group_bind, |
762 | @having_bind, |
763 | [ { sqlt_datatype => 'integer' } => 4 ], |
764 | ], |
765 | ], |
766 | ordered_limit_offset => [ |
767 | "( |
768 | SELECT `me`.`id`, `owner__id`, `owner__name`, `bar`, `baz` |
769 | FROM ( |
f32ef973 |
770 | SELECT `me`.`id`, `owner`.`id` AS `owner__id`, `owner`.`name` AS `owner__name`, ? * ? AS `bar`, ? AS `baz`, `me`.`price (#)` |
d1f4d28d |
771 | FROM `books` `me` |
772 | JOIN `owners` `owner` |
773 | ON `owner`.`id` = `me`.`owner` |
774 | WHERE $where_string |
775 | GROUP BY (`me`.`id` / ?), `owner`.`id` |
776 | HAVING ? |
777 | ) `me` |
778 | WHERE ( |
779 | SELECT COUNT( * ) |
780 | FROM `books` `rownum__emulation` |
781 | WHERE |
f32ef973 |
782 | ( `me`.`price (#)` IS NULL AND `rownum__emulation`.`price (#)` IS NOT NULL ) |
d1f4d28d |
783 | OR |
784 | ( |
f32ef973 |
785 | `rownum__emulation`.`price (#)` > `me`.`price (#)` |
d1f4d28d |
786 | AND |
f32ef973 |
787 | `me`.`price (#)` IS NOT NULL |
d1f4d28d |
788 | AND |
f32ef973 |
789 | `rownum__emulation`.`price (#)` IS NOT NULL |
d1f4d28d |
790 | ) |
791 | OR |
792 | ( |
793 | ( |
f32ef973 |
794 | `me`.`price (#)` = `rownum__emulation`.`price (#)` |
d1f4d28d |
795 | OR |
f32ef973 |
796 | ( `me`.`price (#)` IS NULL AND `rownum__emulation`.`price (#)` IS NULL ) |
d1f4d28d |
797 | ) |
798 | AND |
799 | `rownum__emulation`.`id` < `me`.`id` |
800 | ) |
801 | ) BETWEEN ? AND ? |
f32ef973 |
802 | ORDER BY `me`.`price (#)` DESC, `me`.`id` ASC |
d1f4d28d |
803 | )", |
804 | [ |
805 | @select_bind, |
806 | @where_bind, |
807 | @group_bind, |
808 | @having_bind, |
809 | [ { sqlt_datatype => 'integer' } => 3 ], |
810 | [ { sqlt_datatype => 'integer' } => 6 ], |
811 | ], |
812 | ], |
813 | limit_offset_prefetch => [ |
814 | "( |
70e0e783 |
815 | SELECT `me`.`name`, `books`.`id`, `books`.`source`, `books`.`owner`, `books`.`title`, `books`.`price (#)`, `books`.`short desc` |
d1f4d28d |
816 | FROM ( |
817 | SELECT `me`.`name`, `me`.`id` |
818 | FROM ( |
819 | SELECT `me`.`name`, `me`.`id` |
820 | FROM `owners` `me` |
821 | ) `me` |
822 | WHERE |
823 | ( |
824 | SELECT COUNT(*) |
825 | FROM `owners` `rownum__emulation` |
826 | WHERE ( |
827 | `rownum__emulation`.`name` < `me`.`name` |
828 | OR |
829 | ( |
830 | `me`.`name` = `rownum__emulation`.`name` |
831 | AND |
832 | `rownum__emulation`.`id` > `me`.`id` |
833 | ) |
834 | ) |
835 | ) BETWEEN ? AND ? |
836 | ORDER BY `me`.`name` ASC, `me`.`id` DESC |
837 | ) `me` |
838 | LEFT JOIN `books` `books` |
839 | ON `books`.`owner` = `me`.`id` |
840 | ORDER BY `me`.`name` ASC, `me`.`id` DESC |
841 | )", |
842 | [ |
843 | [ { sqlt_datatype => 'integer' } => 1 ], |
844 | [ { sqlt_datatype => 'integer' } => 3 ], |
845 | ], |
846 | ], |
847 | } |
848 | }; |
849 | |
850 | for my $limtype (sort keys %$tests) { |
851 | |
852 | Test::Builder->new->is_passing or exit; |
853 | |
854 | delete $schema->storage->_sql_maker->{_cached_syntax}; |
855 | $schema->storage->_sql_maker->limit_dialect ($limtype); |
856 | |
857 | # not deploying, so can't run |
858 | my $can_run = 0 && ($limtype eq $native_limit_dialect or $limtype eq 'GenericSubQ'); |
859 | |
860 | # chained search is necessary to exercise the recursive {where} parser |
861 | my $rs = $schema->resultset('BooksInLibrary')->search( |
862 | { 'me.title' => { '=' => \[ '?', 'kama sutra' ] } } |
863 | )->search( |
864 | { source => { '!=', \[ '?', [ {} => 'Study' ] ] } }, |
865 | { |
866 | columns => [ { identifier => 'me.id' }, 'owner.id', 'owner.name' ], # people actually do that. BLEH!!! :) |
867 | join => 'owner', # single-rel manual prefetch |
868 | rows => 4, |
869 | '+columns' => { bar => \['? * ?', [ \ 'numeric' => 11 ], 12 ], baz => \[ '?', [ 'me.id' => 13 ] ] }, |
870 | group_by => \[ '(`me`.`id` / ?), `owner`.`id`', 21 ], |
871 | having => \[ '?', 31 ], |
872 | } |
873 | ); |
874 | |
875 | # |
876 | # not all tests run on all dialects (somewhere impossible, somewhere makes no sense) |
877 | # |
878 | |
879 | # only limit, no offset, no order |
880 | if ($tests->{$limtype}{limit}) { |
881 | lives_ok { |
882 | is_same_sql_bind( |
883 | $rs->as_query, |
884 | @{$tests->{$limtype}{limit}}, |
885 | "$limtype: Unordered limit with select/group/having", |
886 | ); |
887 | |
888 | $rs->all if $can_run; |
889 | } "Grouped limit under $limtype"; |
890 | } |
891 | |
892 | # limit + offset, no order |
893 | if ($tests->{$limtype}{limit_offset}) { |
894 | |
895 | lives_ok { |
896 | my $subrs = $rs->search({}, { offset => 3 }); |
897 | |
898 | is_same_sql_bind( |
899 | $subrs->as_query, |
900 | @{$tests->{$limtype}{limit_offset}}, |
901 | "$limtype: Unordered limit+offset with select/group/having", |
902 | ); |
903 | |
904 | $subrs->all if $can_run; |
905 | } "Grouped limit+offset runs under $limtype"; |
906 | } |
907 | |
908 | # order + limit, no offset |
909 | $rs = $rs->search(undef, { |
910 | order_by => ( $limtype =~ /GenericSubQ/ |
70e0e783 |
911 | ? [ { -desc => 'price (#)' }, 'me.id', \[ '`owner`.`name` + ?', 'bah' ], \'`me`.`short desc`' ] # needs a same-table stable order to be happy |
912 | : [ \['? / ?', [ \ 'int' => 1 ], [ name => 2 ]], \[ '?', 3 ], \'`me`.`short desc`' ] |
d1f4d28d |
913 | ), |
914 | }); |
915 | |
916 | if ($tests->{$limtype}{ordered_limit}) { |
917 | |
918 | lives_ok { |
919 | is_same_sql_bind( |
920 | $rs->as_query, |
921 | @{$tests->{$limtype}{ordered_limit}}, |
922 | "$limtype: Ordered limit with select/group/having", |
923 | ); |
924 | |
925 | $rs->all if $can_run; |
926 | } "Grouped ordered limit runs under $limtype" |
927 | } |
928 | |
929 | # order + limit + offset |
930 | if ($tests->{$limtype}{ordered_limit_offset}) { |
931 | lives_ok { |
932 | my $subrs = $rs->search({}, { offset => 3 }); |
933 | |
934 | is_same_sql_bind( |
935 | $subrs->as_query, |
936 | @{$tests->{$limtype}{ordered_limit_offset}}, |
937 | "$limtype: Ordered limit+offset with select/group/having", |
938 | ); |
939 | |
940 | $subrs->all if $can_run; |
941 | } "Grouped ordered limit+offset runs under $limtype"; |
942 | } |
943 | |
944 | # complex prefetch on partial-fetch root with limit |
945 | my $pref_rs = $schema->resultset('Owners')->search({}, { |
946 | rows => 3, |
947 | offset => 1, |
948 | columns => 'name', # only the owner name, still prefetch all the books |
949 | prefetch => 'books', |
950 | ($limtype !~ /GenericSubQ/ ? () : ( |
951 | # needs a same-table stable order to be happy |
952 | order_by => [ { -asc => 'me.name' }, \ '`me`.`id` DESC' ] |
953 | )), |
954 | }); |
955 | |
956 | lives_ok { |
957 | is_same_sql_bind ( |
958 | $pref_rs->as_query, |
959 | @{$tests->{$limtype}{limit_offset_prefetch}}, |
960 | "$limtype: Prefetch with limit+offset", |
961 | ) if $tests->{$limtype}{limit_offset_prefetch}; |
962 | |
963 | is ($pref_rs->all, 1, 'Expected count of objects on limited prefetch') |
964 | if $can_run; |
965 | } "Complex limited prefetch runs under $limtype"; |
966 | } |
967 | |
968 | done_testing; |