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