Handle NULLS clauses when mangling ordering
[dbsrgits/DBIx-Class.git] / t / sqlmaker / limit_dialects / fetch_first.t
1 BEGIN { do "./t/lib/ANFANG.pm" or die ( $@ || $! ) }
2
3 use strict;
4 use warnings;
5
6 use Test::More;
7
8 use DBICTest ':DiffSQL';
9
10 my $schema = DBICTest->init_schema;
11
12 # based on toplimit.t
13 delete $schema->storage->_sql_maker->{_cached_syntax};
14 $schema->storage->_sql_maker->limit_dialect ('FetchFirst');
15
16 my $books_45_and_owners = $schema->resultset ('BooksInLibrary')->search ({}, {
17   prefetch => 'owner', rows => 2, offset => 3,
18   columns => [ grep { $_ ne 'title' } $schema->source('BooksInLibrary')->columns ],
19 });
20
21 for my $null_order (
22   undef,
23   '',
24   {},
25   [],
26   [{}],
27 ) {
28   my $rs = $books_45_and_owners->search ({}, {order_by => $null_order });
29   is_same_sql_bind(
30       $rs->as_query,
31       '(SELECT me.id, me.source, me.owner, me.price, owner__id, owner__name
32           FROM (
33             SELECT me.id, me.source, me.owner, me.price, owner.id AS owner__id, owner.name AS owner__name
34               FROM books me
35               JOIN owners owner ON owner.id = me.owner
36             WHERE ( source = ? )
37             ORDER BY me.id
38             FETCH FIRST 5 ROWS ONLY
39           ) me
40         ORDER BY me.id DESC
41         FETCH FIRST 2 ROWS ONLY
42        )',
43     [ [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' }
44         => 'Library' ] ],
45   );
46 }
47
48
49 for my $ord_set (
50   {
51     order_by => \'title DESC',
52     order_inner => 'title DESC',
53     order_outer => 'ORDER__BY__001 ASC',
54     order_req => 'ORDER__BY__001 DESC',
55     exselect_outer => 'ORDER__BY__001',
56     exselect_inner => 'title AS ORDER__BY__001',
57   },
58   {
59     order_by => { -asc => 'title'  },
60     order_inner => 'title ASC',
61     order_outer => 'ORDER__BY__001 DESC',
62     order_req => 'ORDER__BY__001 ASC',
63     exselect_outer => 'ORDER__BY__001',
64     exselect_inner => 'title AS ORDER__BY__001',
65   },
66   {
67     order_by => { -desc => 'title' },
68     order_inner => 'title DESC',
69     order_outer => 'ORDER__BY__001 ASC',
70     order_req => 'ORDER__BY__001 DESC',
71     exselect_outer => 'ORDER__BY__001',
72     exselect_inner => 'title AS ORDER__BY__001',
73   },
74   {
75     order_by => 'title',
76     order_inner => 'title',
77     order_outer => 'ORDER__BY__001 DESC',
78     order_req => 'ORDER__BY__001',
79     exselect_outer => 'ORDER__BY__001',
80     exselect_inner => 'title AS ORDER__BY__001',
81   },
82   {
83     order_by => [ qw{ title me.owner}   ],
84     order_inner => 'title, me.owner',
85     order_outer => 'ORDER__BY__001 DESC, me.owner DESC',
86     order_req => 'ORDER__BY__001, me.owner',
87     exselect_outer => 'ORDER__BY__001',
88     exselect_inner => 'title AS ORDER__BY__001',
89   },
90   {
91     order_by => ['title', { -desc => 'bar' } ],
92     order_inner => 'title, bar DESC',
93     order_outer => 'ORDER__BY__001 DESC, ORDER__BY__002 ASC',
94     order_req => 'ORDER__BY__001, ORDER__BY__002 DESC',
95     exselect_outer => 'ORDER__BY__001, ORDER__BY__002',
96     exselect_inner => 'title AS ORDER__BY__001, bar AS ORDER__BY__002',
97   },
98   {
99     order_by => { -asc => [qw{ title bar }] },
100     order_inner => 'title ASC, bar ASC',
101     order_outer => 'ORDER__BY__001 DESC, ORDER__BY__002 DESC',
102     order_req => 'ORDER__BY__001 ASC, ORDER__BY__002 ASC',
103     exselect_outer => 'ORDER__BY__001, ORDER__BY__002',
104     exselect_inner => 'title AS ORDER__BY__001, bar AS ORDER__BY__002',
105   },
106   {
107     order_by => [
108       'title',
109       { -desc => [qw{bar}] },
110       { -asc  => [qw{me.owner sensors}]},
111     ],
112     order_inner => 'title, bar DESC, me.owner ASC, sensors ASC',
113     order_outer => 'ORDER__BY__001 DESC, ORDER__BY__002 ASC, me.owner DESC, ORDER__BY__003 DESC',
114     order_req => 'ORDER__BY__001, ORDER__BY__002 DESC, me.owner ASC, ORDER__BY__003 ASC',
115     exselect_outer => 'ORDER__BY__001, ORDER__BY__002, ORDER__BY__003',
116     exselect_inner => 'title AS ORDER__BY__001, bar AS ORDER__BY__002, sensors AS ORDER__BY__003',
117   },
118
119   {
120     order_by => [
121       'name',
122     ],
123     order_inner => 'name',
124     order_outer => 'name DESC',
125     order_req => 'name',
126   },
127   {
128     order_by => [
129         { -asc => 'title', -nulls => 'first' },
130         { -desc => 'bar', -nulls => 'last' },
131     ],
132     order_inner => 'title ASC NULLS FIRST, bar DESC NULLS LAST',
133     order_outer => 'ORDER__BY__001 DESC NULLS LAST, ORDER__BY__002 ASC NULLS FIRST',
134     order_req => 'ORDER__BY__001 ASC NULLS FIRST, ORDER__BY__002 DESC NULLS LAST',
135     exselect_outer => 'ORDER__BY__001, ORDER__BY__002',
136     exselect_inner => 'title AS ORDER__BY__001, bar AS ORDER__BY__002',
137   },
138 ) {
139   my $o_sel = $ord_set->{exselect_outer}
140     ? ', ' . $ord_set->{exselect_outer}
141     : ''
142   ;
143   my $i_sel = $ord_set->{exselect_inner}
144     ? ', ' . $ord_set->{exselect_inner}
145     : ''
146   ;
147
148   my $rs = $books_45_and_owners->search ({}, {order_by => $ord_set->{order_by}});
149
150   # query actually works
151   ok( defined $rs->count, 'Query actually works' );
152
153   is_same_sql_bind(
154     $rs->as_query,
155     "(SELECT me.id, me.source, me.owner, me.price, owner__id, owner__name
156         FROM (
157           SELECT me.id, me.source, me.owner, me.price, owner__id, owner__name$o_sel
158             FROM (
159               SELECT me.id, me.source, me.owner, me.price, owner.id AS owner__id, owner.name AS owner__name$i_sel
160                 FROM books me
161                 JOIN owners owner ON owner.id = me.owner
162               WHERE ( source = ? )
163               ORDER BY $ord_set->{order_inner}
164               FETCH FIRST 5 ROWS ONLY
165             ) me
166           ORDER BY $ord_set->{order_outer}
167           FETCH FIRST 2 ROWS ONLY
168         ) me
169       ORDER BY $ord_set->{order_req}
170     )",
171     [ [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' }
172         => 'Library' ] ],
173   );
174
175 }
176
177 # with groupby
178 is_same_sql_bind (
179   $books_45_and_owners->search ({}, { group_by => 'title', order_by => 'title' })->as_query,
180   '(SELECT me.id, me.source, me.owner, me.price, owner.id, owner.name
181       FROM (
182         SELECT me.id, me.source, me.owner, me.price, me.title
183           FROM (
184             SELECT me.id, me.source, me.owner, me.price, me.title
185               FROM (
186                 SELECT me.id, me.source, me.owner, me.price, me.title
187                   FROM books me
188                   JOIN owners owner ON owner.id = me.owner
189                 WHERE ( source = ? )
190                 GROUP BY title
191                 ORDER BY title
192                 FETCH FIRST 5 ROWS ONLY
193               ) me
194             ORDER BY title DESC
195             FETCH FIRST 2 ROWS ONLY
196           ) me
197         ORDER BY title
198       ) me
199       JOIN owners owner ON owner.id = me.owner
200     WHERE ( source = ? )
201     ORDER BY title
202   )',
203   [ map { [
204     { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' }
205       => 'Library' ]
206   } (1,2) ],
207 );
208
209 # test deprecated column mixing over join boundaries
210 my $rs_selectas_top = $schema->resultset ('BooksInLibrary')->search ({}, {
211   '+select' => ['owner.name'],
212   '+as' => ['owner_name'],
213   join => 'owner',
214   rows => 1
215 });
216
217 is_same_sql_bind( $rs_selectas_top->search({})->as_query,
218                   '(SELECT
219                       me.id, me.source, me.owner, me.title, me.price, owner.name
220                     FROM books me
221                     JOIN owners owner ON owner.id = me.owner
222                     WHERE ( source = ? )
223                     FETCH FIRST 1 ROWS ONLY
224                    )',
225                   [ [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' }
226                     => 'Library' ] ],
227                 );
228
229 {
230   my $rs = $schema->resultset('Artist')->search({}, {
231     columns => 'artistid',
232     offset => 1,
233     order_by => 'artistid',
234   });
235   local $rs->result_source->{name} = "weird \n newline/multi \t \t space containing \n table";
236
237   like (
238     ${$rs->as_query}->[0],
239     qr| weird \s \n \s newline/multi \s \t \s \t \s space \s containing \s \n \s table|x,
240     'Newlines/spaces preserved in final sql',
241   );
242 }
243
244 done_testing;