Commit | Line | Data |
c173ce76 |
1 | package DBIx::Class::SQLMaker::DateOps; |
2 | |
5e7a0b00 |
3 | use strict; |
4 | use warnings; |
5 | |
c173ce76 |
6 | use base qw/ |
7 | Class::Accessor::Grouped |
8 | /; |
9 | __PACKAGE__->mk_group_accessors (simple => qw/datetime_parser/); |
c173ce76 |
10 | use Sub::Name 'subname'; |
5972c6a7 |
11 | use Scalar::Util 'blessed'; |
c173ce76 |
12 | |
13 | sub _where_op_CONVERT_DATETIME { |
14 | my $self = shift; |
15 | my ($op, $rhs) = splice @_, -2; |
90676d46 |
16 | $self->throw_exception("-$op takes a DateTime only") unless ref $rhs && $rhs->isa('DateTime'); |
c173ce76 |
17 | |
18 | # in case we are called as a top level special op (no '=') |
19 | my $lhs = shift; |
20 | |
21 | $rhs = $self->datetime_parser->format_datetime($rhs); |
22 | |
23 | my @bind = [ |
24 | ($lhs || $self->{_nested_func_lhs} || undef), |
25 | $rhs |
26 | ]; |
27 | |
28 | return $lhs |
29 | ? ( |
30 | $self->_convert($self->_quote($lhs)) . ' = ' . $self->_convert('?'), |
31 | @bind |
32 | ) |
33 | : ( |
34 | $self->_convert('?'), |
35 | @bind |
36 | ) |
37 | ; |
38 | } |
39 | |
40 | sub _unsupported_date_extraction { |
41 | "date part extraction not supported for part \"$_[1]\" with database \"$_[2]\"" |
42 | } |
43 | |
44 | sub _unsupported_date_adding { |
45 | "date part adding not supported for part \"$_[1]\" with database \"$_[2]\"" |
46 | } |
47 | |
48 | sub _unsupported_date_diff { |
49 | "date diff not supported for part \"$_[1]\" with database \"$_[2]\"" |
50 | } |
51 | |
52 | sub _datetime_sql { die 'date part extraction not implemented for this database' } |
53 | |
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' } |
56 | |
57 | sub _where_op_GET_DATETIME { |
58 | my ($self) = @_; |
59 | |
60 | my ($k, $op, $vals); |
61 | |
62 | if (@_ == 3) { |
63 | $op = $_[1]; |
64 | $vals = $_[2]; |
65 | $k = ''; |
66 | } elsif (@_ == 4) { |
67 | $k = $_[1]; |
68 | $op = $_[2]; |
69 | $vals = $_[3]; |
70 | } |
71 | |
90676d46 |
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'; |
c173ce76 |
74 | |
75 | my $part = $vals->[0]; |
76 | my $val = $vals->[1]; |
77 | |
5972c6a7 |
78 | my ($sql, @bind) = $self->_dt_arg_transform('', $val); |
c173ce76 |
79 | |
464a9709 |
80 | if (!ref $part) { |
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; |
84 | } |
c173ce76 |
85 | } |
86 | |
fcaf47ee |
87 | for my $part (qw(month year hour minute second)) { |
c173ce76 |
88 | no strict 'refs'; |
89 | my $name = '_where_op_GET_DATETIME_' . uc($part); |
90 | *{$name} = subname "DBIx::Class::SQLMaker::DateOps::$name", sub { |
91 | my $self = shift; |
92 | my ($op, $rhs) = splice @_, -2; |
93 | |
94 | my $lhs = shift; |
95 | |
96 | return $self->_where_op_GET_DATETIME($op, $lhs, [$part, $rhs]) |
97 | } |
98 | } |
99 | |
fcaf47ee |
100 | sub _where_op_GET_DATETIME_DAY { |
101 | my $self = shift; |
102 | my ($op, $rhs) = splice @_, -2; |
103 | |
104 | my $lhs = shift; |
105 | |
455edaef |
106 | # we use day_of_month instead of plain day internally |
107 | # because some databases also support day_of_week and day_of_year |
fcaf47ee |
108 | return $self->_where_op_GET_DATETIME($op, $lhs, [day_of_month => $rhs]) |
109 | } |
110 | |
c173ce76 |
111 | sub _where_op_DATETIME_NOW { |
112 | my ($self) = @_; |
113 | |
114 | my ($k, $op, $vals); |
115 | |
116 | if (@_ == 3) { |
117 | $op = $_[1]; |
118 | $vals = $_[2]; |
119 | $k = ''; |
120 | } elsif (@_ == 4) { |
121 | $k = $_[1]; |
122 | $op = $_[2]; |
123 | $vals = $_[3]; |
124 | } |
125 | |
90676d46 |
126 | $self->throw_exception("args to -$op must be an arrayref") unless ref $vals eq 'ARRAY'; |
c173ce76 |
127 | if (!exists $vals->[0]) { |
128 | return $self->_datetime_now_sql() |
129 | } elsif ($vals->[0] eq 'system') { |
130 | require DateTime; |
131 | return $self->_where_op_CONVERT_DATETIME('dt', DateTime->now); |
132 | } else { |
90676d46 |
133 | $self->throw_exception("first arg to -$op must be a 'system' or non-existant") |
c173ce76 |
134 | } |
135 | } |
136 | |
137 | sub _reorder_add_datetime_vars { |
138 | my ($self, $amount, $date) = @_; |
139 | |
140 | return ($amount, $date); |
141 | } |
142 | |
90676d46 |
143 | sub _dt_arg_transform { |
5972c6a7 |
144 | my $self = shift; |
145 | |
146 | my ($k, $val) = @_; |
147 | |
148 | if (blessed $val && $val->isa('DateTime')) { |
149 | return ( |
150 | $self->_convert('?'), |
151 | [ |
152 | \'timestamp', |
153 | $self->datetime_parser->format_datetime($val) |
154 | ] |
155 | ) |
156 | } |
157 | |
158 | $self->_dt_arg_non_date_transform(@_); |
159 | } |
160 | |
161 | sub _dt_arg_non_date_transform { |
90676d46 |
162 | my ($self, $k, $val) = @_; |
5972c6a7 |
163 | |
90676d46 |
164 | my ($sql, @bind) = $self->_SWITCH_refkind($val, { |
165 | SCALAR => sub { |
166 | return ($self->_convert('?'), $self->_bindtype($k, $val) ); |
167 | }, |
168 | SCALARREF => sub { |
169 | return $$val; |
170 | }, |
171 | ARRAYREFREF => sub { |
172 | my ($sql, @bind) = @$$val; |
173 | $self->_assert_bindval_matches_bindtype(@bind); |
174 | return ($sql, @bind); |
175 | }, |
176 | HASHREF => sub { |
177 | my $method = $self->_METHOD_FOR_refkind("_where_hashpair", $val); |
178 | $self->$method('', $val); |
179 | } |
180 | }); |
181 | return ($sql, @bind); |
182 | } |
183 | |
fbf2cd0a |
184 | sub _where_op_ADD_DATETIME_transform_args { |
185 | if ($_[1] == 0) { |
186 | $_[0]->_dt_arg_non_date_transform($_[2], $_[3]) |
187 | } else { |
188 | $_[0]->_dt_arg_transform($_[2], $_[3]) |
189 | } |
190 | } |
90676d46 |
191 | |
c173ce76 |
192 | sub _where_op_ADD_DATETIME { |
193 | my ($self) = @_; |
194 | |
195 | my ($k, $op, $vals); |
196 | |
197 | if (@_ == 3) { |
198 | $op = $_[1]; |
199 | $vals = $_[2]; |
200 | $k = ''; |
201 | } elsif (@_ == 4) { |
202 | $k = $_[1]; |
203 | $op = $_[2]; |
204 | $vals = $_[3]; |
205 | } |
206 | |
90676d46 |
207 | $self->throw_exception("args to -$op must be an arrayref") unless ref $vals eq 'ARRAY'; |
208 | $self->throw_exception("first arg to -$op must be a scalar") unless !ref $vals->[0]; |
209 | $self->throw_exception("-$op must have two more arguments") unless scalar @$vals == 3; |
c173ce76 |
210 | |
211 | my ($part, @rest) = @$vals; |
212 | |
c173ce76 |
213 | my (@all_sql, @all_bind); |
90676d46 |
214 | my $i = 0; |
fbf2cd0a |
215 | foreach my $val ($self->_reorder_add_datetime_vars(map [ $i++, $_ ], @rest)) { |
216 | my ($sql, @bind) = $self->_where_op_ADD_DATETIME_transform_args($val->[0], $k, $val->[1]); |
c173ce76 |
217 | push @all_sql, $sql; |
218 | push @all_bind, @bind; |
219 | } |
220 | |
221 | return $self->_datetime_add_sql($part, $all_sql[0], $all_sql[1]), @all_bind |
222 | } |
223 | |
f94672f9 |
224 | sub _reorder_diff_datetime_vars { |
225 | my ($self, $d1, $d2) = @_; |
226 | |
227 | return ($d1, $d2); |
228 | } |
229 | |
c173ce76 |
230 | sub _where_op_DIFF_DATETIME { |
231 | my ($self) = @_; |
232 | |
233 | my ($k, $op, $vals); |
234 | |
235 | if (@_ == 3) { |
236 | $op = $_[1]; |
237 | $vals = $_[2]; |
238 | $k = ''; |
239 | } elsif (@_ == 4) { |
240 | $k = $_[1]; |
241 | $op = $_[2]; |
242 | $vals = $_[3]; |
243 | } |
244 | |
90676d46 |
245 | $self->throw_exception('args to -dt_diff must be an arrayref') unless ref $vals eq 'ARRAY'; |
246 | $self->throw_exception('first arg to -dt_diff must be a scalar') unless !ref $vals->[0]; |
247 | $self->throw_exception('-dt_diff must have two more arguments') unless scalar @$vals == 3; |
c173ce76 |
248 | |
249 | my ($part, @val) = @$vals; |
250 | my $placeholder = $self->_convert('?'); |
251 | |
f94672f9 |
252 | @val = $self->_reorder_diff_datetime_vars(@val); |
c173ce76 |
253 | my (@all_sql, @all_bind); |
254 | foreach my $val (@val) { |
fbf2cd0a |
255 | my ($sql, @bind) = $self->_dt_arg_transform($k, $val); |
c173ce76 |
256 | push @all_sql, $sql; |
257 | push @all_bind, @bind; |
258 | } |
259 | |
260 | return $self->_datetime_diff_sql($part, $all_sql[0], $all_sql[1]), @all_bind |
261 | } |
262 | |
69320ddf |
263 | sub _where_op_CIRCA_DATETIME { |
264 | my ($self) = @_; |
265 | |
266 | my ($k, $op, $val); |
267 | |
268 | if (@_ == 3) { |
269 | $op = $_[1]; |
270 | $val = $_[2]; |
271 | $k = ''; |
272 | } elsif (@_ == 4) { |
273 | $k = $_[1]; |
274 | $op = $_[2]; |
275 | $val = $_[3]; |
276 | } |
277 | |
fbf2cd0a |
278 | my ($sql, @bind) = $self->_dt_arg_transform($k, $val); |
69320ddf |
279 | |
280 | my ($equal, $before, $after) = $op =~ /dt_(on_or_)?(before)?(after)?/; |
281 | my $sym = $before |
282 | ? '<' |
283 | : '>' |
284 | ; |
285 | |
286 | $sym .= $equal |
287 | ? '=' |
288 | : '' |
289 | ; |
290 | return "$k $sym $sql", @bind; |
291 | } |
292 | |
c173ce76 |
293 | 1; |