1 package DBIx::Class::SQLMaker::DateOps;
7 Class::Accessor::Grouped
9 __PACKAGE__->mk_group_accessors (simple => qw/datetime_parser/);
10 use Sub::Name 'subname';
11 use Scalar::Util 'blessed';
13 sub _where_op_CONVERT_DATETIME {
15 my ($op, $rhs) = splice @_, -2;
16 $self->throw_exception("-$op takes a DateTime only") unless ref $rhs && $rhs->isa('DateTime');
18 # in case we are called as a top level special op (no '=')
21 $rhs = $self->datetime_parser->format_datetime($rhs);
24 ($lhs || $self->{_nested_func_lhs} || undef),
30 $self->_convert($self->_quote($lhs)) . ' = ' . $self->_convert('?'),
40 sub _unsupported_date_extraction {
41 "date part extraction not supported for part \"$_[1]\" with database \"$_[2]\""
44 sub _unsupported_date_adding {
45 "date part adding not supported for part \"$_[1]\" with database \"$_[2]\""
48 sub _unsupported_date_diff {
49 "date diff not supported for part \"$_[1]\" with database \"$_[2]\""
52 sub _datetime_sql { die 'date part extraction not implemented for this database' }
54 sub _datetime_diff_sql { die 'date diffing not implemented for this database' }
55 sub _datetime_add_sql { die 'date adding not implemented for this database' }
57 sub _where_op_GET_DATETIME {
72 $self->throw_exception('args to -dt_get must be an arrayref') unless ref $vals eq 'ARRAY';
73 $self->throw_exception('first arg to -dt_get must be a scalar or ARRAY ref') unless !ref $vals->[0] || ref $vals->[0] eq 'ARRAY';
75 my $part = $vals->[0];
78 my ($sql, @bind) = $self->_dt_arg_transform('', $val);
81 return $self->_datetime_sql($part, $sql), @bind;
82 } elsif (ref $part eq 'ARRAY' ) {
83 return ( join ', ', map { $self->_datetime_sql($_, $sql) } @$part ), (@bind) x @$part;
87 for my $part (qw(month year hour minute second)) {
89 my $name = '_where_op_GET_DATETIME_' . uc($part);
90 *{$name} = subname "DBIx::Class::SQLMaker::DateOps::$name", sub {
92 my ($op, $rhs) = splice @_, -2;
96 return $self->_where_op_GET_DATETIME($op, $lhs, [$part, $rhs])
100 sub _where_op_GET_DATETIME_DAY {
102 my ($op, $rhs) = splice @_, -2;
106 # we use day_of_month instead of plain day internally
107 # because some databases also support day_of_week and day_of_year
108 return $self->_where_op_GET_DATETIME($op, $lhs, [day_of_month => $rhs])
111 sub _where_op_DATETIME_NOW {
126 $self->throw_exception("args to -$op must be an arrayref") unless ref $vals eq 'ARRAY';
127 if (!exists $vals->[0]) {
128 return $self->_datetime_now_sql()
129 } elsif ($vals->[0] eq 'system') {
131 return $self->_where_op_CONVERT_DATETIME('dt', DateTime->now);
133 $self->throw_exception("first arg to -$op must be a 'system' or non-existant")
137 sub _reorder_add_datetime_vars {
138 my ($self, $amount, $date) = @_;
140 return ($amount, $date);
143 sub _dt_arg_transform {
148 if (blessed $val && $val->isa('DateTime')) {
150 $self->_convert('?'),
153 $self->datetime_parser->format_datetime($val)
158 $self->_dt_arg_non_date_transform(@_);
161 sub _dt_arg_non_date_transform {
162 my ($self, $k, $val) = @_;
164 my ($sql, @bind) = $self->_SWITCH_refkind($val, {
166 return ($self->_convert('?'), $self->_bindtype($k, $val) );
172 my ($sql, @bind) = @$$val;
173 $self->_assert_bindval_matches_bindtype(@bind);
174 return ($sql, @bind);
177 my $method = $self->_METHOD_FOR_refkind("_where_hashpair", $val);
178 $self->$method('', $val);
181 return ($sql, @bind);
184 sub _where_op_ADD_DATETIME_transform_args { $_[0]->_dt_arg_non_date_transform($_[2], $_[3]) }
186 sub _where_op_ADD_DATETIME {
201 $self->throw_exception("args to -$op must be an arrayref") unless ref $vals eq 'ARRAY';
202 $self->throw_exception("first arg to -$op must be a scalar") unless !ref $vals->[0];
203 $self->throw_exception("-$op must have two more arguments") unless scalar @$vals == 3;
205 my ($part, @rest) = @$vals;
207 my (@all_sql, @all_bind);
209 foreach my $val ($self->_reorder_add_datetime_vars(@rest)) {
210 my ($sql, @bind) = $self->_where_op_ADD_DATETIME_transform_args($i, $k, $val);
212 push @all_bind, @bind;
216 return $self->_datetime_add_sql($part, $all_sql[0], $all_sql[1]), @all_bind
219 sub _reorder_diff_datetime_vars {
220 my ($self, $d1, $d2) = @_;
225 sub _where_op_DIFF_DATETIME {
240 $self->throw_exception('args to -dt_diff must be an arrayref') unless ref $vals eq 'ARRAY';
241 $self->throw_exception('first arg to -dt_diff must be a scalar') unless !ref $vals->[0];
242 $self->throw_exception('-dt_diff must have two more arguments') unless scalar @$vals == 3;
244 my ($part, @val) = @$vals;
245 my $placeholder = $self->_convert('?');
247 @val = $self->_reorder_diff_datetime_vars(@val);
248 my (@all_sql, @all_bind);
249 foreach my $val (@val) {
250 my ($sql, @bind) = $self->_dt_arg_non_date_transform($k, $val);
252 push @all_bind, @bind;
255 return $self->_datetime_diff_sql($part, $all_sql[0], $all_sql[1]), @all_bind
258 sub _where_op_CIRCA_DATETIME {
273 my ($sql, @bind) = $self->_dt_arg_non_date_transform($k, $val);
275 my ($equal, $before, $after) = $op =~ /dt_(on_or_)?(before)?(after)?/;
285 return "$k $sym $sql", @bind;