Make DateTime objects work for all ops
[dbsrgits/DBIx-Class.git] / t / sqlmaker / op_dt.t
1 use strict;
2 use warnings;
3
4 use Test::More;
5 use Test::Exception;
6
7 use lib qw(t/lib);
8 use DBICTest::RunMode;
9 use DBIC::SqlMakerTest;
10 use DateTime;
11 use DBIx::Class::SQLMaker::MSSQL;
12 use Try::Tiny;
13 use Data::Dumper::Concise;
14
15 use DBICTest;
16
17 sub unknown_col { +{ dbic_colname => '' } }
18
19 my %dbs_to_test = (
20    sqlite   => 1,
21    mssql    => 0,
22    postgres => 0,
23    oracle   => 0,
24 );
25
26 my %schema = (
27    sqlite => DBICTest->init_schema( no_populate => 1 ),
28    mssql => do {
29       my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_MSSQL_ODBC_${_}" } qw/DSN USER PASS/};
30       if ($dsn && $user) {
31          my $s = DBICTest::Schema->connect($dsn, $user, $pass);
32          try { $s->storage->ensure_connected };
33
34          $s->storage->dbh_do (sub {
35              my ($storage, $dbh) = @_;
36              eval { $dbh->do("DROP TABLE event") };
37              $dbh->do(<<'SQL');
38 CREATE TABLE event (
39    id INT IDENTITY NOT NULL,
40    starts_at DATE NOT NULL,
41    created_on DATETIME NOT NULL,
42    varchar_date VARCHAR(20),
43    varchar_datetime VARCHAR(20),
44    skip_inflation DATETIME,
45    ts_without_tz DATETIME,
46
47    primary key(id)
48 )
49 SQL
50         $dbs_to_test{mssql} = 1;
51 });
52 $s;
53       } else {
54          DBICTest->init_schema( no_deploy=> 1, storage_type => '::DBI::MSSQL' )
55       }
56    },
57    mysql => do {
58       my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_MYSQL_${_}" } qw/DSN USER PASS/};
59       if ($dsn && $user) {
60          my $s = DBICTest::Schema->connect($dsn, $user, $pass);
61          try { $s->storage->ensure_connected };
62
63          $s->storage->dbh_do (sub {
64              my ($storage, $dbh) = @_;
65              eval { $dbh->do("DROP TABLE event") };
66              $dbh->do(<<'SQL');
67 CREATE TABLE event (
68    id INT AUTO_INCREMENT NOT NULL,
69    starts_at DATE NOT NULL,
70    created_on DATETIME NOT NULL,
71    varchar_date VARCHAR(20),
72    varchar_datetime VARCHAR(20),
73    skip_inflation DATETIME,
74    ts_without_tz DATETIME,
75
76    primary key(id)
77 )
78 SQL
79         $dbs_to_test{mysql} = 1;
80 });
81 $s;
82       } else {
83          DBICTest->init_schema( no_deploy=> 1, storage_type => '::DBI::mysql' )
84       }
85    },
86    ## copypasta'd for great justice
87    postgres =>  do {
88       my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_PG_${_}" } qw/DSN USER PASS/};
89       if ($dsn && $user) {
90          my $s = DBICTest::Schema->connect($dsn, $user, $pass);
91          try { $s->storage->ensure_connected };
92
93          $s->storage->dbh_do (sub {
94              my ($storage, $dbh) = @_;
95              eval { $dbh->do("DROP TABLE event") };
96              $dbh->do(<<'SQL');
97 CREATE TABLE event (
98    id SERIAL NOT NULL PRIMARY KEY,
99    starts_at DATE NOT NULL,
100    created_on TIMESTAMP NOT NULL,
101    varchar_date VARCHAR(20),
102    varchar_datetime VARCHAR(20),
103    skip_inflation TIMESTAMP,
104    ts_without_tz TIMESTAMP WITHOUT TIME ZONE
105 )
106 SQL
107         $dbs_to_test{postgres} = 1;
108 });
109 $s;
110       } else {
111          DBICTest->init_schema( no_deploy=> 1, storage_type => '::DBI::Pg' )
112       }
113    },
114    oracle =>  do {
115       my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_ORA_${_}" } qw/DSN USER PASS/};
116       if ($dsn && $user) {
117          my $s = DBICTest::Schema->connect($dsn, $user, $pass, { on_connect_call => 'datetime_setup' });
118          try { $s->storage->ensure_connected };
119
120          $s->storage->dbh_do (sub {
121              my ($storage, $dbh) = @_;
122              eval { $dbh->do("DROP TRIGGER trq_event_id") };
123              eval { $dbh->do("DROP SEQUENCE sq_event_id") };
124              eval { $dbh->do("DROP TABLE event") };
125              $dbh->do('CREATE SEQUENCE sq_event_id');
126              $dbh->do(<<'SQL');
127 CREATE TABLE event (
128    id NUMBER NOT NULL,
129    starts_at DATE NOT NULL,
130    created_on TIMESTAMP NOT NULL,
131    varchar_date VARCHAR(20),
132    varchar_datetime VARCHAR(20),
133    skip_inflation TIMESTAMP,
134    ts_without_tz TIMESTAMP,
135    CONSTRAINT PK_EVENT PRIMARY KEY (id)
136 )
137 SQL
138              $dbh->do(<<'SQL');
139 CREATE TRIGGER trg_event_id
140 BEFORE INSERT ON event
141 FOR EACH ROW WHEN (
142   new.id IS NULL OR new.id = 0
143 )
144 BEGIN
145   SELECT sq_event_id.nextval
146   INTO :new.id
147   FROM dual;
148   END;
149 SQL
150         $dbs_to_test{oracle} = 1;
151 });
152 $s;
153       } else {
154          DBICTest->init_schema( no_deploy=> 1, storage_type => '::DBI::Oracle::Generic', on_connect_call => 'datetime_setup' )
155       }
156    },
157 );
158
159 my %rs = map { $_ => $schema{$_}->resultset('Event') } keys %schema;
160
161 for (grep { $schema{$_}->storage->connected } keys %rs) {
162    $rs{$_}->populate([
163      [qw(starts_at created_on skip_inflation)],
164      ['2010-12-12', '2010-12-14 12:12:12', '2019-12-12 12:12:12'],
165      ['2010-12-12', '2011-12-14 12:12:12', '2011-12-12 12:12:12'],
166    ])
167 }
168
169 my $date = DateTime->new(
170    year => 2010,
171    month => 12,
172    day   => 14,
173    hour  => 12,
174    minute => 12,
175    second => 12,
176 );
177
178 sub hri_thing {
179    return {
180       starts_at => $_[0],
181       created_on => $_[1],
182       skip_inflation => $_[2]
183    }
184 }
185
186 my $date2 = $date->clone->set_day(16);
187
188 my $date3 = DateTime->new(
189    year => 2011,
190    month => 12,
191    day   => 14,
192    hour  => 12,
193    minute => 12,
194    second => 12,
195 );
196
197 my $date4 = DateTime->new(
198    year => 2010,
199    month => 12,
200    day   => 12,
201 );
202
203 ## test format:
204 ##   search => { dbic_search_code/params }
205 ##   rdbms_name => literal_sql
206 my @tests = (
207
208   {
209     msg => '-dt_now works',
210     search => { 'me.created_on' => { -dt => $date } },
211     select_sql => 'me.starts_at, me.created_on, me.skip_inflation',
212     where  => 'me.created_on = ?',
213     bind   => [[{ dbic_colname => 'me.created_on', sqlt_datatype => 'timestamp' }, '2010-12-14 12:12:12' ]],
214     hri    => [hri_thing('2010-12-12', '2010-12-14 12:12:12', '2019-12-12 12:12:12')],
215     sqlite => { },
216     mssql => {
217       bind   => [[{ dbic_colname => 'me.created_on', sqlt_datatype => 'timestamp' }, '2010-12-14 12:12:12.000' ]],
218       hri    => [hri_thing('2010-12-12', '2010-12-14 12:12:12.000', '2019-12-12 12:12:12.000')],
219     },
220     mysql => { },
221     postgres => { },
222     oracle => {
223       hri    => [hri_thing('2010-12-12 00:00:00', '2010-12-14 12:12:12.000000', '2019-12-12 12:12:12.000000')],
224     },
225   },
226
227   {
228     msg    => '-dt_year works',
229     search => { 'me.id' => 1 },
230     select => [ [ -dt_year => { -ident => 'me.created_on' } ] ],
231     as     => [ 'year' ],
232     where  => "me.id = ?",
233     bind   => [[{ dbic_colname => 'me.id', sqlt_datatype => 'integer' } => 1 ]],
234     hri    => [{ year => 2010 }],
235     mssql => {
236       select => "DATEPART(year, me.created_on)",
237     },
238     mysql => {
239       select => "EXTRACT(YEAR FROM me.created_on)",
240     },
241     sqlite => {
242       select => "STRFTIME('%Y', me.created_on)",
243     },
244     postgres => {
245       select => "date_part('year', me.created_on)",
246     },
247     oracle => {
248       select => "EXTRACT(year FROM me.created_on)",
249     },
250   },
251
252   {
253     msg    => '-dt_year works with DateTime obj',
254     search => { 'me.id' => 1 },
255     select => [ [ -dt_year => $date ] ],
256     as     => [ 'year' ],
257     where  => "me.id = ?",
258     hri    => [{ year => 2010 }],
259     bind   => [[{ sqlt_datatype => 'timestamp' } => '2010-12-14 12:12:12' ], [{ dbic_colname => 'me.id', sqlt_datatype => 'integer' } => 1 ]],
260     mssql => {
261       select => "DATEPART(year, ?)",
262       bind   => [[{ sqlt_datatype => 'timestamp' } => '2010-12-14 12:12:12.000' ], [{ dbic_colname => 'me.id', sqlt_datatype => 'integer' } => 1 ]],
263     },
264     mysql => {
265       select => "EXTRACT(YEAR FROM ?)",
266     },
267     sqlite => {
268       select => "STRFTIME('%Y', ?)",
269     },
270     postgres => {
271       select => "date_part('year', ?)",
272     },
273     oracle => {
274       select => "EXTRACT(year FROM ?)",
275     },
276   },
277
278   {
279     msg    => '-dt_get (year, month) works',
280     search => { 'me.id' => 1 },
281     select => [ [ -dt_get => [[qw(year month)], { -ident => 'me.created_on' }] ] ],
282     as     => [ qw(year month) ],
283     where  => "me.id = ?",
284     bind   => [[{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
285     hri    => [{ year => 2010, month => 12 }],
286     mssql => {
287       select => "DATEPART(year, me.created_on), DATEPART(month, me.created_on)",
288     },
289     mysql => {
290       select => "EXTRACT(YEAR FROM me.created_on), EXTRACT(MONTH FROM me.created_on)",
291     },
292     sqlite => {
293       select => "STRFTIME('%Y', me.created_on), STRFTIME('%m', me.created_on)",
294     },
295     postgres => {
296       select => "date_part('year', me.created_on), date_part('month', me.created_on)",
297     },
298     oracle => {
299       select => "EXTRACT(year FROM me.created_on), EXTRACT(month FROM me.created_on)",
300     },
301   },
302
303 {
304     msg    => '-dt_get (year, month) works with DateTime obj',
305     search => { 'me.id' => 1 },
306     select => [ [ -dt_get => [[qw(year month)], $date ] ] ],
307     as     => [ qw(year month) ],
308     where  => "me.id = ?",
309     bind   => [
310       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
311       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
312       [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]
313     ],
314     hri    => [{ year => 2010, month => 12 }],
315     mssql => {
316       select => "DATEPART(year, ?), DATEPART(month, ?)",
317        bind   => [
318          [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
319          [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
320          [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]
321        ],
322     },
323     mysql => {
324       select => "EXTRACT(YEAR FROM ?), EXTRACT(MONTH FROM ?)",
325     },
326     sqlite => {
327       select => "STRFTIME('%Y', ?), STRFTIME('%m', ?)",
328     },
329     postgres => {
330       select => "date_part('year', ?), date_part('month', ?)",
331     },
332     oracle => {
333       select => "EXTRACT(year FROM ?), EXTRACT(month FROM ?)",
334     },
335   },
336
337   {
338     msg    => '-dt_month works',
339     search => { 'me.id' => 1 },
340     select => [ [ -dt_month => { -ident => 'me.created_on' } ] ],
341     as     => [ 'month' ],
342     where  => "me.id = ?",
343     bind   => [[{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
344     hri    => [{ month => 12 }],
345     sqlite => {
346       select   => "STRFTIME('%m', me.created_on)",
347     },
348     mssql => {
349       select => "DATEPART(month, me.created_on)",
350     },
351     mysql => {
352       select => "EXTRACT(MONTH FROM me.created_on)",
353     },
354     postgres => {
355       select => "date_part('month', me.created_on)",
356     },
357     oracle => {
358       select => "EXTRACT(month FROM me.created_on)",
359     },
360   },
361
362   {
363     msg    => '-dt_month works with DateTime obj',
364     search => { 'me.id' => 1 },
365     select => [ [ -dt_month => $date ] ],
366     as     => [ 'month' ],
367     where  => "me.id = ?",
368     bind   => [[{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
369     hri    => [{ month => 12 }],
370     sqlite => {
371       select   => "STRFTIME('%m', ?)",
372     },
373     mssql => {
374       bind   => [[{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
375       select => "DATEPART(month, ?)",
376     },
377     mysql => {
378       select => "EXTRACT(MONTH FROM ?)",
379     },
380     postgres => {
381       select => "date_part('month', ?)",
382     },
383     oracle => {
384       select => "EXTRACT(month FROM ?)",
385     },
386   },
387
388   {
389     msg    => '-dt_day works',
390     search => { 'me.id' => 1 },
391     select => [ [ -dt_day => { -ident => 'me.created_on' } ] ],
392     as     => [ 'day' ],
393     where  => "me.id = ?",
394     bind   => [[{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
395     hri    => [{ day => 14 }],
396     sqlite => {
397       select   => "STRFTIME('%d', me.created_on)",
398     },
399     mssql => {
400       select => "DATEPART(day, me.created_on)",
401     },
402     mysql => {
403       select => "EXTRACT(DAY FROM me.created_on)",
404     },
405     postgres => {
406       select => "date_part('day', me.created_on)",
407     },
408     oracle => {
409       select => "EXTRACT(day FROM me.created_on)",
410     },
411   },
412
413   {
414     msg    => '-dt_day works with DateTime obj',
415     search => { 'me.id' => 1 },
416     select => [ [ -dt_day => $date ] ],
417     as     => [ 'day' ],
418     where  => "me.id = ?",
419     bind   => [[{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
420     hri    => [{ day => 14 }],
421     sqlite => {
422       select   => "STRFTIME('%d', ?)",
423     },
424     mssql => {
425       select => "DATEPART(day, ?)",
426       bind   => [[{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
427     },
428     mysql => {
429       select => "EXTRACT(DAY FROM ?)",
430     },
431     postgres => {
432       select => "date_part('day', ?)",
433     },
434     oracle => {
435       select => "EXTRACT(day FROM ?)",
436     },
437   },
438
439   {
440     msg    => '-dt_hour works',
441     search => { 'me.id' => 1 },
442     select => [ [ -dt_hour => { -ident => 'me.created_on' } ] ],
443     as     => [ 'hour' ],
444     where  => "me.id = ?",
445     bind   => [[{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
446     hri    => [{ hour => 12 }],
447     sqlite => {
448       select   => "STRFTIME('%H', me.created_on)",
449     },
450     mssql => {
451       select => "DATEPART(hour, me.created_on)",
452     },
453     mysql => {
454       select => "EXTRACT(HOUR FROM me.created_on)",
455     },
456     postgres => {
457       select => "date_part('hour', me.created_on)",
458     },
459     oracle => {
460       select => "EXTRACT(hour FROM me.created_on)",
461     },
462   },
463
464   {
465     msg    => '-dt_hour works with DateTime obj',
466     search => { 'me.id' => 1 },
467     select => [ [ -dt_hour => $date ] ],
468     as     => [ 'hour' ],
469     where  => "me.id = ?",
470     bind   => [[{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
471     hri    => [{ hour => 12 }],
472     sqlite => {
473       select   => "STRFTIME('%H', ?)",
474     },
475     mssql => {
476       select => "DATEPART(hour, ?)",
477       bind   => [[{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
478     },
479     mysql => {
480       select => "EXTRACT(HOUR FROM ?)",
481     },
482     postgres => {
483       select => "date_part('hour', ?)",
484     },
485     oracle => {
486       select => "EXTRACT(hour FROM ?)",
487     },
488   },
489
490   {
491     msg    => '-dt_minute works',
492     search => { 'me.id' => 1 },
493     select => [ [ -dt_minute => { -ident => 'me.created_on' } ] ],
494     as     => [ 'minute' ],
495     where  => "me.id = ?",
496     bind   => [[{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
497     hri    => [{ minute => 12 }],
498     sqlite => {
499       select   => "STRFTIME('%M', me.created_on)",
500     },
501     mssql => {
502       select => "DATEPART(minute, me.created_on)",
503     },
504     mysql => {
505       select => "EXTRACT(MINUTE FROM me.created_on)",
506     },
507     postgres => {
508       select => "date_part('minute', me.created_on)",
509     },
510     oracle => {
511       select => "EXTRACT(minute FROM me.created_on)",
512     },
513   },
514
515   {
516     msg    => '-dt_minute works with DateTime obj',
517     search => { 'me.id' => 1 },
518     select => [ [ -dt_minute => $date ] ],
519     as     => [ 'minute' ],
520     where  => "me.id = ?",
521     bind   => [[{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
522     hri    => [{ minute => 12 }],
523     sqlite => {
524       select   => "STRFTIME('%M', ?)",
525     },
526     mssql => {
527       select => "DATEPART(minute, ?)",
528       bind   => [[{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
529     },
530     mysql => {
531       select => "EXTRACT(MINUTE FROM ?)",
532     },
533     postgres => {
534       select => "date_part('minute', ?)",
535     },
536     oracle => {
537       select => "EXTRACT(minute FROM ?)",
538     },
539   },
540
541   {
542     msg    => '-dt_second works',
543     search => { 'me.id' => 1 },
544     select => [ [ -dt_second => { -ident => 'me.created_on' } ] ],
545     as     => [ 'second' ],
546     where  => "me.id = ?",
547     bind   => [[{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
548     hri    => [{ second => 12 }],
549     sqlite => {
550       select   => "STRFTIME('%S', me.created_on)",
551     },
552     mssql => {
553       select => "DATEPART(second, me.created_on)",
554     },
555     mysql => {
556       select => "EXTRACT(SECOND FROM me.created_on)",
557     },
558     postgres => {
559       select => "date_part('second', me.created_on)",
560     },
561     oracle => {
562       select => "EXTRACT(second FROM me.created_on)",
563     },
564   },
565
566   {
567     msg    => '-dt_second works with DateTime obj',
568     search => { 'me.id' => 1 },
569     select => [ [ -dt_second => $date ] ],
570     as     => [ 'second' ],
571     where  => "me.id = ?",
572     bind   => [[{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
573     hri    => [{ second => 12 }],
574     sqlite => {
575       select   => "STRFTIME('%S', ?)",
576     },
577     mssql => {
578       select => "DATEPART(second, ?)",
579       bind   => [[{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 1 ]],
580     },
581     mysql => {
582       select => "EXTRACT(SECOND FROM ?)",
583     },
584     postgres => {
585       select => "date_part('second', ?)",
586     },
587     oracle => {
588       select => "EXTRACT(second FROM ?)",
589     },
590   },
591
592   {
593     msg    => '-dt_diff (second) works',
594     search => { 'me.id' => 2 },
595     select => [ [ -dt_diff => [second => { -ident => 'me.created_on' }, \'me.skip_inflation' ] ] ],
596     as     => [ 'sec_diff' ],
597     where  => "me.id = ?",
598     bind   => [[{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
599     hri    => [{ sec_diff => 2*24*60*60 }],
600     sqlite => {
601       select   => "(STRFTIME('%s', me.created_on) - STRFTIME('%s', me.skip_inflation))",
602     },
603     mssql => {
604       select   => "DATEDIFF(second, me.skip_inflation, me.created_on)",
605     },
606     mysql => {
607       select   => "TIMESTAMPDIFF(SECOND, me.skip_inflation, me.created_on)",
608     },
609     postgres => {
610       select   => "date_part('EPOCH', me.created_on) - date_part('EPOCH', me.skip_inflation)",
611     },
612     oracle => {
613       select   => "TRUNC(MONTHS_BETWEEN(me.created_on, me.skip_inflation) * 31 * 24 * 60 * 60)",
614     },
615   },
616
617   {
618     msg    => '-dt_diff (second) works with DateTime objs',
619     search => { 'me.id' => 2 },
620     select => [ [ -dt_diff => [second => $date2, $date ] ] ],
621     as     => [ 'sec_diff' ],
622     where  => "me.id = ?",
623     bind   => [
624       [{sqlt_datatype => 'timestamp'} => '2010-12-16 12:12:12' ],
625       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
626       [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
627     ],
628     hri    => [{ sec_diff => 2*24*60*60 }],
629     sqlite => {
630       select   => "(STRFTIME('%s', ?) - STRFTIME('%s', ?))",
631     },
632     mssql => {
633       select   => "DATEDIFF(second, ?, ?)",
634       bind   => [
635         [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
636         [{sqlt_datatype => 'timestamp'} => '2010-12-16 12:12:12.000' ],
637         [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
638       ],
639     },
640     mysql => {
641       select   => "TIMESTAMPDIFF(SECOND, ?, ?)",
642       bind   => [
643         [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
644         [{sqlt_datatype => 'timestamp'} => '2010-12-16 12:12:12' ],
645         [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
646       ],
647     },
648     postgres => {
649       select   => "date_part('EPOCH', ?) - date_part('EPOCH', ?)",
650     },
651     oracle => {
652       select   => "TRUNC(MONTHS_BETWEEN(?, ?) * 31 * 24 * 60 * 60)",
653     },
654   },
655
656   {
657     msg    => '-dt_diff (day) works',
658     search => { 'me.id' => 2 },
659     select => [ [ -dt_diff => [day => { -ident => 'me.created_on' }, \'me.skip_inflation' ] ] ],
660     as     => [ 'day_diff' ],
661     where  => "me.id = ?",
662     bind   => [[{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
663     hri    => [{ day_diff => 2 }],
664     sqlite => {
665       select   => "(JULIANDAY(me.created_on) - JULIANDAY(me.skip_inflation))",
666     },
667     mssql => {
668       select   => "DATEDIFF(dayofyear, me.skip_inflation, me.created_on)",
669     },
670     mysql => {
671       select   => "TIMESTAMPDIFF(DAY, me.skip_inflation, me.created_on)",
672     },
673     postgres => {
674       select   => "date_part('DAY', me.created_on) - date_part('DAY', me.skip_inflation)",
675     },
676     oracle => {
677       select   => "TRUNC(MONTHS_BETWEEN(me.created_on, me.skip_inflation) * 31)",
678     },
679   },
680
681   {
682     msg    => '-dt_diff (day) works with DateTime objs',
683     search => { 'me.id' => 2 },
684     select => [ [ -dt_diff => [day => $date2, $date ] ] ],
685     as     => [ 'day_diff' ],
686     where  => "me.id = ?",
687     bind   => [
688       [{sqlt_datatype => 'timestamp'} => '2010-12-16 12:12:12' ],
689       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
690       [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
691     ],
692     hri    => [{ day_diff => 2 }],
693     sqlite => {
694       select   => "(JULIANDAY(?) - JULIANDAY(?))",
695     },
696     mssql => {
697       bind   => [
698         [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
699         [{sqlt_datatype => 'timestamp'} => '2010-12-16 12:12:12.000' ],
700         [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
701       ],
702       select   => "DATEDIFF(dayofyear, ?, ?)",
703     },
704     mysql => {
705       bind   => [
706         [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
707         [{sqlt_datatype => 'timestamp'} => '2010-12-16 12:12:12' ],
708         [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
709       ],
710       select   => "TIMESTAMPDIFF(DAY, ?, ?)",
711     },
712     postgres => {
713       select   => "date_part('DAY', ?) - date_part('DAY', ?)",
714     },
715     oracle => {
716       select   => "TRUNC(MONTHS_BETWEEN(?, ?) * 31)",
717     },
718   },
719
720   {
721     msg    => '-dt_diff (year) works',
722     search => { 'me.id' => 2 },
723     select => [ [ -dt_diff => [year => \'me.starts_at', { -ident => 'me.created_on' } ] ] ],
724     as     => [ 'year' ],
725     where  => "me.id = ?",
726     bind   => [[{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
727     hri    => [{ year => -1 }],
728     sqlite => {
729       exception_like => qr/date diff not supported for part "year" with database "SQLite"/,
730     },
731     mssql => {
732       select   => "DATEDIFF(year, me.created_on, me.starts_at)",
733     },
734     mysql => {
735       select   => "TIMESTAMPDIFF(YEAR, me.created_on, me.starts_at)",
736     },
737     postgres => {
738       select   => "date_part('YEAR', me.starts_at) - date_part('YEAR', me.created_on)",
739     },
740     oracle => {
741       select   => "TRUNC(MONTHS_BETWEEN(me.starts_at, me.created_on) / 12)",
742     },
743   },
744
745   {
746     msg    => '-dt_diff (year) works with DateTime objs',
747     search => { 'me.id' => 2 },
748     select => [ [ -dt_diff => [year => $date4, $date3 ] ] ],
749     as     => [ 'year' ],
750     where  => "me.id = ?",
751     bind   => [
752       [{sqlt_datatype => 'timestamp'} => '2010-12-12 00:00:00' ],
753       [{sqlt_datatype => 'timestamp'} => '2011-12-14 12:12:12' ],
754       [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
755     ],
756     hri    => [{ year => -1 }],
757     sqlite => {
758       exception_like => qr/date diff not supported for part "year" with database "SQLite"/,
759     },
760     mssql => {
761       select   => "DATEDIFF(year, ?, ?)",
762       bind   => [
763         [{sqlt_datatype => 'timestamp'} => '2011-12-14 12:12:12.000' ],
764         [{sqlt_datatype => 'timestamp'} => '2010-12-12 00:00:00.000' ],
765         [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
766       ],
767     },
768     mysql => {
769       select   => "TIMESTAMPDIFF(YEAR, ?, ?)",
770       bind   => [
771         [{sqlt_datatype => 'timestamp'} => '2011-12-14 12:12:12' ],
772         [{sqlt_datatype => 'timestamp'} => '2010-12-12 00:00:00' ],
773         [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
774       ],
775     },
776     postgres => {
777       select   => "date_part('YEAR', ?) - date_part('YEAR', ?)",
778     },
779     oracle => {
780       select   => "TRUNC(MONTHS_BETWEEN(?, ?) / 12)",
781     },
782   },
783
784   {
785     msg    => '-dt_add (year) works',
786     search => { 'me.id' => 2 },
787     select => [ [ -dt_add => [year => 3, { -ident => 'me.created_on' } ] ] ],
788     as     => [ 'date' ],
789     where  => "me.id = ?",
790     bind   => [[unknown_col, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
791     hri    => [{ date => '2014-12-14 12:12:12' }],
792     sqlite => {
793       select => "(datetime(me.created_on, ? || ' years'))",
794     },
795     mssql => {
796       select => "(DATEADD(year, CAST(? AS INTEGER), me.created_on))",
797       bind   => [[{sqlt_datatype => 'integer'}, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
798       hri    => [{ date => '2014-12-14 12:12:12.000' }],
799     },
800     mysql => {
801       select => "DATE_ADD(me.created_on, INTERVAL ? YEAR)",
802     },
803     postgres => {
804       select => "(me.created_on + ? * interval '1 YEAR')",
805     },
806     oracle => {
807       select => "(me.created_on + NUMTOYMINTERVAL(?, 'year'))",
808       hri    => [{ date => '2014-12-14 12:12:12.000000000' }],
809     },
810   },
811
812   {
813     msg    => '-dt_add (year) works with DateTime obj',
814     search => { 'me.id' => 2 },
815     select => [ [ -dt_add => [year => 3, $date ] ] ],
816     as     => [ 'date' ],
817     where  => "me.id = ?",
818     bind   => [
819       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
820       [unknown_col, 3],
821       [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
822     ],
823     hri    => [{ date => '2013-12-14 12:12:12' }],
824     sqlite => {
825       select => "(datetime(?, ? || ' years'))",
826     },
827     mssql => {
828       select => "(DATEADD(year, CAST(? AS INTEGER), ?))",
829       bind   => [
830          [{sqlt_datatype => 'integer'}, 3],
831          [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
832          [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
833       ],
834       hri    => [{ date => '2013-12-14 12:12:12.000' }],
835     },
836     mysql => {
837       select => "DATE_ADD(?, INTERVAL ? YEAR)",
838     },
839     postgres => {
840       select => "(? + ? * interval '1 YEAR')",
841     },
842     oracle => {
843       select => "(? + NUMTOYMINTERVAL(?, 'year'))",
844       hri    => [{ date => '2014-12-14 12:12:12.000000000' }],
845     },
846   },
847
848   {
849     msg    => '-dt_add (month) works',
850     search => { 'me.id' => 2 },
851     select => [ [ -dt_add => [month => 3, { -ident => 'me.created_on' } ] ] ],
852     as     => [ 'date' ],
853     where  => "me.id = ?",
854     bind   => [[unknown_col, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
855     hri    => [{ date => '2012-03-14 12:12:12' }],
856     sqlite => {
857       select => "(datetime(me.created_on, ? || ' months'))",
858     },
859     mssql => {
860       select => "(DATEADD(month, CAST(? AS INTEGER), me.created_on))",
861       bind   => [[{sqlt_datatype => 'integer'}, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
862       hri    => [{ date => '2012-03-14 12:12:12.000' }],
863     },
864     postgres => {
865       select => "(me.created_on + ? * interval '1 MONTH')",
866     },
867     mysql => {
868       select => "DATE_ADD(me.created_on, INTERVAL ? MONTH)",
869     },
870     oracle => {
871       select => "(me.created_on + NUMTOYMINTERVAL(?, 'month'))",
872       hri    => [{ date => '2012-03-14 12:12:12.000000000' }],
873     },
874   },
875
876   {
877     msg    => '-dt_add (month) works with DateTime obj',
878     search => { 'me.id' => 2 },
879     select => [ [ -dt_add => [month => 3, $date ] ] ],
880     as     => [ 'date' ],
881     where  => "me.id = ?",
882     bind   => [
883       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
884       [unknown_col, 3],
885       [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
886     ],
887     hri    => [{ date => '2011-03-14 12:12:12' }],
888     sqlite => {
889       select => "(datetime(?, ? || ' months'))",
890     },
891     mssql => {
892       select => "(DATEADD(month, CAST(? AS INTEGER), ?))",
893       bind   => [
894          [{sqlt_datatype => 'integer'}, 3],
895          [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
896          [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
897       ],
898       hri    => [{ date => '2011-03-14 12:12:12.000' }],
899     },
900     postgres => {
901       select => "(? + ? * interval '1 MONTH')",
902     },
903     mysql => {
904       select => "DATE_ADD(?, INTERVAL ? MONTH)",
905     },
906     oracle => {
907       select => "(? + NUMTOYMINTERVAL(?, 'month'))",
908       hri    => [{ date => '2012-03-14 12:12:12.000000000' }],
909     },
910   },
911
912   {
913     msg    => '-dt_add (day) works',
914     search => { 'me.id' => 2 },
915     select => [ [ -dt_add => [day => 3, { -ident => 'me.created_on' } ] ] ],
916     as     => [ 'date' ],
917     where  => "me.id = ?",
918     bind   => [[unknown_col, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
919     hri    => [{ date => '2011-12-17 12:12:12' }],
920     sqlite => {
921       select => "(datetime(me.created_on, ? || ' days'))",
922     },
923     mssql => {
924       select => "(DATEADD(dayofyear, CAST(? AS INTEGER), me.created_on))",
925       bind   => [[{sqlt_datatype => 'integer'}, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
926       hri    => [{ date => '2011-12-17 12:12:12.000' }],
927     },
928     postgres => {
929       select => "(me.created_on + ? * interval '1 DAY')",
930     },
931     mysql => {
932       select => "DATE_ADD(me.created_on, INTERVAL ? DAY)",
933     },
934     oracle => {
935       select => "(me.created_on + NUMTODSINTERVAL(?, 'day'))",
936       hri    => [{ date => '2011-12-17 12:12:12.000000000' }],
937     },
938   },
939
940   {
941     msg    => '-dt_add (day) works with DateTime obj',
942     search => { 'me.id' => 2 },
943     select => [ [ -dt_add => [day => 3, $date ] ] ],
944     as     => [ 'date' ],
945     where  => "me.id = ?",
946     bind   => [
947       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
948       [unknown_col, 3],
949       [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
950     ],
951     hri    => [{ date => '2010-12-17 12:12:12' }],
952     sqlite => {
953       select => "(datetime(?, ? || ' days'))",
954     },
955     mssql => {
956       select => "(DATEADD(dayofyear, CAST(? AS INTEGER), ?))",
957       bind   => [
958          [{sqlt_datatype => 'integer'}, 3],
959          [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
960          [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
961       ],
962       hri    => [{ date => '2010-12-17 12:12:12.000' }],
963     },
964     postgres => {
965       select => "(? + ? * interval '1 DAY')",
966     },
967     mysql => {
968       select => "DATE_ADD(?, INTERVAL ? DAY)",
969     },
970     oracle => {
971       select => "(? + NUMTODSINTERVAL(?, 'day'))",
972       hri    => [{ date => '2011-12-17 12:12:12.000000000' }],
973     },
974   },
975
976   {
977     msg    => '-dt_add (hour) works',
978     search => { 'me.id' => 2 },
979     select => [ [ -dt_add => [hour => 3, { -ident => 'me.created_on' } ] ] ],
980     as     => [ 'date' ],
981     where  => "me.id = ?",
982     bind   => [[unknown_col, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
983     hri    => [{ date => '2011-12-14 15:12:12' }],
984     sqlite => {
985       select => "(datetime(me.created_on, ? || ' hours'))",
986     },
987     mssql => {
988       select => "(DATEADD(hour, CAST(? AS INTEGER), me.created_on))",
989       bind   => [[{sqlt_datatype => 'integer'}, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
990       hri    => [{ date => '2011-12-14 15:12:12.000' }],
991     },
992     postgres => {
993       select => "(me.created_on + ? * interval '1 HOUR')",
994     },
995     mysql => {
996       select => "DATE_ADD(me.created_on, INTERVAL ? HOUR)",
997     },
998     oracle => {
999       select => "(me.created_on + NUMTODSINTERVAL(?, 'hour'))",
1000       hri    => [{ date => '2011-12-14 15:12:12.000000000' }],
1001     },
1002   },
1003
1004   {
1005     msg    => '-dt_add (hour) works with DateTime obj',
1006     search => { 'me.id' => 2 },
1007     select => [ [ -dt_add => [hour => 3, $date ] ] ],
1008     as     => [ 'date' ],
1009     where  => "me.id = ?",
1010     bind   => [
1011       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
1012       [unknown_col, 3],
1013       [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
1014     ],
1015     hri    => [{ date => '2010-12-14 15:12:12' }],
1016     sqlite => {
1017       select => "(datetime(?, ? || ' hours'))",
1018     },
1019     mssql => {
1020       select => "(DATEADD(hour, CAST(? AS INTEGER), ?))",
1021       bind   => [
1022          [{sqlt_datatype => 'integer'}, 3],
1023          [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
1024          [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
1025       ],
1026       hri    => [{ date => '2010-12-14 15:12:12.000' }],
1027     },
1028     postgres => {
1029       select => "(? + ? * interval '1 HOUR')",
1030     },
1031     mysql => {
1032       select => "DATE_ADD(?, INTERVAL ? HOUR)",
1033     },
1034     oracle => {
1035       select => "(? + NUMTODSINTERVAL(?, 'hour'))",
1036       hri    => [{ date => '2011-12-14 15:12:12.000000000' }],
1037     },
1038   },
1039
1040   {
1041     msg    => '-dt_add (minute) works',
1042     search => { 'me.id' => 2 },
1043     select => [ [ -dt_add => [minute => 3, { -ident => 'me.created_on' } ] ] ],
1044     as     => [ 'date' ],
1045     where  => "me.id = ?",
1046     bind   => [[unknown_col, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
1047     hri    => [{ date => '2011-12-14 12:15:12' }],
1048     sqlite => {
1049       select => "(datetime(me.created_on, ? || ' minutes'))",
1050     },
1051     mssql => {
1052       select => "(DATEADD(minute, CAST(? AS INTEGER), me.created_on))",
1053       bind   => [[{sqlt_datatype => 'integer'}, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
1054       hri    => [{ date => '2011-12-14 12:15:12.000' }],
1055     },
1056     postgres => {
1057       select => "(me.created_on + ? * interval '1 MINUTE')",
1058     },
1059     mysql => {
1060       select => "DATE_ADD(me.created_on, INTERVAL ? MINUTE)",
1061     },
1062     oracle => {
1063       select => "(me.created_on + NUMTODSINTERVAL(?, 'minute'))",
1064       hri    => [{ date => '2011-12-14 12:15:12.000000000' }],
1065     },
1066   },
1067
1068   {
1069     msg    => '-dt_add (minute) works with DateTime obj',
1070     search => { 'me.id' => 2 },
1071     select => [ [ -dt_add => [minute => 3, $date ] ] ],
1072     as     => [ 'date' ],
1073     where  => "me.id = ?",
1074     bind   => [
1075       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
1076       [unknown_col, 3],
1077       [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
1078     ],
1079     hri    => [{ date => '2010-12-14 12:15:12' }],
1080     sqlite => {
1081       select => "(datetime(?, ? || ' minutes'))",
1082     },
1083     mssql => {
1084       select => "(DATEADD(minute, CAST(? AS INTEGER), ?))",
1085       bind   => [
1086          [{sqlt_datatype => 'integer'}, 3],
1087          [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
1088          [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
1089       ],
1090       hri    => [{ date => '2010-12-14 12:15:12.000' }],
1091     },
1092     postgres => {
1093       select => "(? + ? * interval '1 MINUTE')",
1094     },
1095     mysql => {
1096       select => "DATE_ADD(?, INTERVAL ? MINUTE)",
1097     },
1098     oracle => {
1099       select => "(? + NUMTODSINTERVAL(?, 'minute'))",
1100       hri    => [{ date => '2011-12-14 12:15:12.000000000' }],
1101     },
1102   },
1103
1104   {
1105     msg    => '-dt_add (second) works',
1106     search => { 'me.id' => 2 },
1107     select => [ [ -dt_add => [second => 3, { -ident => 'me.created_on' } ] ] ],
1108     as     => [ 'date' ],
1109     where  => "me.id = ?",
1110     bind   => [[unknown_col, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
1111     hri    => [{ date => '2011-12-14 12:12:15' }],
1112     sqlite => {
1113       select => "(datetime(me.created_on, ? || ' seconds'))",
1114     },
1115     mssql => {
1116       select => "(DATEADD(second, CAST(? AS INTEGER), me.created_on))",
1117       bind   => [[{sqlt_datatype => 'integer'}, 3], [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]],
1118       hri    => [{ date => '2011-12-14 12:12:15.000' }],
1119     },
1120     postgres => {
1121       select => "(me.created_on + ? * interval '1 SECOND')",
1122     },
1123     mysql => {
1124       select => "DATE_ADD(me.created_on, INTERVAL ? SECOND)",
1125     },
1126     oracle => {
1127       select => "(me.created_on + NUMTODSINTERVAL(?, 'second'))",
1128       hri    => [{ date => '2011-12-14 12:12:15.000000000' }],
1129     },
1130   },
1131
1132   {
1133     msg    => '-dt_add (second) works with DateTime obj',
1134     search => { 'me.id' => 2 },
1135     select => [ [ -dt_add => [second => 3, $date ] ] ],
1136     as     => [ 'date' ],
1137     where  => "me.id = ?",
1138     bind   => [
1139       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
1140       [unknown_col, 3],
1141       [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
1142     ],
1143     hri    => [{ date => '2010-12-14 12:12:15' }],
1144     sqlite => {
1145       select => "(datetime(?, ? || ' seconds'))",
1146     },
1147     mssql => {
1148       select => "(DATEADD(second, CAST(? AS INTEGER), ?))",
1149       bind   => [
1150          [{sqlt_datatype => 'integer'}, 3],
1151          [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
1152          [{dbic_colname => 'me.id', sqlt_datatype => 'integer'} => 2 ]
1153       ],
1154       hri    => [{ date => '2010-12-14 12:12:15.000' }],
1155     },
1156     postgres => {
1157       select => "(? + ? * interval '1 SECOND')",
1158     },
1159     mysql => {
1160       select => "DATE_ADD(?, INTERVAL ? SECOND)",
1161     },
1162     oracle => {
1163       select => "(? + NUMTODSINTERVAL(?, 'second'))",
1164       hri    => [{ date => '2011-12-14 12:12:15.000000000' }],
1165     },
1166   },
1167
1168   {
1169     msg    => 'nested -dt_add works',
1170     search => { 'me.id' => 2 },
1171     select => [ [ -dt_add => [second => 3, { -dt_add => [ day => 1, { -ident => 'me.created_on' } ] } ] ] ],
1172     as     => [ 'date' ],
1173     where  => "me.id = ?",
1174     hri    => [{ date => '2011-12-15 12:12:15' }],
1175     bind   => [[unknown_col, 1], [unknown_col, 3 ], [{dbic_colname => 'me.id', sqlt_datatype => 'integer' }, 2]],
1176     sqlite => {
1177       select   => "(datetime((datetime(me.created_on, ? || ' days')), ? || ' seconds'))",
1178     },
1179     mssql => {
1180       select => "(DATEADD(second, CAST(? AS INTEGER), (DATEADD(dayofyear, CAST(? AS INTEGER), me.created_on))))",
1181       bind   => [[{sqlt_datatype => 'integer'}, 3 ], [{sqlt_datatype => 'integer'}, 1], [{dbic_colname => 'me.id', sqlt_datatype => 'integer' }, 2]],
1182       hri    => [{ date => '2011-12-15 12:12:15.000' }],
1183     },
1184      postgres => {
1185       select => "((me.created_on + ? * interval '1 DAY') + ? * interval '1 SECOND')",
1186     },
1187     mysql => {
1188       select => "DATE_ADD(DATE_ADD(me.created_on, INTERVAL ? DAY), INTERVAL ? SECOND)",
1189     },
1190     oracle => {
1191       select => "((me.created_on + NUMTODSINTERVAL(?, 'day')) + NUMTODSINTERVAL(?, 'second'))",
1192       hri    => [{ date => '2011-12-15 12:12:15.000000000' }],
1193     },
1194   },
1195
1196   {
1197     msg    => 'nested -dt_add works with DateTime obj',
1198     search => { 'me.id' => 2 },
1199     select => [ [ -dt_add => [second => 3, { -dt_add => [ day => 1, $date ] } ] ] ],
1200     as     => [ 'date' ],
1201     where  => "me.id = ?",
1202     hri    => [{ date => '2010-12-15 12:12:15' }],
1203     bind   => [
1204       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12' ],
1205       [unknown_col, 1],
1206       [unknown_col, 3 ],
1207       [{dbic_colname => 'me.id', sqlt_datatype => 'integer' }, 2]
1208     ],
1209     sqlite => {
1210       select   => "(datetime((datetime(?, ? || ' days')), ? || ' seconds'))",
1211     },
1212     mssql => {
1213       select => "(DATEADD(second, CAST(? AS INTEGER), (DATEADD(dayofyear, CAST(? AS INTEGER), ?))))",
1214     bind   => [
1215       [{sqlt_datatype => 'integer'}, 3 ],
1216       [{sqlt_datatype => 'integer'}, 1],
1217       [{sqlt_datatype => 'timestamp'} => '2010-12-14 12:12:12.000' ],
1218       [{dbic_colname => 'me.id', sqlt_datatype => 'integer' }, 2]
1219     ],
1220       hri    => [{ date => '2010-12-15 12:12:15.000' }],
1221     },
1222      postgres => {
1223       select => "((? + ? * interval '1 DAY') + ? * interval '1 SECOND')",
1224     },
1225     mysql => {
1226       select => "DATE_ADD(DATE_ADD(?, INTERVAL ? DAY), INTERVAL ? SECOND)",
1227     },
1228     oracle => {
1229       select => "((? + NUMTODSINTERVAL(?, 'day')) + NUMTODSINTERVAL(?, 'second'))",
1230       hri    => [{ date => '2011-12-15 12:12:15.000000000' }],
1231     },
1232   },
1233
1234   {
1235     msg      => '-dt_before works',
1236     search   => { 'me.created_on' => { -dt_before => '2011-12-14 12:12:12' } },
1237     select   => [ [ -ident => 'me.created_on' ] ],
1238     as       => [ 'date' ],
1239     select_sql   => "me.created_on",
1240     where    => "me.created_on < ?",
1241     bind     => [[{dbic_colname => 'me.created_on', sqlt_datatype => 'timestamp' }, '2011-12-14 12:12:12']],
1242     hri      => [{ date => '2010-12-14 12:12:12' }],
1243     sqlite   => { },
1244     postgres => { },
1245     mysql    => { },
1246     mssql    => {
1247       hri => [{ date => '2010-12-14 12:12:12.000' }],
1248     },
1249     oracle   => {
1250       hri => [{ date => '2010-12-14 12:12:12.000000' }],
1251     },
1252   },
1253
1254   {
1255     msg      => '-dt_before works with DateTime obj',
1256     search   => { 'me.created_on' => { -dt_before => $date3 } },
1257     select   => [ [ -ident => 'me.created_on' ] ],
1258     as       => [ 'date' ],
1259     select_sql   => "me.created_on",
1260     where    => "me.created_on < ?",
1261     bind     => [[{sqlt_datatype => 'timestamp' }, '2011-12-14 12:12:12']],
1262     hri      => [{ date => '2010-12-14 12:12:12' }],
1263     sqlite   => { },
1264     postgres => { },
1265     mysql    => { },
1266     mssql    => {
1267       hri => [{ date => '2010-12-14 12:12:12.000' }],
1268       bind => [[{sqlt_datatype => 'timestamp' }, '2011-12-14 12:12:12.000']],
1269     },
1270     oracle   => {
1271       hri => [{ date => '2010-12-14 12:12:12.000000' }],
1272     },
1273   },
1274
1275   {
1276     msg      => '-dt_on_or_before works',
1277     search   => { 'me.created_on' => { -dt_on_or_before => '2011-12-14 12:12:12' } },
1278     select   => [ [ -ident => 'me.created_on' ] ],
1279     as       => [ 'date' ],
1280     select_sql   => "me.created_on",
1281     where    => "me.created_on <= ?",
1282     bind     => [[{dbic_colname => 'me.created_on', sqlt_datatype => 'timestamp' }, '2011-12-14 12:12:12']],
1283     hri      => [{ date => '2010-12-14 12:12:12' }, { date => '2011-12-14 12:12:12' }],
1284     sqlite   => { },
1285     postgres => { },
1286     mysql    => { },
1287     mssql    => {
1288       hri => [{ date => '2010-12-14 12:12:12.000' }, { date => '2011-12-14 12:12:12.000' }],
1289     },
1290     oracle   => {
1291       hri => [{ date => '2010-12-14 12:12:12.000000' }, { date => '2011-12-14 12:12:12.000000' }],
1292     },
1293   },
1294
1295   {
1296     msg      => '-dt_on_or_before works with DateTime obj',
1297     search   => { 'me.created_on' => { -dt_on_or_before => $date3 } },
1298     select   => [ [ -ident => 'me.created_on' ] ],
1299     as       => [ 'date' ],
1300     select_sql   => "me.created_on",
1301     where    => "me.created_on <= ?",
1302     bind     => [[{sqlt_datatype => 'timestamp' }, '2011-12-14 12:12:12']],
1303     hri      => [{ date => '2010-12-14 12:12:12' }, { date => '2011-12-14 12:12:12' }],
1304     sqlite   => { },
1305     postgres => { },
1306     mysql    => { },
1307     mssql    => {
1308       hri => [{ date => '2010-12-14 12:12:12.000' }, { date => '2011-12-14 12:12:12.000' }],
1309       bind => [[{sqlt_datatype => 'timestamp' }, '2011-12-14 12:12:12.000']],
1310     },
1311     oracle   => {
1312       hri => [{ date => '2010-12-14 12:12:12.000000' }, { date => '2011-12-14 12:12:12.000000' }],
1313     },
1314   },
1315
1316   {
1317     msg      => '-dt_after works',
1318     search   => { 'me.created_on' => { -dt_after => '2010-12-14 12:12:12' } },
1319     select   => [ [ -ident => 'me.created_on' ] ],
1320     as       => [ 'date' ],
1321     select_sql => "me.created_on",
1322     where    => "me.created_on > ?",
1323     bind     => [[{dbic_colname => 'me.created_on', sqlt_datatype => 'timestamp' }, '2010-12-14 12:12:12']],
1324     hri      => [{ date => '2011-12-14 12:12:12' }],
1325     sqlite   => { },
1326     postgres => { },
1327     mysql    => { },
1328     mssql    => {
1329       hri => [{ date => '2011-12-14 12:12:12.000' }],
1330     },
1331     oracle   => {
1332       hri => [{ date => '2011-12-14 12:12:12.000000' }],
1333     },
1334   },
1335
1336   {
1337     msg      => '-dt_after works with DateTime obj',
1338     search   => { 'me.created_on' => { -dt_after => $date } },
1339     select   => [ [ -ident => 'me.created_on' ] ],
1340     as       => [ 'date' ],
1341     select_sql => "me.created_on",
1342     where    => "me.created_on > ?",
1343     bind     => [[{sqlt_datatype => 'timestamp' }, '2010-12-14 12:12:12']],
1344     hri      => [{ date => '2011-12-14 12:12:12' }],
1345     sqlite   => { },
1346     postgres => { },
1347     mysql    => { },
1348     mssql    => {
1349       hri => [{ date => '2011-12-14 12:12:12.000' }],
1350       bind     => [[{sqlt_datatype => 'timestamp' }, '2010-12-14 12:12:12.000']],
1351     },
1352     oracle   => {
1353       hri => [{ date => '2011-12-14 12:12:12.000000' }],
1354     },
1355   },
1356
1357   {
1358     msg      => '-dt_on_or_after works',
1359     search   => { 'me.created_on' => { -dt_on_or_after => '2010-12-14 12:12:12' } },
1360     select   => [ [ -ident => 'me.created_on' ] ],
1361     as       => [ 'date' ],
1362     select_sql => "me.created_on",
1363     where    => "me.created_on >= ?",
1364     bind     => [[{dbic_colname => 'me.created_on', sqlt_datatype => 'timestamp' }, '2010-12-14 12:12:12']],
1365     hri      => [{ date => '2010-12-14 12:12:12' }, { date => '2011-12-14 12:12:12' }],
1366     sqlite   => { },
1367     postgres => { },
1368     mysql    => { },
1369     mssql    => {
1370       hri => [{ date => '2010-12-14 12:12:12.000' }, { date => '2011-12-14 12:12:12.000' }],
1371     },
1372     oracle   => {
1373       hri => [{ date => '2010-12-14 12:12:12.000000' }, { date => '2011-12-14 12:12:12.000000' }],
1374     },
1375   },
1376
1377   {
1378     msg      => '-dt_on_or_after works with DateTime obj',
1379     search   => { 'me.created_on' => { -dt_on_or_after => $date } },
1380     select   => [ [ -ident => 'me.created_on' ] ],
1381     as       => [ 'date' ],
1382     select_sql => "me.created_on",
1383     where    => "me.created_on >= ?",
1384     bind     => [[{sqlt_datatype => 'timestamp' }, '2010-12-14 12:12:12']],
1385     hri      => [{ date => '2010-12-14 12:12:12' }, { date => '2011-12-14 12:12:12' }],
1386     sqlite   => { },
1387     postgres => { },
1388     mysql    => { },
1389     mssql    => {
1390       hri => [{ date => '2010-12-14 12:12:12.000' }, { date => '2011-12-14 12:12:12.000' }],
1391       bind => [[{sqlt_datatype => 'timestamp' }, '2010-12-14 12:12:12.000']],
1392     },
1393     oracle   => {
1394       hri => [{ date => '2010-12-14 12:12:12.000000' }, { date => '2011-12-14 12:12:12.000000' }],
1395     },
1396   },
1397
1398 );
1399
1400 for my $t (@tests) {
1401
1402   DB_TEST:
1403   for my $db (keys %rs) {
1404      my $db_test = $t->{$db};
1405      unless ($db_test) {
1406         ok 0, "$t->{msg} ($db not tested!)";
1407         next DB_TEST;
1408      }
1409
1410      my ($r, $my_rs);
1411
1412      my $cref = sub {
1413        my $stuff = {
1414          ( exists $t->{select}
1415            ? ( select => $t->{select}, as => $t->{as} )
1416            : ( columns => [qw(starts_at created_on skip_inflation)] )
1417          )
1418        };
1419        $my_rs = $rs{$db}->search($t->{search}, $stuff);
1420        $r = $my_rs->as_query
1421      };
1422
1423      if ($db_test->{exception_like}) {
1424        throws_ok(
1425          sub { $cref->() },
1426          $db_test->{exception_like},
1427          "throws the expected exception ($db_test->{exception_like})",
1428        );
1429      } else {
1430        if ($db_test->{warning_like}) {
1431          warning_like(
1432            sub { $cref->() },
1433            $db_test->{warning_like},
1434            "issues the expected warning ($db_test->{warning_like})"
1435          );
1436        }
1437        else {
1438          $cref->();
1439        }
1440        is_same_sql_bind(
1441          $r,
1442          '(SELECT ' . ($db_test->{select} || $t->{select_sql}) . ' FROM event me WHERE ' . ($db_test->{where} || $t->{where}) . ')',
1443          $db_test->{bind} || $t->{bind},
1444          ($t->{msg} ? "$t->{msg} ($db)" : ())
1445        );
1446
1447        SKIP: {
1448        if (my $hri = $db_test->{hri} || $t->{hri}) {
1449           skip "Cannot test database we are not connected to ($db)", 1 unless $dbs_to_test{$db};
1450           skip $db_test->{skip} . " ($db)", 1 if $db_test->{skip};
1451
1452           my $msg = ($t->{msg} ? "$t->{msg} ($db actually pulls expected data)" : '');
1453           try {
1454              my $got = [ $my_rs->hri_dump->all ];
1455              my $success = is_deeply $got, $hri, $msg;
1456              unless ($success) {
1457                 warn "$db: $t->{msg} got:      " . Dumper $got;
1458                 warn "$db: $t->{msg} expected: " . Dumper $hri;
1459              }
1460           } catch {
1461              ok 0, $msg . " $_";
1462           }
1463         } }
1464      }
1465   }
1466 }
1467
1468 done_testing;