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