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 "rel_statistics.h"
15 : #include "mtime.h"
16 :
17 : sql_hash *sql_functions_lookup = NULL;
18 :
19 : static void
20 38165 : sql_add_propagate_statistics(mvc *sql, sql_exp *e)
21 : {
22 38165 : list *l = e->l;
23 38165 : sql_exp *first = l->h->data, *second = l->h->next->data;
24 38165 : atom *lmax, *rmax, *lmin, *rmin, *res1 = NULL, *res2 = NULL;
25 38165 : str msg1 = NULL, msg2 = NULL;
26 38165 : prop *est;
27 :
28 38165 : if ((lmax = find_prop_and_get(first->p, PROP_MAX)) && (rmax = find_prop_and_get(second->p, PROP_MAX)) &&
29 16756 : (lmin = find_prop_and_get(first->p, PROP_MIN)) && (rmin = find_prop_and_get(second->p, PROP_MIN))) {
30 16755 : sql_subfunc *f = (sql_subfunc *)e->f;
31 :
32 16755 : if (strcmp(f->func->mod, "calc") == 0) {
33 16604 : res1 = atom_add(sql->sa, atom_copy(sql->sa, lmax), atom_copy(sql->sa, rmax));
34 16604 : res2 = atom_add(sql->sa, atom_copy(sql->sa, lmin), atom_copy(sql->sa, rmin));
35 : } else {
36 151 : sql_subtype tp;
37 :
38 151 : assert(strcmp(f->func->mod, "mtime") == 0);
39 151 : if (strcmp(f->func->imp, "date_add_msec_interval") == 0) {
40 40 : date sub1, sub2;
41 :
42 40 : if (!(msg1 = date_add_msec_interval(&sub1, (date)lmax->data.val.ival, rmax->data.val.lval)) &&
43 40 : !(msg2 = date_add_msec_interval(&sub2, (date)lmin->data.val.ival, rmin->data.val.lval))) {
44 40 : sql_find_subtype(&tp, "date", 0, 0);
45 40 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
46 40 : res2 = atom_general_ptr(sql->sa, &tp, &sub2);
47 : }
48 111 : } else if (strcmp(f->func->imp, "addmonths") == 0) {
49 10 : date sub1, sub2;
50 :
51 10 : if (!(msg1 = date_addmonths(&sub1, (date)lmax->data.val.ival, rmax->data.val.ival)) &&
52 10 : !(msg2 = date_addmonths(&sub2, (date)lmin->data.val.ival, rmin->data.val.ival))) {
53 10 : sql_find_subtype(&tp, "date", 0, 0);
54 10 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
55 10 : res2 = atom_general_ptr(sql->sa, &tp, &sub2);
56 : }
57 101 : } else if (strcmp(f->func->imp, "time_add_msec_interval") == 0) {
58 2 : daytime v1 = (daytime)lmax->data.val.lval, v2 = (daytime)lmin->data.val.lval,
59 2 : sub1 = time_add_msec_interval(v1, rmax->data.val.lval),
60 2 : sub2 = time_add_msec_interval(v2, rmin->data.val.lval);
61 :
62 2 : if (sub1 >= v1 && sub2 >= v2) { /* look for overflows */
63 2 : sql_find_subtype(&tp, "time", 0, 0);
64 2 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
65 2 : res2 = atom_general_ptr(sql->sa, &tp, &sub2);
66 : }
67 99 : } else if (strcmp(f->func->imp, "timestamp_add_msec_interval") == 0) {
68 99 : timestamp sub1, sub2;
69 :
70 99 : if (!(msg1 = timestamp_add_msec_interval(&sub1, (timestamp)lmax->data.val.lval, rmax->data.val.lval)) &&
71 99 : !(msg2 = timestamp_add_msec_interval(&sub2, (timestamp)lmin->data.val.lval, rmin->data.val.lval))) {
72 99 : sql_find_subtype(&tp, "timestamp", 0, 0);
73 99 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
74 99 : res2 = atom_general_ptr(sql->sa, &tp, &sub2);
75 : }
76 0 : } else if (strcmp(f->func->imp, "timestamp_add_month_interval") == 0) {
77 0 : timestamp sub1, sub2;
78 :
79 0 : if (!(msg1 = timestamp_add_month_interval(&sub1, (timestamp)lmax->data.val.lval, rmax->data.val.ival)) &&
80 0 : !(msg2 = timestamp_add_month_interval(&sub2, (timestamp)lmin->data.val.lval, rmin->data.val.ival))) {
81 0 : sql_find_subtype(&tp, "timestamp", 0, 0);
82 0 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
83 0 : res2 = atom_general_ptr(sql->sa, &tp, &sub2);
84 : }
85 : }
86 : }
87 :
88 16755 : if (res1 && res2) { /* if the min/max pair overflows, then don't propagate */
89 15707 : if (atom_cmp(res1, res2) > 0) {
90 14677 : set_minmax_property(sql, e, PROP_MAX, res1);
91 14677 : set_minmax_property(sql, e, PROP_MIN, res2);
92 : } else {
93 1030 : set_minmax_property(sql, e, PROP_MAX, res2);
94 1030 : set_minmax_property(sql, e, PROP_MIN, res1);
95 : }
96 : }
97 16755 : freeException(msg1);
98 16755 : freeException(msg2);
99 : }
100 : /* propagate estimate */
101 38164 : if (!exp_is_atom(first) && exp_is_atom(second) && (est = find_prop(first->p, PROP_NUNIQUES))) {
102 15353 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
103 15353 : p->value.dval = est->value.dval;
104 22811 : } else if (exp_is_atom(first) && !exp_is_atom(second) && (est = find_prop(second->p, PROP_NUNIQUES))) {
105 48 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
106 48 : p->value.dval = est->value.dval;
107 : }
108 38164 : }
109 :
110 : static void
111 11184 : sql_sub_propagate_statistics(mvc *sql, sql_exp *e)
112 : {
113 11184 : list *l = e->l;
114 11184 : sql_exp *first = l->h->data, *second = l->h->next->data;
115 11184 : atom *lmax, *rmax, *lmin, *rmin, *res1 = NULL, *res2 = NULL;
116 11184 : prop *est;
117 :
118 11184 : if ((lmax = find_prop_and_get(first->p, PROP_MAX)) && (rmax = find_prop_and_get(second->p, PROP_MAX)) &&
119 3840 : (lmin = find_prop_and_get(first->p, PROP_MIN)) && (rmin = find_prop_and_get(second->p, PROP_MIN))) {
120 3840 : sql_subfunc *f = (sql_subfunc *)e->f;
121 3840 : str msg1 = NULL, msg2 = NULL;
122 :
123 3840 : if (strcmp(f->func->mod, "calc") == 0) {
124 3801 : res1 = atom_sub(sql->sa, atom_copy(sql->sa, lmax), atom_copy(sql->sa, rmin));
125 3801 : res2 = atom_sub(sql->sa, atom_copy(sql->sa, lmin), atom_copy(sql->sa, rmax));
126 : } else {
127 39 : sql_subtype tp;
128 :
129 39 : assert(strcmp(f->func->mod, "mtime") == 0);
130 39 : if (strcmp(f->func->imp, "diff") == 0) {
131 29 : sql_subtype *t1 = exp_subtype(first);
132 :
133 29 : switch (t1->type->eclass) {
134 10 : case EC_DATE: {
135 10 : res1 = atom_int(sql->sa, exp_subtype(e), date_diff_imp((date)lmax->data.val.ival, (date)rmin->data.val.ival));
136 10 : res2 = atom_int(sql->sa, exp_subtype(e), date_diff_imp((date)lmin->data.val.ival, (date)rmax->data.val.ival));
137 10 : } break;
138 3 : case EC_TIME: {
139 3 : res1 = atom_int(sql->sa, exp_subtype(e), daytime_diff((daytime)lmax->data.val.lval, (daytime)rmin->data.val.lval));
140 3 : res2 = atom_int(sql->sa, exp_subtype(e), daytime_diff((daytime)lmin->data.val.lval, (daytime)rmax->data.val.lval));
141 3 : } break;
142 16 : case EC_TIMESTAMP: {
143 16 : res1 = atom_int(sql->sa, exp_subtype(e), TSDIFF((timestamp)lmax->data.val.lval, (timestamp)rmin->data.val.lval));
144 16 : res2 = atom_int(sql->sa, exp_subtype(e), TSDIFF((timestamp)lmin->data.val.lval, (timestamp)rmax->data.val.lval));
145 16 : } break;
146 : default:
147 : break;
148 : }
149 10 : } else if (strcmp(f->func->imp, "date_sub_msec_interval") == 0) {
150 5 : date sub1, sub2;
151 :
152 5 : if (!(msg1 = date_sub_msec_interval(&sub1, (date)lmax->data.val.ival, rmin->data.val.lval)) &&
153 5 : !(msg2 = date_sub_msec_interval(&sub2, (date)lmin->data.val.ival, rmax->data.val.lval))) {
154 5 : sql_find_subtype(&tp, "date", 0, 0);
155 5 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
156 5 : res2 = atom_general_ptr(sql->sa, &tp, &sub2);
157 : }
158 5 : } else if (strcmp(f->func->imp, "date_sub_month_interval") == 0) {
159 1 : date sub1, sub2;
160 :
161 1 : if (!(msg1 = date_submonths(&sub1, (date)lmax->data.val.ival, rmin->data.val.ival)) &&
162 0 : !(msg2 = date_submonths(&sub2, (date)lmin->data.val.ival, rmax->data.val.ival))) {
163 0 : sql_find_subtype(&tp, "date", 0, 0);
164 0 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
165 0 : res2 = atom_general_ptr(sql->sa, &tp, &sub2);
166 : }
167 4 : } else if (strcmp(f->func->imp, "time_sub_msec_interval") == 0) {
168 2 : daytime v1 = (daytime)lmax->data.val.lval, v2 = (daytime)lmin->data.val.lval,
169 2 : sub1 = time_sub_msec_interval(v1, rmin->data.val.lval),
170 2 : sub2 = time_sub_msec_interval(v2, rmax->data.val.lval);
171 :
172 2 : if (sub1 <= v1 && sub2 <= v2) { /* look for overflows */
173 2 : sql_find_subtype(&tp, "time", 0, 0);
174 2 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
175 2 : res2 = atom_general_ptr(sql->sa, &tp, &sub2);
176 : }
177 2 : } else if (strcmp(f->func->imp, "timestamp_sub_msec_interval") == 0) {
178 2 : timestamp sub1, sub2;
179 :
180 2 : if (!(msg1 = timestamp_sub_msec_interval(&sub1, (timestamp)lmax->data.val.lval, rmin->data.val.lval)) &&
181 2 : !(msg2 = timestamp_sub_msec_interval(&sub2, (timestamp)lmin->data.val.lval, rmax->data.val.lval))) {
182 2 : sql_find_subtype(&tp, "timestamp", 0, 0);
183 2 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
184 2 : res2 = atom_general_ptr(sql->sa, &tp, &sub2);
185 : }
186 0 : } else if (strcmp(f->func->imp, "timestamp_sub_month_interval") == 0) {
187 0 : timestamp sub1, sub2;
188 :
189 0 : if (!(msg1 = timestamp_sub_month_interval(&sub1, (timestamp)lmax->data.val.lval, rmin->data.val.ival)) &&
190 0 : !(msg2 = timestamp_sub_month_interval(&sub2, (timestamp)lmin->data.val.lval, rmax->data.val.ival))) {
191 0 : sql_find_subtype(&tp, "timestamp", 0, 0);
192 0 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
193 0 : res2 = atom_general_ptr(sql->sa, &tp, &sub2);
194 : }
195 : }
196 : }
197 :
198 3840 : if (res1 && res2) { /* if the min/max pair overflows, then don't propagate */
199 3839 : if (atom_cmp(res1, res2) > 0) {
200 3759 : set_minmax_property(sql, e, PROP_MAX, res1);
201 3759 : set_minmax_property(sql, e, PROP_MIN, res2);
202 : } else {
203 80 : set_minmax_property(sql, e, PROP_MAX, res2);
204 80 : set_minmax_property(sql, e, PROP_MIN, res1);
205 : }
206 : }
207 3840 : freeException(msg1);
208 3840 : freeException(msg2);
209 : }
210 : /* propagate estimate */
211 11184 : if (!exp_is_atom(first) && exp_is_atom(second) && (est = find_prop(first->p, PROP_NUNIQUES))) {
212 4003 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
213 4003 : p->value.dval = est->value.dval;
214 7181 : } else if (exp_is_atom(first) && !exp_is_atom(second) && (est = find_prop(second->p, PROP_NUNIQUES))) {
215 29 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
216 29 : p->value.dval = est->value.dval;
217 : }
218 11184 : }
219 :
220 : static void
221 14724 : sql_mul_propagate_statistics(mvc *sql, sql_exp *e)
222 : {
223 14724 : list *l = e->l;
224 14724 : sql_exp *first = l->h->data, *second = l->h->next->data;
225 14724 : atom *lmax, *rmax, *lmin, *rmin;
226 :
227 14724 : if ((lmax = find_prop_and_get(first->p, PROP_MAX)) && (rmax = find_prop_and_get(second->p, PROP_MAX)) &&
228 8873 : (lmin = find_prop_and_get(first->p, PROP_MIN)) && (rmin = find_prop_and_get(second->p, PROP_MIN))) {
229 8873 : atom *res1 = atom_mul(sql->sa, atom_copy(sql->sa, lmax), atom_copy(sql->sa, rmax));
230 8873 : atom *res2 = atom_mul(sql->sa, atom_copy(sql->sa, lmin), atom_copy(sql->sa, rmin));
231 :
232 8873 : if (res1 && res2) { /* if the min/max pair overflows, then don't propagate */
233 8802 : atom *zero1 = atom_zero_value(sql->sa, &(lmax->tpe)), *zero2 = atom_zero_value(sql->sa, &(rmax->tpe));
234 8802 : int cmp1 = atom_cmp(lmax, zero1), cmp2 = atom_cmp(lmin, zero1), cmp3 = atom_cmp(rmin, zero2), cmp4 = atom_cmp(rmax, zero2);
235 :
236 8802 : if (cmp1 >= 0 && cmp2 >= 0 && cmp3 >= 0 && cmp4 >= 0) { /* if all positive then propagate */
237 8767 : set_minmax_property(sql, e, PROP_MAX, res1);
238 8767 : set_minmax_property(sql, e, PROP_MIN, res2);
239 35 : } else if (cmp1 < 0 && cmp2 < 0 && cmp3 < 0 && cmp4 < 0) { /* if all negative propagate by swapping min and max */
240 6 : set_minmax_property(sql, e, PROP_MAX, res2);
241 6 : set_minmax_property(sql, e, PROP_MIN, res1);
242 : }
243 : }
244 : }
245 14724 : }
246 :
247 : static void
248 1381 : sql_div_propagate_statistics(mvc *sql, sql_exp *e)
249 : {
250 1381 : list *l = e->l;
251 1381 : sql_exp *first = l->h->data, *second = l->h->next->data;
252 1381 : atom *lmax, *rmax, *lmin, *rmin;
253 :
254 1381 : if ((lmax = find_prop_and_get(first->p, PROP_MAX)) && (rmax = find_prop_and_get(second->p, PROP_MAX)) &&
255 301 : (lmin = find_prop_and_get(first->p, PROP_MIN)) && (rmin = find_prop_and_get(second->p, PROP_MIN))) {
256 301 : atom *res1 = atom_div(sql->sa, atom_copy(sql->sa, lmax), atom_copy(sql->sa, rmin));
257 301 : atom *res2 = atom_div(sql->sa, atom_copy(sql->sa, lmin), atom_copy(sql->sa, rmax));
258 :
259 301 : if (res1 && res2) { /* on div by zero don't propagate */
260 277 : atom *zero1 = atom_zero_value(sql->sa, &(lmax->tpe)), *zero2 = atom_zero_value(sql->sa, &(rmax->tpe));
261 277 : int cmp1 = atom_cmp(lmax, zero1), cmp2 = atom_cmp(lmin, zero1), cmp3 = atom_cmp(rmin, zero2), cmp4 = atom_cmp(rmax, zero2);
262 :
263 277 : if (cmp1 >= 0 && cmp2 >= 0 && cmp3 >= 0 && cmp4 >= 0) { /* if all positive then propagate */
264 252 : set_minmax_property(sql, e, PROP_MAX, res1);
265 252 : set_minmax_property(sql, e, PROP_MIN, res2);
266 25 : } else if (cmp1 < 0 && cmp2 < 0 && cmp3 < 0 && cmp4 < 0) { /* if all negative propagate by swapping min and max */
267 3 : set_minmax_property(sql, e, PROP_MAX, res2);
268 3 : set_minmax_property(sql, e, PROP_MIN, res1);
269 : }
270 : }
271 : }
272 1381 : }
273 :
274 : static void
275 366 : sql_extend_min_max(mvc *sql, sql_exp *e, sql_exp *first, sql_exp *second)
276 : {
277 366 : atom *lval, *rval;
278 :
279 366 : if ((lval = find_prop_and_get(first->p, PROP_MAX)) && (rval = find_prop_and_get(second->p, PROP_MAX)))
280 318 : set_minmax_property(sql, e, PROP_MAX, atom_cmp(lval, rval) > 0 ? lval : rval);
281 366 : if ((lval = find_prop_and_get(first->p, PROP_MIN)) && (rval = find_prop_and_get(second->p, PROP_MIN)))
282 324 : set_minmax_property(sql, e, PROP_MIN, atom_cmp(lval, rval) > 0 ? rval : lval);
283 366 : }
284 :
285 : static void
286 366 : sql_least_greatest_propagate_statistics(mvc *sql, sql_exp *e)
287 : {
288 366 : list *l = e->l;
289 366 : sql_extend_min_max(sql, e, l->h->data, l->h->next->data);
290 366 : }
291 :
292 : static void
293 20214 : sql_ifthenelse_propagate_statistics(mvc *sql, sql_exp *e)
294 : {
295 20214 : list *l = e->l;
296 20214 : sql_exp *first = l->h->next->data;
297 20214 : atom *curmin = NULL, *curmax = NULL, *lval;
298 20214 : unsigned int i = 0;
299 :
300 20214 : assert(list_length(l) >= 2);
301 20214 : if ((lval = find_prop_and_get(first->p, PROP_MAX)))
302 : curmax = lval;
303 20214 : if ((lval = find_prop_and_get(first->p, PROP_MIN)))
304 : curmin = lval;
305 35528 : for (node *n = l->h->next->next ; n && curmin && curmax ; n = n->next) {
306 15314 : if ((i & 1) || n == l->t) { /* the last expression, ie the result, must be included */
307 15314 : sql_exp *next = n->data;
308 15314 : if ((lval = find_prop_and_get(next->p, PROP_MAX))) {
309 7272 : curmax = atom_cmp(lval, curmax) > 0 ? lval : curmax;
310 : } else {
311 : curmax = NULL;
312 : }
313 15314 : if ((lval = find_prop_and_get(next->p, PROP_MIN))) {
314 7272 : curmin = atom_cmp(lval, curmin) > 0 ? curmin : lval;
315 : } else {
316 : curmin = NULL;
317 : }
318 : }
319 15314 : i++;
320 : }
321 :
322 20214 : if (curmin && curmax) {
323 7272 : set_minmax_property(sql, e, PROP_MAX, curmax);
324 7272 : set_minmax_property(sql, e, PROP_MIN, curmin);
325 : }
326 20214 : }
327 :
328 : static void
329 4184 : sql_casewhen_propagate_statistics(mvc *sql, sql_exp *e)
330 : {
331 4184 : list *l = e->l;
332 4184 : sql_exp *first = l->h->next->next->data;
333 4184 : atom *curmin = NULL, *curmax = NULL, *lval;
334 4184 : unsigned int i = 0;
335 :
336 4184 : assert(list_length(l) >= 3);
337 4184 : if ((lval = find_prop_and_get(first->p, PROP_MAX)))
338 : curmax = lval;
339 4183 : if ((lval = find_prop_and_get(first->p, PROP_MIN)))
340 : curmin = lval;
341 31335 : for (node *n = l->h->next->next->next ; n && curmin && curmax ; n = n->next) {
342 27152 : if ((i & 1) || n == l->t) { /* the last expression, ie the result, must be included */
343 15073 : sql_exp *next = n->data;
344 15073 : if ((lval = find_prop_and_get(next->p, PROP_MAX))) {
345 13033 : curmax = atom_cmp(lval, curmax) > 0 ? lval : curmax;
346 : } else {
347 : curmax = NULL;
348 : }
349 15073 : if ((lval = find_prop_and_get(next->p, PROP_MIN))) {
350 13033 : curmin = atom_cmp(lval, curmin) > 0 ? curmin : lval;
351 : } else {
352 : curmin = NULL;
353 : }
354 : }
355 27152 : i++;
356 : }
357 :
358 4183 : if (curmin && curmax) {
359 1386 : set_minmax_property(sql, e, PROP_MAX, curmax);
360 1386 : set_minmax_property(sql, e, PROP_MIN, curmin);
361 : }
362 4183 : }
363 :
364 : static void
365 88 : sql_nullif_propagate_statistics(mvc *sql, sql_exp *e)
366 : {
367 88 : list *l = e->l;
368 88 : sql_exp *first = l->h->data;
369 88 : atom *lval;
370 :
371 88 : if ((lval = find_prop_and_get(first->p, PROP_MAX)))
372 53 : set_minmax_property(sql, e, PROP_MAX, lval);
373 88 : if ((lval = find_prop_and_get(first->p, PROP_MIN)))
374 53 : set_minmax_property(sql, e, PROP_MIN, lval);
375 88 : }
376 :
377 : static void
378 3518 : sql_neg_propagate_statistics(mvc *sql, sql_exp *e)
379 : {
380 3518 : list *l = e->l;
381 3518 : sql_exp *first = l->h->data;
382 3518 : atom *lval;
383 3518 : prop *est;
384 :
385 3518 : if ((lval = find_prop_and_get(first->p, PROP_MIN))) {
386 3351 : atom *res = atom_copy(sql->sa, lval);
387 3351 : if ((res = atom_neg(sql->sa, res)))
388 3351 : set_minmax_property(sql, e, PROP_MAX, res);
389 : }
390 3518 : if ((lval = find_prop_and_get(first->p, PROP_MAX))) {
391 3349 : atom *res = atom_copy(sql->sa, lval);
392 3349 : if ((res = atom_neg(sql->sa, res)))
393 3349 : set_minmax_property(sql, e, PROP_MIN, res);
394 : }
395 3518 : if ((est = find_prop(first->p, PROP_NUNIQUES))) {
396 3421 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
397 3421 : p->value.dval = est->value.dval;
398 : }
399 3518 : }
400 :
401 : static void
402 15 : sql_sign_propagate_statistics(mvc *sql, sql_exp *e)
403 : {
404 15 : list *l = e->l;
405 15 : sql_exp *first = l->h->data;
406 15 : atom *omin, *omax;
407 15 : sql_subtype *bte = sql_bind_localtype("bte");
408 15 : bool properties_set = false;
409 :
410 15 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
411 10 : atom *zero1 = atom_zero_value(sql->sa, &(omin->tpe));
412 10 : int cmp1 = atom_cmp(omax, zero1), cmp2 = atom_cmp(omin, zero1);
413 :
414 10 : if (cmp1 == 0 && cmp2 == 0) {
415 0 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, bte, 0));
416 0 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, bte, 0));
417 0 : properties_set = true;
418 10 : } else if (cmp1 > 0 && cmp2 > 0) {
419 3 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, bte, 1));
420 3 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, bte, 1));
421 3 : properties_set = true;
422 7 : } else if (cmp1 < 0 && cmp2 < 0) {
423 1 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, bte, -1));
424 1 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, bte, -1));
425 1 : properties_set = true;
426 : }
427 : }
428 4 : if (!properties_set) {
429 11 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, bte, 1));
430 11 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, bte, -1));
431 : }
432 15 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
433 15 : p->value.dval = 2;
434 15 : }
435 :
436 : static void
437 2077 : sql_abs_propagate_statistics(mvc *sql, sql_exp *e)
438 : {
439 2077 : list *l = e->l;
440 2077 : sql_exp *first = l->h->data;
441 2077 : atom *omin, *omax;
442 :
443 2077 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
444 1222 : atom *zero = atom_zero_value(sql->sa, &(omin->tpe));
445 1222 : int cmp1 = atom_cmp(omax, zero), cmp2 = atom_cmp(omin, zero);
446 :
447 1222 : if (cmp1 >= 0 && cmp2 >= 0) {
448 1017 : set_minmax_property(sql, e, PROP_MAX, omax);
449 1017 : set_minmax_property(sql, e, PROP_MIN, omin);
450 205 : } else if (cmp1 < 0 && cmp2 < 0) {
451 15 : atom *res1 = atom_copy(sql->sa, omin), *res2 = atom_copy(sql->sa, omax);
452 :
453 15 : if ((res1 = atom_absolute(sql->sa, res1)) && (res2 = atom_absolute(sql->sa, res2))) {
454 15 : set_minmax_property(sql, e, PROP_MAX, res1);
455 15 : set_minmax_property(sql, e, PROP_MIN, res2);
456 : }
457 : } else {
458 190 : atom *res1 = atom_copy(sql->sa, omin);
459 :
460 190 : if ((res1 = atom_absolute(sql->sa, res1))) {
461 369 : set_minmax_property(sql, e, PROP_MAX, atom_cmp(res1, omax) > 0 ? res1 : omax);
462 190 : set_minmax_property(sql, e, PROP_MIN, zero);
463 : }
464 : }
465 : }
466 2077 : }
467 :
468 : static void
469 1394 : sql_coalesce_propagate_statistics(mvc *sql, sql_exp *e)
470 : {
471 1394 : list *l = e->l;
472 1394 : sql_exp *first = l->h->data;
473 1394 : atom *curmin = NULL, *curmax = NULL, *lval;
474 :
475 1394 : if ((lval = find_prop_and_get(first->p, PROP_MAX)))
476 : curmax = lval;
477 1394 : if ((lval = find_prop_and_get(first->p, PROP_MIN)))
478 : curmin = lval;
479 1741 : for (node *n = l->h->next ; n && curmin && curmax ; n = n->next) {
480 347 : sql_exp *next = n->data;
481 :
482 347 : if ((lval = find_prop_and_get(next->p, PROP_MAX))) {
483 176 : curmax = atom_cmp(lval, curmax) > 0 ? lval : curmax;
484 : } else {
485 : curmax = NULL;
486 : }
487 347 : if ((lval = find_prop_and_get(next->p, PROP_MIN))) {
488 176 : curmin = atom_cmp(lval, curmin) > 0 ? curmin : lval;
489 : } else {
490 : curmin = NULL;
491 : }
492 : }
493 :
494 1394 : if (curmin && curmax) {
495 131 : set_minmax_property(sql, e, PROP_MAX, curmax);
496 131 : set_minmax_property(sql, e, PROP_MIN, curmin);
497 : }
498 1394 : }
499 :
500 : static void
501 3 : sql_century_propagate_statistics(mvc *sql, sql_exp *e)
502 : {
503 3 : list *l = e->l;
504 3 : sql_exp *first = l->h->data;
505 3 : atom *omin, *omax;
506 3 : int nmin = -50, nmax = 1800;
507 :
508 3 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
509 2 : sql_subtype *tp = exp_subtype(first);
510 2 : if (tp->type->eclass == EC_TIMESTAMP) {
511 1 : nmin = timestamp_century((timestamp)omin->data.val.lval);
512 1 : nmax = timestamp_century((timestamp)omax->data.val.lval);
513 1 : } else if (tp->type->eclass == EC_DATE) {
514 1 : nmin = date_century((date)omin->data.val.ival);
515 1 : nmax = date_century((date)omax->data.val.ival);
516 : }
517 : }
518 :
519 3 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype("int"), nmax));
520 3 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype("int"), nmin));
521 3 : }
522 :
523 : static void
524 0 : sql_decade_propagate_statistics(mvc *sql, sql_exp *e)
525 : {
526 0 : list *l = e->l;
527 0 : sql_exp *first = l->h->data;
528 0 : atom *omin, *omax;
529 0 : int nmin = -500, nmax = 18000;
530 :
531 0 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
532 0 : sql_subtype *tp = exp_subtype(first);
533 0 : if (tp->type->eclass == EC_TIMESTAMP) {
534 0 : nmin = timestamp_decade((timestamp)omin->data.val.lval);
535 0 : nmax = timestamp_decade((timestamp)omax->data.val.lval);
536 0 : } else if (tp->type->eclass == EC_DATE) {
537 0 : nmin = date_decade((date)omin->data.val.ival);
538 0 : nmax = date_decade((date)omax->data.val.ival);
539 : }
540 : }
541 :
542 0 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype("int"), nmax));
543 0 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype("int"), nmin));
544 0 : }
545 :
546 : static void
547 53 : sql_year_propagate_statistics(mvc *sql, sql_exp *e)
548 : {
549 53 : list *l = e->l;
550 53 : sql_exp *first = l->h->data;
551 53 : atom *omin, *omax;
552 53 : int nmin = -5000, nmax = 180000;
553 :
554 53 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
555 38 : sql_subtype *tp = exp_subtype(first);
556 38 : if (tp->type->eclass == EC_TIMESTAMP) {
557 4 : nmin = timestamp_year((timestamp)omin->data.val.lval);
558 4 : nmax = timestamp_year((timestamp)omax->data.val.lval);
559 34 : } else if (tp->type->eclass == EC_DATE) {
560 34 : nmin = date_year((date)omin->data.val.ival);
561 34 : nmax = date_year((date)omax->data.val.ival);
562 : }
563 : }
564 :
565 53 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype("int"), nmax));
566 53 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype("int"), nmin));
567 53 : }
568 :
569 : static void
570 15 : sql_quarter_propagate_statistics(mvc *sql, sql_exp *e)
571 : {
572 15 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype("int"), 4));
573 15 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype("int"), 1));
574 15 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
575 15 : p->value.dval = 4;
576 15 : }
577 :
578 : static void
579 61 : sql_month_propagate_statistics(mvc *sql, sql_exp *e)
580 : {
581 61 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype("int"), 12));
582 61 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype("int"), 1));
583 61 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
584 61 : p->value.dval = 12;
585 61 : }
586 :
587 : static void
588 25 : sql_day_propagate_statistics(mvc *sql, sql_exp *e)
589 : {
590 25 : list *l = e->l;
591 25 : sql_exp *first = l->h->data;
592 25 : sql_subtype *tp = exp_subtype(first);
593 25 : const char *localtype = tp->type->eclass == EC_SEC ? "lng" : "int";
594 25 : atom *omin, *omax;
595 25 : lng nmin = 1, nmax = 31;
596 :
597 25 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
598 14 : if (tp->type->eclass == EC_SEC) {
599 5 : nmin = sql_day(omin->data.val.lval);
600 5 : nmax = sql_day(omax->data.val.lval);
601 : }
602 : }
603 :
604 25 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype(localtype), nmax));
605 25 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype(localtype), nmin));
606 25 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
607 25 : p->value.dval = (dbl) (nmax - nmin + 1);
608 25 : }
609 :
610 : static void
611 3 : sql_dayofyear_propagate_statistics(mvc *sql, sql_exp *e)
612 : {
613 3 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype("int"), 366));
614 3 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype("int"), 1));
615 3 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
616 3 : p->value.dval = 366;
617 3 : }
618 :
619 : static void
620 17 : sql_weekofyear_propagate_statistics(mvc *sql, sql_exp *e)
621 : {
622 17 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype("int"), 53));
623 17 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype("int"), 1));
624 17 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
625 17 : p->value.dval = 53;
626 17 : }
627 :
628 : static void
629 4 : sql_dayofweek_propagate_statistics(mvc *sql, sql_exp *e)
630 : {
631 4 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype("int"), 7));
632 4 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype("int"), 1));
633 4 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
634 4 : p->value.dval = 7;
635 4 : }
636 :
637 : static void
638 20 : sql_hour_propagate_statistics(mvc *sql, sql_exp *e)
639 : {
640 20 : list *l = e->l;
641 20 : sql_exp *first = l->h->data;
642 20 : atom *omin, *omax;
643 20 : int nmin = 0, nmax = 23;
644 20 : sql_subtype *tp = exp_subtype(first);
645 20 : const char *localtype = tp->type->eclass == EC_SEC ? "lng" : "int";
646 :
647 20 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
648 8 : if (tp->type->eclass == EC_TIME) {
649 6 : nmin = daytime_hour((daytime)omin->data.val.lval);
650 6 : nmax = daytime_hour((daytime)omax->data.val.lval);
651 : }
652 : }
653 :
654 20 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype(localtype), nmax));
655 20 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype(localtype), nmin));
656 20 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
657 20 : p->value.dval = 24;
658 20 : }
659 :
660 : static void
661 22 : sql_minute_propagate_statistics(mvc *sql, sql_exp *e)
662 : {
663 22 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype("int"), 59));
664 22 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype("int"), 0));
665 22 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
666 22 : p->value.dval = 60;
667 22 : }
668 :
669 : static void
670 25 : sql_second_propagate_statistics(mvc *sql, sql_exp *e)
671 : {
672 25 : list *l = e->l;
673 25 : sql_exp *first = l->h->data;
674 25 : sql_subtype *tp = exp_subtype(first), tp_res;
675 25 : int nmin = 0, nmax = 59;
676 :
677 25 : if (tp->type->eclass == EC_TIMESTAMP || tp->type->eclass == EC_TIME) {
678 24 : nmax = 59999999;
679 24 : sql_find_subtype(&tp_res, "decimal", 8, 6);
680 : } else {
681 1 : sql_find_subtype(&tp_res, "int", 0, 0);
682 : }
683 25 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, &tp_res, nmax));
684 25 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, &tp_res, nmin));
685 25 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
686 25 : p->value.dval = 60;
687 25 : }
688 :
689 : static void
690 6 : sql_epoch_ms_propagate_statistics(mvc *sql, sql_exp *e)
691 : {
692 6 : list *l = e->l;
693 6 : sql_exp *first = l->h->data;
694 6 : atom *omin, *omax, *nmin = NULL, *nmax = NULL;
695 6 : sql_subtype *tp = exp_subtype(first);
696 :
697 6 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
698 1 : switch (tp->type->eclass) {
699 1 : case EC_DATE: {
700 1 : nmax = atom_int(sql->sa, sql_bind_localtype("lng"), date_to_msec_since_epoch((date)omax->data.val.ival));
701 1 : nmin = atom_int(sql->sa, sql_bind_localtype("lng"), date_to_msec_since_epoch((date)omin->data.val.ival));
702 1 : } break;
703 0 : case EC_TIME: {
704 0 : nmax = atom_int(sql->sa, sql_bind_localtype("lng"), daytime_to_msec_since_epoch((daytime)omax->data.val.lval));
705 0 : nmin = atom_int(sql->sa, sql_bind_localtype("lng"), daytime_to_msec_since_epoch((daytime)omin->data.val.lval));
706 0 : } break;
707 0 : case EC_TIMESTAMP: {
708 0 : nmax = atom_int(sql->sa, sql_bind_localtype("lng"), timestamp_to_msec_since_epoch((timestamp)omax->data.val.lval));
709 0 : nmin = atom_int(sql->sa, sql_bind_localtype("lng"), timestamp_to_msec_since_epoch((timestamp)omin->data.val.lval));
710 0 : } break;
711 0 : case EC_SEC: {
712 0 : nmax = atom_int(sql->sa, sql_bind_localtype("lng"), msec_since_epoch(omax->data.val.lval));
713 0 : nmin = atom_int(sql->sa, sql_bind_localtype("lng"), msec_since_epoch(omin->data.val.lval));
714 0 : } break;
715 : default:
716 : break;
717 : }
718 1 : if (nmin && nmax) {
719 1 : set_minmax_property(sql, e, PROP_MAX, nmax);
720 1 : set_minmax_property(sql, e, PROP_MIN, nmin);
721 : }
722 : }
723 6 : }
724 :
725 : static void
726 3512 : sql_min_max_propagate_statistics(mvc *sql, sql_exp *e)
727 : {
728 3512 : list *l = e->l;
729 3512 : if (list_empty(l))
730 : return;
731 3512 : sql_exp *first = l->h->data;
732 3512 : atom *omin, *omax;
733 :
734 3512 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
735 1146 : set_minmax_property(sql, e, PROP_MAX, omax);
736 1146 : set_minmax_property(sql, e, PROP_MIN, omin);
737 : }
738 : }
739 :
740 : static void
741 1541 : sql_avg_propagate_statistics(mvc *sql, sql_exp *e)
742 : {
743 1541 : list *l = e->l;
744 1541 : sql_exp *first = l->h->data;
745 1541 : atom *omin, *omax;
746 :
747 1541 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
748 1250 : sql_subtype *etype = exp_subtype(e), *ftype = exp_subtype(first);
749 1250 : if (ftype && etype->type->base.id == ftype->type->base.id) { /* average on decimals or intervals */
750 68 : set_minmax_property(sql, e, PROP_MAX, omax);
751 68 : set_minmax_property(sql, e, PROP_MIN, omin);
752 1182 : } else if (ftype && etype) { /* average on integer types */
753 1182 : assert(etype->type->eclass == EC_FLT);
754 1182 : atom *min_cast = atom_copy(sql->sa, omin), *max_cast = atom_copy(sql->sa, omax);
755 1182 : if ((min_cast = atom_cast(sql->sa, min_cast, etype)) && (max_cast = atom_cast(sql->sa, max_cast, etype))) {
756 1167 : set_minmax_property(sql, e, PROP_MAX, max_cast);
757 1167 : set_minmax_property(sql, e, PROP_MIN, min_cast);
758 : }
759 : }
760 : }
761 1541 : }
762 :
763 : static void
764 2238 : sql_zero_or_one_propagate_statistics(mvc *sql, sql_exp *e)
765 : {
766 2238 : list *l = e->l;
767 2238 : sql_exp *first = l->h->data;
768 2238 : atom *omin, *omax;
769 :
770 2238 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
771 23 : set_minmax_property(sql, e, PROP_MAX, omax);
772 23 : set_minmax_property(sql, e, PROP_MIN, omin);
773 : }
774 2238 : }
775 :
776 : static struct function_properties functions_list[35] = {
777 : /* arithmetic functions */
778 : {"sql_add", &sql_add_propagate_statistics},
779 : {"sql_sub", &sql_sub_propagate_statistics},
780 : {"sql_mul", &sql_mul_propagate_statistics},
781 : {"sql_div", &sql_div_propagate_statistics},
782 : {"sql_neg", &sql_neg_propagate_statistics},
783 : {"sign", &sql_sign_propagate_statistics},
784 : {"abs", &sql_abs_propagate_statistics},
785 :
786 : /* sql comparison functions */
787 : {"sql_min", &sql_least_greatest_propagate_statistics},
788 : {"sql_max", &sql_least_greatest_propagate_statistics},
789 : {"least", &sql_least_greatest_propagate_statistics},
790 : {"greatest", &sql_least_greatest_propagate_statistics},
791 : {"ifthenelse", &sql_ifthenelse_propagate_statistics},
792 : {"nullif", &sql_nullif_propagate_statistics},
793 : {"coalesce", &sql_coalesce_propagate_statistics},
794 : {"casewhen", &sql_casewhen_propagate_statistics},
795 :
796 : /* time functions */
797 : {"century", &sql_century_propagate_statistics},
798 : {"decade", &sql_decade_propagate_statistics},
799 : {"year", &sql_year_propagate_statistics},
800 : {"quarter", &sql_quarter_propagate_statistics},
801 : {"month", &sql_month_propagate_statistics},
802 : {"day", &sql_day_propagate_statistics},
803 : {"dayofyear", &sql_dayofyear_propagate_statistics},
804 : {"weekofyear", &sql_weekofyear_propagate_statistics},
805 : {"usweekofyear", &sql_weekofyear_propagate_statistics},
806 : {"dayofweek", &sql_dayofweek_propagate_statistics},
807 : {"dayofmonth", &sql_day_propagate_statistics},
808 : {"week", &sql_weekofyear_propagate_statistics},
809 : {"hour", &sql_hour_propagate_statistics},
810 : {"minute", &sql_minute_propagate_statistics},
811 : {"second", &sql_second_propagate_statistics},
812 : {"epoch_ms", &sql_epoch_ms_propagate_statistics},
813 :
814 : /* aggregates */
815 : {"min", &sql_min_max_propagate_statistics},
816 : {"max", &sql_min_max_propagate_statistics},
817 : {"avg", &sql_avg_propagate_statistics},
818 : {"zero_or_one", &sql_zero_or_one_propagate_statistics}
819 : };
820 :
821 : void
822 314 : initialize_sql_functions_lookup(allocator *sa)
823 : {
824 314 : int nentries = sizeof(functions_list) / sizeof(functions_list[0]);
825 :
826 314 : sql_functions_lookup = hash_new(sa, nentries, (fkeyvalue)&hash_key);
827 11304 : for (int i = 0; i < nentries ; i++) {
828 10990 : int key = hash_key(functions_list[i].name);
829 :
830 10990 : hash_add(sql_functions_lookup, key, &(functions_list[i]));
831 : }
832 314 : }
|