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 38626 : sql_add_propagate_statistics(mvc *sql, sql_exp *e)
21 : {
22 38626 : list *l = e->l;
23 38626 : sql_exp *first = l->h->data, *second = l->h->next->data;
24 38626 : atom *lmax, *rmax, *lmin, *rmin, *res1 = NULL, *res2 = NULL;
25 38626 : str msg1 = NULL, msg2 = NULL;
26 38626 : prop *est;
27 :
28 38626 : if ((lmax = find_prop_and_get(first->p, PROP_MAX)) && (rmax = find_prop_and_get(second->p, PROP_MAX)) &&
29 16744 : (lmin = find_prop_and_get(first->p, PROP_MIN)) && (rmin = find_prop_and_get(second->p, PROP_MIN))) {
30 16743 : sql_subfunc *f = (sql_subfunc *)e->f;
31 :
32 16743 : if (strcmp(f->func->mod, "calc") == 0) {
33 16592 : res1 = atom_add(sql->sa, atom_copy(sql->sa, lmax), atom_copy(sql->sa, rmax));
34 16592 : 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 16743 : if (res1 && res2) { /* if the min/max pair overflows, then don't propagate */
89 15842 : if (atom_cmp(res1, res2) > 0) {
90 14569 : set_minmax_property(sql, e, PROP_MAX, res1);
91 14569 : set_minmax_property(sql, e, PROP_MIN, res2);
92 : } else {
93 1273 : set_minmax_property(sql, e, PROP_MAX, res2);
94 1273 : set_minmax_property(sql, e, PROP_MIN, res1);
95 : }
96 : }
97 16743 : freeException(msg1);
98 16743 : freeException(msg2);
99 : }
100 : /* propagate estimate */
101 38626 : if (!exp_is_atom(first) && exp_is_atom(second) && (est = find_prop(first->p, PROP_NUNIQUES))) {
102 15812 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
103 15812 : p->value.dval = est->value.dval;
104 22814 : } 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 38626 : }
109 :
110 : static void
111 11210 : sql_sub_propagate_statistics(mvc *sql, sql_exp *e)
112 : {
113 11210 : list *l = e->l;
114 11210 : sql_exp *first = l->h->data, *second = l->h->next->data;
115 11210 : atom *lmax, *rmax, *lmin, *rmin, *res1 = NULL, *res2 = NULL;
116 11210 : prop *est;
117 :
118 11210 : if ((lmax = find_prop_and_get(first->p, PROP_MAX)) && (rmax = find_prop_and_get(second->p, PROP_MAX)) &&
119 3828 : (lmin = find_prop_and_get(first->p, PROP_MIN)) && (rmin = find_prop_and_get(second->p, PROP_MIN))) {
120 3828 : sql_subfunc *f = (sql_subfunc *)e->f;
121 3828 : str msg1 = NULL, msg2 = NULL;
122 :
123 3828 : if (strcmp(f->func->mod, "calc") == 0) {
124 3792 : res1 = atom_sub(sql->sa, atom_copy(sql->sa, lmax), atom_copy(sql->sa, rmin));
125 3792 : res2 = atom_sub(sql->sa, atom_copy(sql->sa, lmin), atom_copy(sql->sa, rmax));
126 : } else {
127 36 : sql_subtype tp;
128 :
129 36 : assert(strcmp(f->func->mod, "mtime") == 0);
130 36 : if (strcmp(f->func->imp, "diff") == 0) {
131 24 : sql_subtype *t1 = exp_subtype(first);
132 :
133 24 : switch (t1->type->eclass) {
134 5 : case EC_DATE: {
135 5 : res1 = atom_int(sql->sa, sql_bind_localtype("int"), date_diff_imp((date)lmax->data.val.ival, (date)rmin->data.val.ival));
136 5 : res2 = atom_int(sql->sa, sql_bind_localtype("int"), date_diff_imp((date)lmin->data.val.ival, (date)rmax->data.val.ival));
137 5 : } break;
138 3 : case EC_TIME: {
139 3 : res1 = atom_int(sql->sa, sql_bind_localtype("lng"), daytime_diff((daytime)lmax->data.val.lval, (daytime)rmin->data.val.lval));
140 3 : res2 = atom_int(sql->sa, sql_bind_localtype("lng"), 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, sql_bind_localtype("lng"), TSDIFF((timestamp)lmax->data.val.lval, (timestamp)rmin->data.val.lval));
144 16 : res2 = atom_int(sql->sa, sql_bind_localtype("lng"), TSDIFF((timestamp)lmin->data.val.lval, (timestamp)rmax->data.val.lval));
145 16 : } break;
146 : default:
147 : break;
148 : }
149 12 : } 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 7 : } 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 6 : } else if (strcmp(f->func->imp, "time_sub_msec_interval") == 0) {
168 4 : daytime v1 = (daytime)lmax->data.val.lval, v2 = (daytime)lmin->data.val.lval,
169 4 : sub1 = time_sub_msec_interval(v1, rmin->data.val.lval),
170 4 : sub2 = time_sub_msec_interval(v2, rmax->data.val.lval);
171 :
172 4 : if (sub1 <= v1 && sub2 <= v2) { /* look for overflows */
173 4 : sql_find_subtype(&tp, "time", 0, 0);
174 4 : res1 = atom_general_ptr(sql->sa, &tp, &sub1);
175 4 : 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 3828 : if (res1 && res2) { /* if the min/max pair overflows, then don't propagate */
199 3827 : if (atom_cmp(res1, res2) > 0) {
200 3743 : set_minmax_property(sql, e, PROP_MAX, res1);
201 3743 : set_minmax_property(sql, e, PROP_MIN, res2);
202 : } else {
203 84 : set_minmax_property(sql, e, PROP_MAX, res2);
204 84 : set_minmax_property(sql, e, PROP_MIN, res1);
205 : }
206 : }
207 3828 : freeException(msg1);
208 3828 : freeException(msg2);
209 : }
210 : /* propagate estimate */
211 11210 : if (!exp_is_atom(first) && exp_is_atom(second) && (est = find_prop(first->p, PROP_NUNIQUES))) {
212 4019 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
213 4019 : p->value.dval = est->value.dval;
214 7191 : } else if (exp_is_atom(first) && !exp_is_atom(second) && (est = find_prop(second->p, PROP_NUNIQUES))) {
215 24 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
216 24 : p->value.dval = est->value.dval;
217 : }
218 11210 : }
219 :
220 : static void
221 14733 : sql_mul_propagate_statistics(mvc *sql, sql_exp *e)
222 : {
223 14733 : list *l = e->l;
224 14733 : sql_exp *first = l->h->data, *second = l->h->next->data;
225 14733 : atom *lmax, *rmax, *lmin, *rmin;
226 :
227 14733 : if ((lmax = find_prop_and_get(first->p, PROP_MAX)) && (rmax = find_prop_and_get(second->p, PROP_MAX)) &&
228 8817 : (lmin = find_prop_and_get(first->p, PROP_MIN)) && (rmin = find_prop_and_get(second->p, PROP_MIN))) {
229 8817 : atom *res1 = atom_mul(sql->sa, atom_copy(sql->sa, lmax), atom_copy(sql->sa, rmax));
230 8817 : atom *res2 = atom_mul(sql->sa, atom_copy(sql->sa, lmin), atom_copy(sql->sa, rmin));
231 :
232 8817 : if (res1 && res2) { /* if the min/max pair overflows, then don't propagate */
233 8746 : atom *zero1 = atom_zero_value(sql->sa, &(lmax->tpe)), *zero2 = atom_zero_value(sql->sa, &(rmax->tpe));
234 8746 : int cmp1 = atom_cmp(lmax, zero1), cmp2 = atom_cmp(lmin, zero1), cmp3 = atom_cmp(rmin, zero2), cmp4 = atom_cmp(rmax, zero2);
235 :
236 8746 : if (cmp1 >= 0 && cmp2 >= 0 && cmp3 >= 0 && cmp4 >= 0) { /* if all positive then propagate */
237 8708 : set_minmax_property(sql, e, PROP_MAX, res1);
238 8708 : set_minmax_property(sql, e, PROP_MIN, res2);
239 38 : } else if (cmp1 < 0 && cmp2 < 0 && cmp3 < 0 && cmp4 < 0) { /* if all negative propagate by swapping min and max */
240 7 : set_minmax_property(sql, e, PROP_MAX, res2);
241 7 : set_minmax_property(sql, e, PROP_MIN, res1);
242 : }
243 : }
244 : }
245 14733 : }
246 :
247 : static void
248 1375 : sql_div_propagate_statistics(mvc *sql, sql_exp *e)
249 : {
250 1375 : list *l = e->l;
251 1375 : sql_exp *first = l->h->data, *second = l->h->next->data;
252 1375 : atom *lmax, *rmax, *lmin, *rmin;
253 :
254 1375 : if ((lmax = find_prop_and_get(first->p, PROP_MAX)) && (rmax = find_prop_and_get(second->p, PROP_MAX)) &&
255 99 : (lmin = find_prop_and_get(first->p, PROP_MIN)) && (rmin = find_prop_and_get(second->p, PROP_MIN))) {
256 99 : atom *res1 = atom_div(sql->sa, atom_copy(sql->sa, lmax), atom_copy(sql->sa, rmin));
257 99 : atom *res2 = atom_div(sql->sa, atom_copy(sql->sa, lmin), atom_copy(sql->sa, rmax));
258 :
259 99 : if (res1 && res2) { /* on div by zero don't propagate */
260 76 : atom *zero1 = atom_zero_value(sql->sa, &(lmax->tpe)), *zero2 = atom_zero_value(sql->sa, &(rmax->tpe));
261 76 : int cmp1 = atom_cmp(lmax, zero1), cmp2 = atom_cmp(lmin, zero1), cmp3 = atom_cmp(rmin, zero2), cmp4 = atom_cmp(rmax, zero2);
262 :
263 76 : if (cmp1 >= 0 && cmp2 >= 0 && cmp3 >= 0 && cmp4 >= 0) { /* if all positive then propagate */
264 55 : set_minmax_property(sql, e, PROP_MAX, res1);
265 55 : set_minmax_property(sql, e, PROP_MIN, res2);
266 21 : } 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 1375 : }
273 :
274 : static void
275 369 : sql_extend_min_max(mvc *sql, sql_exp *e, sql_exp *first, sql_exp *second)
276 : {
277 369 : atom *lval, *rval;
278 :
279 369 : if ((lval = find_prop_and_get(first->p, PROP_MAX)) && (rval = find_prop_and_get(second->p, PROP_MAX)))
280 320 : set_minmax_property(sql, e, PROP_MAX, atom_cmp(lval, rval) > 0 ? lval : rval);
281 369 : if ((lval = find_prop_and_get(first->p, PROP_MIN)) && (rval = find_prop_and_get(second->p, PROP_MIN)))
282 326 : set_minmax_property(sql, e, PROP_MIN, atom_cmp(lval, rval) > 0 ? rval : lval);
283 369 : }
284 :
285 : static void
286 369 : sql_least_greatest_propagate_statistics(mvc *sql, sql_exp *e)
287 : {
288 369 : list *l = e->l;
289 369 : sql_extend_min_max(sql, e, l->h->data, l->h->next->data);
290 369 : }
291 :
292 : static void
293 20718 : sql_ifthenelse_propagate_statistics(mvc *sql, sql_exp *e)
294 : {
295 20718 : list *l = e->l;
296 20718 : sql_exp *first = l->h->next->data;
297 20718 : atom *curmin = NULL, *curmax = NULL, *lval;
298 20718 : unsigned int i = 0;
299 :
300 20718 : assert(list_length(l) >= 2);
301 20718 : if ((lval = find_prop_and_get(first->p, PROP_MAX)))
302 : curmax = lval;
303 20718 : if ((lval = find_prop_and_get(first->p, PROP_MIN)))
304 : curmin = lval;
305 36645 : for (node *n = l->h->next->next ; n && curmin && curmax ; n = n->next) {
306 15927 : if ((i & 1) || n == l->t) { /* the last expression, ie the result, must be included */
307 15927 : sql_exp *next = n->data;
308 15927 : if ((lval = find_prop_and_get(next->p, PROP_MAX))) {
309 7794 : curmax = atom_cmp(lval, curmax) > 0 ? lval : curmax;
310 : } else {
311 : curmax = NULL;
312 : }
313 15927 : if ((lval = find_prop_and_get(next->p, PROP_MIN))) {
314 7794 : curmin = atom_cmp(lval, curmin) > 0 ? curmin : lval;
315 : } else {
316 : curmin = NULL;
317 : }
318 : }
319 15927 : i++;
320 : }
321 :
322 20718 : if (curmin && curmax) {
323 7794 : set_minmax_property(sql, e, PROP_MAX, curmax);
324 7794 : set_minmax_property(sql, e, PROP_MIN, curmin);
325 : }
326 20718 : }
327 :
328 : static void
329 4204 : sql_casewhen_propagate_statistics(mvc *sql, sql_exp *e)
330 : {
331 4204 : list *l = e->l;
332 4204 : sql_exp *first = l->h->next->next->data;
333 4204 : atom *curmin = NULL, *curmax = NULL, *lval;
334 4204 : unsigned int i = 0;
335 :
336 4204 : assert(list_length(l) >= 3);
337 4204 : if ((lval = find_prop_and_get(first->p, PROP_MAX)))
338 : curmax = lval;
339 4204 : if ((lval = find_prop_and_get(first->p, PROP_MIN)))
340 : curmin = lval;
341 29697 : for (node *n = l->h->next->next->next ; n && curmin && curmax ; n = n->next) {
342 25493 : if ((i & 1) || n == l->t) { /* the last expression, ie the result, must be included */
343 14252 : sql_exp *next = n->data;
344 14252 : if ((lval = find_prop_and_get(next->p, PROP_MAX))) {
345 12109 : curmax = atom_cmp(lval, curmax) > 0 ? lval : curmax;
346 : } else {
347 : curmax = NULL;
348 : }
349 14252 : if ((lval = find_prop_and_get(next->p, PROP_MIN))) {
350 12109 : curmin = atom_cmp(lval, curmin) > 0 ? curmin : lval;
351 : } else {
352 : curmin = NULL;
353 : }
354 : }
355 25493 : i++;
356 : }
357 :
358 4204 : if (curmin && curmax) {
359 1292 : set_minmax_property(sql, e, PROP_MAX, curmax);
360 1292 : set_minmax_property(sql, e, PROP_MIN, curmin);
361 : }
362 4204 : }
363 :
364 : static void
365 92 : sql_nullif_propagate_statistics(mvc *sql, sql_exp *e)
366 : {
367 92 : list *l = e->l;
368 92 : sql_exp *first = l->h->data;
369 92 : atom *lval;
370 :
371 92 : if ((lval = find_prop_and_get(first->p, PROP_MAX)))
372 55 : set_minmax_property(sql, e, PROP_MAX, lval);
373 92 : if ((lval = find_prop_and_get(first->p, PROP_MIN)))
374 55 : set_minmax_property(sql, e, PROP_MIN, lval);
375 92 : }
376 :
377 : static void
378 3306 : sql_neg_propagate_statistics(mvc *sql, sql_exp *e)
379 : {
380 3306 : list *l = e->l;
381 3306 : sql_exp *first = l->h->data;
382 3306 : atom *lval;
383 3306 : prop *est;
384 :
385 3306 : if ((lval = find_prop_and_get(first->p, PROP_MIN))) {
386 3144 : atom *res = atom_copy(sql->sa, lval);
387 3144 : if ((res = atom_neg(sql->sa, res)))
388 3144 : set_minmax_property(sql, e, PROP_MAX, res);
389 : }
390 3306 : if ((lval = find_prop_and_get(first->p, PROP_MAX))) {
391 3142 : atom *res = atom_copy(sql->sa, lval);
392 3142 : if ((res = atom_neg(sql->sa, res)))
393 3142 : set_minmax_property(sql, e, PROP_MIN, res);
394 : }
395 3306 : if ((est = find_prop(first->p, PROP_NUNIQUES))) {
396 3202 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
397 3202 : p->value.dval = est->value.dval;
398 : }
399 3306 : }
400 :
401 : static void
402 17 : sql_sign_propagate_statistics(mvc *sql, sql_exp *e)
403 : {
404 17 : list *l = e->l;
405 17 : sql_exp *first = l->h->data;
406 17 : atom *omin, *omax;
407 17 : sql_subtype *bte = sql_bind_localtype("bte");
408 17 : bool properties_set = false;
409 :
410 17 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
411 11 : atom *zero1 = atom_zero_value(sql->sa, &(omin->tpe));
412 11 : int cmp1 = atom_cmp(omax, zero1), cmp2 = atom_cmp(omin, zero1);
413 :
414 11 : 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 11 : } else if (cmp1 > 0 && cmp2 > 0) {
419 4 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, bte, 1));
420 4 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, bte, 1));
421 4 : 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 5 : if (!properties_set) {
429 12 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, bte, 1));
430 12 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, bte, -1));
431 : }
432 17 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
433 17 : p->value.dval = 2;
434 17 : }
435 :
436 : static void
437 2073 : sql_abs_propagate_statistics(mvc *sql, sql_exp *e)
438 : {
439 2073 : list *l = e->l;
440 2073 : sql_exp *first = l->h->data;
441 2073 : atom *omin, *omax;
442 :
443 2073 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
444 1220 : atom *zero = atom_zero_value(sql->sa, &(omin->tpe));
445 1220 : int cmp1 = atom_cmp(omax, zero), cmp2 = atom_cmp(omin, zero);
446 :
447 1220 : if (cmp1 >= 0 && cmp2 >= 0) {
448 1019 : set_minmax_property(sql, e, PROP_MAX, omax);
449 1019 : set_minmax_property(sql, e, PROP_MIN, omin);
450 201 : } else if (cmp1 < 0 && cmp2 < 0) {
451 11 : atom *res1 = atom_copy(sql->sa, omin), *res2 = atom_copy(sql->sa, omax);
452 :
453 11 : if ((res1 = atom_absolute(sql->sa, res1)) && (res2 = atom_absolute(sql->sa, res2))) {
454 11 : set_minmax_property(sql, e, PROP_MAX, res1);
455 11 : 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 2073 : }
467 :
468 : static void
469 1389 : sql_coalesce_propagate_statistics(mvc *sql, sql_exp *e)
470 : {
471 1389 : list *l = e->l;
472 1389 : sql_exp *first = l->h->data;
473 1389 : atom *curmin = NULL, *curmax = NULL, *lval;
474 :
475 1389 : if ((lval = find_prop_and_get(first->p, PROP_MAX)))
476 : curmax = lval;
477 1389 : if ((lval = find_prop_and_get(first->p, PROP_MIN)))
478 : curmin = lval;
479 1719 : for (node *n = l->h->next ; n && curmin && curmax ; n = n->next) {
480 330 : sql_exp *next = n->data;
481 :
482 330 : if ((lval = find_prop_and_get(next->p, PROP_MAX))) {
483 166 : curmax = atom_cmp(lval, curmax) > 0 ? lval : curmax;
484 : } else {
485 : curmax = NULL;
486 : }
487 330 : if ((lval = find_prop_and_get(next->p, PROP_MIN))) {
488 166 : curmin = atom_cmp(lval, curmin) > 0 ? curmin : lval;
489 : } else {
490 : curmin = NULL;
491 : }
492 : }
493 :
494 1389 : if (curmin && curmax) {
495 113 : set_minmax_property(sql, e, PROP_MAX, curmax);
496 113 : set_minmax_property(sql, e, PROP_MIN, curmin);
497 : }
498 1389 : }
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 34 : sql_subtype *tp = exp_subtype(first);
556 34 : 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 30 : } else if (tp->type->eclass == EC_DATE) {
560 30 : nmin = date_year((date)omin->data.val.ival);
561 30 : 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 21 : sql_day_propagate_statistics(mvc *sql, sql_exp *e)
589 : {
590 21 : list *l = e->l;
591 21 : sql_exp *first = l->h->data;
592 21 : sql_subtype *tp = exp_subtype(first);
593 21 : const char *localtype = tp->type->eclass == EC_SEC ? "lng" : "int";
594 21 : atom *omin, *omax;
595 21 : lng nmin = 1, nmax = 31;
596 :
597 21 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
598 9 : if (tp->type->eclass == EC_SEC) {
599 0 : nmin = sql_day(omin->data.val.lval);
600 0 : nmax = sql_day(omax->data.val.lval);
601 : }
602 : }
603 :
604 21 : set_minmax_property(sql, e, PROP_MAX, atom_int(sql->sa, sql_bind_localtype(localtype), nmax));
605 21 : set_minmax_property(sql, e, PROP_MIN, atom_int(sql->sa, sql_bind_localtype(localtype), nmin));
606 21 : prop *p = e->p = prop_create(sql->sa, PROP_NUNIQUES, e->p);
607 21 : p->value.dval = 31;
608 21 : }
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 18 : nmax = 59999999;
679 18 : sql_find_subtype(&tp_res, "decimal", 8, 6);
680 : } else {
681 7 : 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 4 : 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 2 : case EC_TIME: {
704 2 : nmax = atom_int(sql->sa, sql_bind_localtype("lng"), daytime_to_msec_since_epoch((daytime)omax->data.val.lval));
705 2 : nmin = atom_int(sql->sa, sql_bind_localtype("lng"), daytime_to_msec_since_epoch((daytime)omin->data.val.lval));
706 2 : } break;
707 1 : case EC_TIMESTAMP: {
708 1 : nmax = atom_int(sql->sa, sql_bind_localtype("lng"), timestamp_to_msec_since_epoch((timestamp)omax->data.val.lval));
709 1 : nmin = atom_int(sql->sa, sql_bind_localtype("lng"), timestamp_to_msec_since_epoch((timestamp)omin->data.val.lval));
710 1 : } 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 4 : if (nmin && nmax) {
719 4 : set_minmax_property(sql, e, PROP_MAX, nmax);
720 4 : set_minmax_property(sql, e, PROP_MIN, nmin);
721 : }
722 : }
723 6 : }
724 :
725 : static void
726 3418 : sql_min_max_propagate_statistics(mvc *sql, sql_exp *e)
727 : {
728 3418 : list *l = e->l;
729 3418 : if (list_empty(l))
730 : return;
731 3418 : sql_exp *first = l->h->data;
732 3418 : atom *omin, *omax;
733 :
734 3418 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
735 1843 : set_minmax_property(sql, e, PROP_MAX, omax);
736 1843 : set_minmax_property(sql, e, PROP_MIN, omin);
737 : }
738 : }
739 :
740 : static void
741 1542 : sql_avg_propagate_statistics(mvc *sql, sql_exp *e)
742 : {
743 1542 : list *l = e->l;
744 1542 : sql_exp *first = l->h->data;
745 1542 : atom *omin, *omax;
746 :
747 1542 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
748 1249 : sql_subtype *etype = exp_subtype(e), *ftype = exp_subtype(first);
749 1249 : if (ftype && etype->type->base.id == ftype->type->base.id) { /* average on decimals or intervals */
750 65 : set_minmax_property(sql, e, PROP_MAX, omax);
751 65 : set_minmax_property(sql, e, PROP_MIN, omin);
752 1184 : } else if (ftype && etype) { /* average on integer types */
753 1184 : assert(etype->type->eclass == EC_FLT);
754 1184 : atom *min_cast = atom_copy(sql->sa, omin), *max_cast = atom_copy(sql->sa, omax);
755 1184 : if ((min_cast = atom_cast(sql->sa, min_cast, etype)) && (max_cast = atom_cast(sql->sa, max_cast, etype))) {
756 1169 : set_minmax_property(sql, e, PROP_MAX, max_cast);
757 1169 : set_minmax_property(sql, e, PROP_MIN, min_cast);
758 : }
759 : }
760 : }
761 1542 : }
762 :
763 : static void
764 2208 : sql_zero_or_one_propagate_statistics(mvc *sql, sql_exp *e)
765 : {
766 2208 : list *l = e->l;
767 2208 : sql_exp *first = l->h->data;
768 2208 : atom *omin, *omax;
769 :
770 2208 : if ((omin = find_prop_and_get(first->p, PROP_MIN)) && (omax = find_prop_and_get(first->p, PROP_MAX))) {
771 21 : set_minmax_property(sql, e, PROP_MAX, omax);
772 21 : set_minmax_property(sql, e, PROP_MIN, omin);
773 : }
774 2208 : }
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 335 : initialize_sql_functions_lookup(sql_allocator *sa)
823 : {
824 335 : int nentries = sizeof(functions_list) / sizeof(functions_list[0]);
825 :
826 335 : sql_functions_lookup = hash_new(sa, nentries, (fkeyvalue)&hash_key);
827 12060 : for (int i = 0; i < nentries ; i++) {
828 11725 : int key = hash_key(functions_list[i].name);
829 :
830 11725 : hash_add(sql_functions_lookup, key, &(functions_list[i]));
831 : }
832 335 : }
|