1 | /* |
2 | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
5 | * |
6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
7 | */ |
8 | |
9 | #include "monetdb_config.h" |
10 | #include "sql.h" |
11 | #include "mal_instruction.h" |
12 | |
13 | #define do_date_trunc(val, DIVISOR) \ |
14 | timestamp_create(timestamp_date(val), \ |
15 | (timestamp_daytime(val) / (DIVISOR)) * (DIVISOR)) |
16 | |
17 | #define date_trunc_time_loop(NAME, DIVISOR) \ |
18 | do { \ |
19 | if ( strcasecmp(*scale, NAME) == 0){ \ |
20 | for( ; lo < hi; lo++) \ |
21 | if (is_timestamp_nil(bt[lo])) { \ |
22 | dt[lo] = timestamp_nil; \ |
23 | } else { \ |
24 | dt[lo] = do_date_trunc(bt[lo], DIVISOR); \ |
25 | } \ |
26 | } \ |
27 | } while (0) |
28 | |
29 | static inline bool |
30 | truncate_check(const char *scale) |
31 | { |
32 | return |
33 | strcasecmp(scale, "millennium" ) == 0 || |
34 | strcasecmp(scale, "century" ) == 0 || |
35 | strcasecmp(scale, "decade" ) == 0 || |
36 | strcasecmp(scale, "year" ) == 0 || |
37 | strcasecmp(scale, "quarter" ) == 0 || |
38 | strcasecmp(scale, "month" ) == 0 || |
39 | strcasecmp(scale, "week" ) == 0 || |
40 | strcasecmp(scale, "day" ) == 0 || |
41 | strcasecmp(scale, "hour" ) == 0 || |
42 | strcasecmp(scale, "minute" ) == 0 || |
43 | strcasecmp(scale, "second" ) == 0 || |
44 | strcasecmp(scale, "milliseconds" ) == 0 || |
45 | strcasecmp(scale, "microseconds" ) == 0; |
46 | } |
47 | |
48 | str |
49 | bat_date_trunc(bat *res, const str *scale, const bat *bid) |
50 | { |
51 | BAT *b, *bn; |
52 | oid lo, hi; |
53 | const timestamp *bt; |
54 | timestamp *dt; |
55 | char *msg = NULL; |
56 | date days; |
57 | |
58 | if ( truncate_check(*scale) == 0) |
59 | throw(SQL, "batcalc.truncate_timestamp" , SQLSTATE(HY005) "Improper directive " ); |
60 | |
61 | if ((b = BATdescriptor(*bid)) == NULL) { |
62 | throw(SQL, "batcalc.truncate_timestamp" , SQLSTATE(HY005) "Cannot access column descriptor" ); |
63 | } |
64 | bn = COLnew(b->hseqbase, TYPE_timestamp, BATcount(b), TRANSIENT); |
65 | if (bn == NULL) { |
66 | BBPunfix(b->batCacheid); |
67 | throw(SQL, "sql.truncate" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
68 | } |
69 | |
70 | bt = (const timestamp *) Tloc(b, 0); |
71 | dt = (timestamp *) Tloc(bn, 0); |
72 | |
73 | lo = 0; |
74 | hi = lo + BATcount(b); |
75 | |
76 | date_trunc_time_loop("microseconds" , 1); |
77 | date_trunc_time_loop("milliseconds" , 1000); |
78 | date_trunc_time_loop("second" , 1000000); |
79 | date_trunc_time_loop("minute" , 1000000 * 60); |
80 | date_trunc_time_loop("hour" , 1000000 * 60 * 24); |
81 | |
82 | if ( strcasecmp(*scale, "day" ) == 0){ |
83 | for( ; lo < hi; lo++) |
84 | if (is_timestamp_nil(bt[lo])) { |
85 | dt[lo] = timestamp_nil; |
86 | } else { |
87 | days = timestamp_date(bt[lo]); |
88 | dt[lo] = timestamp_fromdate(days); |
89 | } |
90 | } |
91 | |
92 | if ( strcasecmp(*scale, "week" ) == 0){ |
93 | for( ; lo < hi; lo++) |
94 | if (is_timestamp_nil(bt[lo])) { |
95 | dt[lo] = timestamp_nil; |
96 | } else { |
97 | days = timestamp_date(bt[lo]); |
98 | dt[lo] = timestamp_fromdate(date_add_day(days, 1 - date_dayofweek(days))); |
99 | } |
100 | } |
101 | |
102 | if ( strcasecmp(*scale, "month" ) == 0){ |
103 | for( ; lo < hi; lo++) |
104 | if (is_timestamp_nil(bt[lo])) { |
105 | dt[lo] = timestamp_nil; |
106 | } else { |
107 | days = timestamp_date(bt[lo]); |
108 | dt[lo] = timestamp_fromdate( |
109 | date_create(date_year(days), |
110 | date_month(days), |
111 | 1)); |
112 | } |
113 | } |
114 | |
115 | if ( strcasecmp(*scale, "quarter" ) == 0){ |
116 | for( ; lo < hi; lo++) |
117 | if (is_timestamp_nil(bt[lo])) { |
118 | dt[lo] = timestamp_nil; |
119 | } else { |
120 | days = timestamp_date(bt[lo]); |
121 | dt[lo] = timestamp_fromdate( |
122 | date_create(date_year(days), |
123 | (date_month(days) - 1) / 3 + 1, |
124 | 1)); |
125 | } |
126 | } |
127 | |
128 | if ( strcasecmp(*scale, "year" ) == 0){ |
129 | for( ; lo < hi; lo++) |
130 | if (is_timestamp_nil(bt[lo])) { |
131 | dt[lo] = timestamp_nil; |
132 | } else { |
133 | days = timestamp_date(bt[lo]); |
134 | dt[lo] = timestamp_fromdate(date_create(date_year(days), 1, 1)); |
135 | } |
136 | } |
137 | |
138 | if ( strcasecmp(*scale, "decade" ) == 0){ |
139 | for( ; lo < hi; lo++) |
140 | if (is_timestamp_nil(bt[lo])) { |
141 | dt[lo] = timestamp_nil; |
142 | } else { |
143 | days = timestamp_date(bt[lo]); |
144 | dt[lo] = timestamp_fromdate(date_create((date_year(days) / 10) * 10, 1, 1)); |
145 | } |
146 | } |
147 | |
148 | if ( strcasecmp(*scale, "century" ) == 0){ |
149 | for( ; lo < hi; lo++) |
150 | if (is_timestamp_nil(bt[lo])) { |
151 | dt[lo] = timestamp_nil; |
152 | } else { |
153 | days = timestamp_date(bt[lo]); |
154 | dt[lo] = timestamp_fromdate(date_create((date_year(days) / 100) * 100, 1, 1)); |
155 | } |
156 | } |
157 | |
158 | if ( strcasecmp(*scale, "millennium" ) == 0){ |
159 | for( ; lo < hi; lo++) |
160 | if (is_timestamp_nil(bt[lo])) { |
161 | dt[lo] = timestamp_nil; |
162 | } else { |
163 | days = timestamp_date(bt[lo]); |
164 | dt[lo] = timestamp_fromdate(date_create((date_year(days) / 1000) * 1000, 1, 1)); |
165 | } |
166 | } |
167 | |
168 | BATsetcount(bn, (BUN) lo); |
169 | /* we can inherit most properties */ |
170 | bn->tnonil = b->tnonil; |
171 | bn->tnil = b->tnil; |
172 | bn->tsorted = b->tsorted; |
173 | bn->trevsorted = b->trevsorted; |
174 | bn->tkey = false; /* can't be sure */ |
175 | BBPkeepref(*res = bn->batCacheid); |
176 | BBPunfix(b->batCacheid); |
177 | return msg; |
178 | } |
179 | |
180 | #define date_trunc_single_time(NAME, DIVISOR) \ |
181 | do { \ |
182 | if ( strcasecmp(*scale, NAME) == 0){ \ |
183 | *dt = do_date_trunc(*bt, DIVISOR); \ |
184 | } \ |
185 | } while (0) |
186 | |
187 | str |
188 | date_trunc(timestamp *dt, const str *scale, const timestamp *bt) |
189 | { |
190 | str msg = MAL_SUCCEED; |
191 | date days; |
192 | |
193 | if (truncate_check(*scale) == 0) |
194 | throw(SQL, "sql.truncate" , SQLSTATE(HY001) "Improper directive " ); |
195 | |
196 | if (is_timestamp_nil(*bt)) { |
197 | *dt = timestamp_nil; |
198 | return MAL_SUCCEED; |
199 | } |
200 | |
201 | date_trunc_single_time("microseconds" , 1); |
202 | date_trunc_single_time("milliseconds" , 1000); |
203 | date_trunc_single_time("second" , 1000000); |
204 | date_trunc_single_time("minute" , 1000000 * 60); |
205 | date_trunc_single_time("hour" , 1000000 * 60 * 24); |
206 | |
207 | if ( strcasecmp(*scale, "day" ) == 0){ |
208 | days = timestamp_date(*bt); |
209 | *dt = timestamp_fromdate(days); |
210 | } |
211 | |
212 | if ( strcasecmp(*scale, "week" ) == 0){ |
213 | days = timestamp_date(*bt); |
214 | *dt = timestamp_fromdate(date_add_day(days, 1 - date_dayofweek(days))); |
215 | } |
216 | |
217 | if ( strcasecmp(*scale, "month" ) == 0){ |
218 | days = timestamp_date(*bt); |
219 | *dt = timestamp_fromdate(date_create(date_year(days), date_month(days), 1)); |
220 | } |
221 | |
222 | if ( strcasecmp(*scale, "quarter" ) == 0){ |
223 | days = timestamp_date(*bt); |
224 | *dt = timestamp_fromdate(date_create(date_year(days), (date_month(days) - 1) / 3 + 1, 1)); |
225 | } |
226 | |
227 | if ( strcasecmp(*scale, "year" ) == 0){ |
228 | days = timestamp_date(*bt); |
229 | *dt = timestamp_fromdate(date_create(date_year(days), 1, 1)); |
230 | } |
231 | |
232 | if ( strcasecmp(*scale, "decade" ) == 0){ |
233 | days = timestamp_date(*bt); |
234 | *dt = timestamp_fromdate(date_create((date_year(days) / 10) * 10, 1, 1)); |
235 | } |
236 | |
237 | if ( strcasecmp(*scale, "century" ) == 0){ |
238 | days = timestamp_date(*bt); |
239 | *dt = timestamp_fromdate(date_create((date_year(days) / 100) * 100, 1, 1)); |
240 | } |
241 | |
242 | if ( strcasecmp(*scale, "millennium" ) == 0){ |
243 | days = timestamp_date(*bt); |
244 | *dt = timestamp_fromdate(date_create((date_year(days) / 1000) * 1000, 1, 1)); |
245 | } |
246 | return msg; |
247 | } |
248 | |