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