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