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