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_propagate.h"
15 : #include "rel_basetable.h"
16 : #include "rel_exp.h"
17 : #include "rel_select.h"
18 : #include "rel_updates.h"
19 : #include "sql_partition.h"
20 :
21 : extern sql_rel *rel_list(allocator *sa, sql_rel *l, sql_rel *r);
22 :
23 : static sql_exp*
24 275 : rel_generate_anti_expression(mvc *sql, sql_rel **anti_rel, sql_table *mt, sql_table *pt)
25 : {
26 275 : sql_exp* res = NULL;
27 :
28 275 : *anti_rel = rel_basetable(sql, pt, pt->base.name);
29 :
30 275 : if (isPartitionedByColumnTable(mt)) {
31 240 : int colr = mt->part.pcol->colnr;
32 :
33 240 : res = rel_base_bind_colnr(sql, *anti_rel, colr);
34 240 : return res;
35 35 : } else if (isPartitionedByExpressionTable(mt)) {
36 35 : *anti_rel = rel_project(sql->sa, *anti_rel, NULL);
37 35 : if (!(res = rel_parse_val(sql, mt->s, mt->part.pexp->exp, NULL, sql->emode, (*anti_rel)->l)))
38 : return NULL;
39 35 : set_processed(*anti_rel);
40 : } else {
41 0 : assert(0);
42 : }
43 35 : (*anti_rel)->exps = new_exp_list(sql->sa);
44 35 : append((*anti_rel)->exps, res);
45 35 : res = exp_ref(sql, res);
46 35 : return res;
47 : }
48 :
49 : static sql_rel*
50 116 : rel_create_common_relation(mvc *sql, sql_rel *rel, sql_table *t)
51 : {
52 116 : if (isPartitionedByColumnTable(t)) {
53 56 : return rel_dup(rel->r);
54 60 : } else if (isPartitionedByExpressionTable(t)) {
55 14 : sql_rel *inserts;
56 14 : list *l = new_exp_list(sql->sa);
57 :
58 14 : rel->r = rel_project(sql->sa, rel->r, l);
59 14 : set_processed((sql_rel*)rel->r);
60 14 : inserts = ((sql_rel*)(rel->r))->l;
61 41 : for (node *n = ol_first_node(t->columns), *m = inserts->exps->h; n && m; n = n->next, m = m->next) {
62 27 : sql_column *col = n->data;
63 27 : sql_exp *before = m->data, *help;
64 :
65 27 : help = exp_ref(sql, before);
66 27 : exp_setalias(help, before->alias.label, t->base.name, col->base.name);
67 27 : list_append(l, help);
68 : }
69 14 : return rel_dup(rel->r);
70 : }
71 : return NULL;
72 : }
73 :
74 : static sql_exp*
75 352 : rel_generate_anti_insert_expression(mvc *sql, sql_rel **anti_rel, sql_table *t)
76 : {
77 352 : sql_exp* res = NULL;
78 :
79 352 : if ((*anti_rel)->op != op_project && (*anti_rel)->op != op_basetable && (*anti_rel)->op != op_table) {
80 20 : sql_rel *inserts; /* In a nested partition case the operation is a op_select, then a projection must be created */
81 20 : list *l = new_exp_list(sql->sa);
82 20 : *anti_rel = rel_project(sql->sa, *anti_rel, l);
83 :
84 20 : inserts = (*anti_rel)->l;
85 20 : if (inserts->op != op_project && inserts->op != op_union && inserts->op != op_basetable && inserts->op != op_table)
86 20 : inserts = inserts->l;
87 60 : for (node *n = ol_first_node(t->columns), *m = inserts->exps->h; n && m; n = n->next, m = m->next) {
88 40 : sql_column *col = n->data;
89 40 : sql_exp *before = m->data, *help;
90 :
91 40 : help = exp_ref(sql, before);
92 40 : exp_setalias(help, before->alias.label, t->base.name, col->base.name);
93 40 : list_append(l, help);
94 : }
95 : }
96 :
97 690 : if (isPartitionedByColumnTable(t)) {
98 338 : int colr = t->part.pcol->colnr;
99 338 : res = list_fetch((*anti_rel)->exps, colr);
100 14 : } else if (isPartitionedByExpressionTable(t)) {
101 14 : *anti_rel = rel_project(sql->sa, *anti_rel, rel_projections(sql, *anti_rel, NULL, 1, 1));
102 14 : if (!(res = rel_parse_val(sql, t->s, t->part.pexp->exp, NULL, sql->emode, (*anti_rel)->l)))
103 : return NULL;
104 14 : exp_label(sql->sa, res, ++sql->label);
105 14 : append((*anti_rel)->exps, res);
106 : } else {
107 0 : assert(0);
108 : }
109 352 : res = exp_ref(sql, res);
110 352 : return res;
111 : }
112 :
113 : static sql_exp *
114 598 : generate_partition_limits(sql_query *query, sql_rel **r, symbol *s, sql_subtype tpe, bool nilok)
115 : {
116 598 : mvc *sql = query->sql;
117 598 : if (!s) {
118 : return NULL;
119 598 : } else if (s->token == SQL_NULL && !nilok) {
120 8 : return sql_error(sql, 02, SQLSTATE(42000) "ALTER TABLE: range bound cannot be null");
121 590 : } else if (s->token == SQL_MINVALUE) {
122 39 : atom *amin = atom_general(sql->sa, &tpe, NULL, 0);
123 39 : if (!amin) {
124 0 : char *err = sql_subtype_string(sql->ta, &tpe);
125 0 : if (!err)
126 0 : return sql_error(sql, 02, SQLSTATE(HY013) MAL_MALLOC_FAIL);
127 0 : sql_error(sql, 02, SQLSTATE(42000) "ALTER TABLE: absolute minimum value not available for %s type", err);
128 0 : return NULL;
129 : }
130 39 : return exp_atom(sql->sa, amin);
131 551 : } else if (s->token == SQL_MAXVALUE) {
132 38 : atom *amax = atom_general(sql->sa, &tpe, NULL, 0);
133 38 : if (!amax) {
134 0 : char *err = sql_subtype_string(sql->ta, &tpe);
135 0 : if (!err)
136 0 : return sql_error(sql, 02, SQLSTATE(HY013) MAL_MALLOC_FAIL);
137 0 : sql_error(sql, 02, SQLSTATE(42000) "ALTER TABLE: absolute maximum value not available for %s type", err);
138 0 : return NULL;
139 : }
140 38 : return exp_atom(sql->sa, amax);
141 : } else {
142 513 : exp_kind ek = {type_value, card_value, FALSE};
143 513 : sql_exp *e = rel_value_exp2(query, r, s, sql_sel | sql_values, ek);
144 :
145 513 : if (!e)
146 : return NULL;
147 512 : return exp_check_type(sql, &tpe, r ? *r : NULL, e, type_equal);
148 : }
149 : }
150 :
151 : static sql_exp*
152 209 : create_range_partition_anti_rel(sql_query* query, sql_table *mt, sql_table *pt, bit with_nills, sql_exp *pmin, sql_exp *pmax, bool all_ranges, bool max_equal_min)
153 : {
154 209 : mvc *sql = query->sql;
155 209 : sql_rel *anti_rel;
156 209 : sql_exp *aggr, *anti_exp = NULL, *anti_le, *e1, *e2, *anti_nils;
157 209 : sql_subfunc *cf = sql_bind_func(sql, "sys", "count", sql_bind_localtype("void"), NULL, F_AGGR, true, true);
158 209 : sql_subtype tpe;
159 :
160 209 : find_partition_type(&tpe, mt);
161 :
162 209 : anti_le = rel_generate_anti_expression(sql, &anti_rel, mt, pt);
163 209 : anti_nils = rel_unop_(sql, anti_rel, anti_le, "sys", "isnull", card_value);
164 209 : set_has_no_nil(anti_nils);
165 209 : if (pmin && pmax) {
166 : /* type could have changed because of partition expression */
167 195 : if (!(anti_le = exp_check_type(sql, &tpe, NULL, anti_le, type_equal)))
168 : return NULL;
169 195 : if (all_ranges) { /*if holds all values in range, don't generate the range comparison */
170 13 : assert(!with_nills);
171 : } else {
172 182 : sql_exp *range1, *range2;
173 :
174 182 : e1 = exp_copy(sql, pmin);
175 182 : if (!(e1 = exp_check_type(sql, &tpe, NULL, e1, type_equal)))
176 : return NULL;
177 :
178 182 : if (max_equal_min) {
179 3 : anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_notequal);
180 : } else {
181 179 : e2 = exp_copy(sql, pmax);
182 179 : if (!(e2 = exp_check_type(sql, &tpe, NULL, e2, type_equal)))
183 : return NULL;
184 :
185 179 : range1 = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_lt);
186 179 : range2 = exp_compare(sql->sa, exp_copy(sql, anti_le), e2, cmp_gte);
187 179 : anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), range1),
188 : list_append(new_exp_list(sql->sa), range2), 0);
189 : }
190 : }
191 182 : if (!with_nills) {
192 175 : anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
193 175 : if (anti_exp)
194 162 : anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
195 : list_append(new_exp_list(sql->sa), anti_nils), 0);
196 : else
197 : anti_exp = anti_nils;
198 : }
199 : } else {
200 14 : anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
201 : }
202 :
203 209 : anti_rel = rel_select(sql->sa, anti_rel, anti_exp);
204 209 : set_processed(anti_rel);
205 209 : anti_rel = rel_groupby(sql, anti_rel, NULL);
206 209 : aggr = exp_aggr(sql->sa, NULL, cf, 0, 0, anti_rel->card, 0);
207 209 : (void) rel_groupby_add_aggr(sql, anti_rel, aggr);
208 209 : set_processed(anti_rel);
209 209 : exp_label(sql->sa, aggr, ++sql->label);
210 :
211 209 : return exp_rel(sql, anti_rel);
212 : }
213 :
214 : static sql_exp*
215 66 : create_list_partition_anti_rel(sql_query* query, sql_table *mt, sql_table *pt, bit with_nills, list *anti_exps)
216 : {
217 66 : mvc *sql = query->sql;
218 66 : sql_rel *anti_rel;
219 66 : sql_exp *aggr, *anti_exp, *anti_le, *anti_nils;
220 66 : sql_subfunc *cf = sql_bind_func(sql, "sys", "count", sql_bind_localtype("void"), NULL, F_AGGR, true, true);
221 66 : sql_subtype tpe;
222 :
223 66 : find_partition_type(&tpe, mt);
224 :
225 66 : anti_le = rel_generate_anti_expression(sql, &anti_rel, mt, pt);
226 66 : anti_nils = rel_unop_(sql, anti_rel, anti_le, "sys", "isnull", card_value);
227 :
228 66 : set_has_no_nil(anti_nils);
229 66 : if (list_length(anti_exps) > 0) {
230 63 : sql_exp *ae = anti_exps->h->data;
231 63 : sql_subtype *ntpe = exp_subtype(ae);
232 : /* function may need conversion */
233 63 : if (!(anti_le = exp_check_type(sql, ntpe, NULL, anti_le, type_equal)))
234 : return NULL;
235 63 : anti_exp = exp_in(sql->sa, anti_le, anti_exps, cmp_notin);
236 63 : if (!with_nills) {
237 54 : anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
238 54 : anti_exp = exp_or(sql->sa, append(new_exp_list(sql->sa), anti_exp),
239 : append(new_exp_list(sql->sa), anti_nils), 0);
240 : }
241 : } else {
242 3 : assert(with_nills);
243 3 : anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
244 : }
245 :
246 66 : anti_rel = rel_select(sql->sa, anti_rel, anti_exp);
247 66 : set_processed(anti_rel);
248 66 : anti_rel = rel_groupby(sql, anti_rel, NULL);
249 66 : aggr = exp_aggr(sql->sa, NULL, cf, 0, 0, anti_rel->card, 0);
250 66 : (void) rel_groupby_add_aggr(sql, anti_rel, aggr);
251 66 : set_processed(anti_rel);
252 66 : exp_label(sql->sa, aggr, ++sql->label);
253 66 : return exp_rel(sql, anti_rel);
254 : }
255 :
256 : static sql_exp *
257 7 : add_check_count(mvc *sql, sql_exp *a, sql_exp *b)
258 : {
259 7 : if (!a)
260 : return b;
261 7 : sql_subtype *lng = sql_bind_localtype("lng");
262 7 : sql_subfunc *add = sql_bind_func_result(sql, "sys", "sql_add", F_FUNC, true, lng, 2, lng, lng);
263 7 : return exp_binop(sql->sa, a, b, add);
264 : }
265 :
266 : static sql_rel *
267 275 : propagate_validation_to_upper_tables(sql_query* query, sql_table *mt, sql_table *pt, sql_rel *rel, sql_exp *check_count)
268 : {
269 275 : mvc *sql = query->sql;
270 275 : sql_part *it = NULL;
271 :
272 282 : for (sql_table *prev = mt ; prev; prev = it?it->t:NULL) {
273 282 : if ((it=partition_find_part(sql->session->tr, prev, NULL)) == NULL)
274 : break;
275 7 : sql_part *spt = it;
276 7 : if (spt) {
277 7 : sql_subtype tp;
278 7 : find_partition_type(&tp, it->t);
279 :
280 7 : if (isRangePartitionTable(it->t)) {
281 1 : int tpe = tp.type->localtype;
282 1 : int (*atomcmp)(const void *, const void *) = ATOMcompare(tpe);
283 1 : const void *nil = ATOMnilptr(tpe);
284 1 : sql_exp *e1 = NULL, *e2 = NULL;
285 1 : bool found_all = false, max_equal_min = false;
286 :
287 1 : if (atomcmp(spt->part.range.minvalue, nil) != 0 && atomcmp(spt->part.range.maxvalue, nil) != 0) {
288 1 : max_equal_min = ATOMcmp(tpe, spt->part.range.maxvalue, spt->part.range.minvalue) == 0;
289 1 : e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, spt->part.range.minvalue));
290 1 : if (!max_equal_min)
291 1 : e2 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, spt->part.range.maxvalue));
292 : } else {
293 0 : assert(spt->with_nills);
294 0 : found_all = is_bit_nil(spt->with_nills);
295 : }
296 1 : if (!found_all || !spt->with_nills) {
297 1 : sql_exp *nres = create_range_partition_anti_rel(query, it->t, pt, spt->with_nills, e1, e2, false, max_equal_min);
298 1 : check_count = add_check_count(sql, check_count, nres);
299 : }
300 6 : } else if (isListPartitionTable(it->t)) {
301 6 : list *exps = new_exp_list(sql->sa);
302 28 : for (node *n = spt->part.values->h ; n ; n = n->next) {
303 22 : sql_part_value *next = (sql_part_value*) n->data;
304 22 : sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, next->value));
305 22 : list_append(exps, e1);
306 : }
307 6 : sql_exp *nres = create_list_partition_anti_rel(query, it->t, pt, spt->with_nills, exps);
308 6 : check_count = add_check_count(sql, check_count, nres);
309 : } else {
310 0 : assert(0);
311 : }
312 : } else { /* the sql_part should exist */
313 : assert(0);
314 : }
315 : }
316 275 : if (check_count) {
317 268 : append(rel->exps, check_count);
318 : } else {
319 7 : append(rel->exps, exp_atom_lng(sql->sa, 0));
320 : }
321 275 : return rel;
322 : }
323 :
324 : sql_rel *
325 221 : rel_alter_table_add_partition_range(sql_query* query, sql_table *mt, sql_table *pt, char *sname, char *tname, char *sname2,
326 : char *tname2, symbol* min, symbol* max, bit with_nills, int update)
327 : {
328 221 : mvc *sql = query->sql;
329 221 : sql_rel *rel_psm = rel_create(sql->sa);
330 221 : list *exps = new_exp_list(sql->sa);
331 221 : sql_exp *pmin, *pmax;
332 221 : sql_subtype tpe;
333 221 : bool all_ranges = false;
334 221 : sql_exp *check_count = NULL;
335 :
336 221 : if (!rel_psm || !exps)
337 : return NULL;
338 :
339 221 : find_partition_type(&tpe, mt);
340 :
341 221 : assert((!min && !max && with_nills) || (min && max));
342 221 : if (min && max) {
343 207 : pmin = generate_partition_limits(query, &rel_psm, min, tpe, false);
344 207 : pmax = generate_partition_limits(query, &rel_psm, max, tpe, false);
345 207 : if (!pmin || !pmax)
346 : return NULL;
347 201 : if (min->token == SQL_MINVALUE && max->token == SQL_MAXVALUE && with_nills)
348 7 : with_nills = bit_nil; /* holds all values in range */
349 382 : all_ranges = (min->token == SQL_MINVALUE && max->token == SQL_MAXVALUE);
350 : } else {
351 14 : pmin = exp_atom(sql->sa, atom_general(sql->sa, &tpe, NULL, 0));
352 14 : pmax = exp_atom(sql->sa, atom_general(sql->sa, &tpe, NULL, 0));
353 : }
354 :
355 : /* generate the psm statement */
356 215 : append(exps, exp_atom_clob(sql->sa, sname));
357 215 : append(exps, exp_atom_clob(sql->sa, tname));
358 215 : assert((sname2 && tname2) || (!sname2 && !tname2));
359 215 : if (sname2) {
360 215 : append(exps, exp_atom_clob(sql->sa, sname2));
361 215 : append(exps, exp_atom_clob(sql->sa, tname2));
362 : }
363 215 : append(exps, pmin);
364 215 : append(exps, pmax);
365 215 : append(exps, is_bit_nil(with_nills) ? exp_atom(sql->sa, atom_general(sql->sa, sql_bind_localtype("bit"), NULL, 0)) : exp_atom_bool(sql->sa, with_nills));
366 215 : append(exps, exp_atom_int(sql->sa, update));
367 215 : rel_psm->l = NULL;
368 215 : rel_psm->r = NULL;
369 215 : rel_psm->op = op_ddl;
370 215 : rel_psm->flag = ddl_alter_table_add_range_partition;
371 215 : rel_psm->exps = exps;
372 215 : rel_psm->card = CARD_MULTI;
373 215 : rel_psm->nrcols = 0;
374 :
375 215 : if (!is_bit_nil(with_nills)) {
376 208 : bool min_max_equal = false;
377 208 : if (pmin && pmax && pmin->type == e_atom && pmax->type == e_atom && pmin->l && pmax->l) {
378 162 : atom *e1 = pmin->l, *e2 = pmax->l;
379 162 : min_max_equal = ATOMcmp(tpe.type->localtype, &e1->data.val, &e2->data.val) == 0;
380 : }
381 236 : check_count = create_range_partition_anti_rel(query, mt, pt, with_nills, (min && max) ? pmin : NULL, (min && max) ? pmax : NULL, all_ranges, min_max_equal);
382 : }
383 215 : return propagate_validation_to_upper_tables(query, mt, pt, rel_psm, check_count); /* this adds the check_count to the rel_psm exps list */
384 : }
385 :
386 : sql_rel *
387 62 : rel_alter_table_add_partition_list(sql_query *query, sql_table *mt, sql_table *pt, char *sname, char *tname, char *sname2,
388 : char *tname2, dlist* values, bit with_nills, int update)
389 : {
390 62 : mvc *sql = query->sql;
391 62 : sql_rel *rel_psm = rel_create(sql->sa);
392 62 : list *exps = new_exp_list(sql->sa), *lvals = new_exp_list(sql->sa);
393 62 : sql_subtype tpe;
394 62 : sql_exp *converted_values = NULL;
395 :
396 62 : if (!rel_psm || !exps)
397 : return NULL;
398 :
399 62 : find_partition_type(&tpe, mt);
400 :
401 62 : if (values) {
402 241 : for (dnode *dn = values->h; dn ; dn = dn->next) { /* parse the atoms and generate the expressions */
403 184 : symbol* next = dn->data.sym;
404 184 : sql_exp *pnext = generate_partition_limits(query, &rel_psm, next, tpe, true);
405 :
406 184 : if (!pnext)
407 : return NULL;
408 183 : if (next->token == SQL_NULL)
409 1 : return sql_error(sql, 02, SQLSTATE(42000) "ALTER TABLE: a list value cannot be null");
410 182 : append(lvals, pnext);
411 : }
412 : }
413 :
414 60 : converted_values = exp_values(sql->sa, lvals);
415 60 : if (!(converted_values = exp_values_set_supertype(sql, converted_values, &tpe)))
416 : return NULL;
417 239 : for (node *n = ((list*)converted_values->f)->h ; n ; n = n->next)
418 179 : if (!(n->data = exp_check_type(sql, &tpe, NULL, n->data, type_equal)))
419 : return NULL;
420 :
421 : /* generate the psm statement */
422 60 : append(exps, exp_atom_clob(sql->sa, sname));
423 60 : append(exps, exp_atom_clob(sql->sa, tname));
424 60 : assert((sname2 && tname2) || (!sname2 && !tname2));
425 60 : if (sname2) {
426 60 : append(exps, exp_atom_clob(sql->sa, sname2));
427 60 : append(exps, exp_atom_clob(sql->sa, tname2));
428 : }
429 60 : append(exps, exp_atom_bool(sql->sa, with_nills));
430 60 : append(exps, exp_atom_int(sql->sa, update));
431 60 : rel_psm->l = NULL;
432 60 : rel_psm->r = NULL;
433 60 : rel_psm->op = op_ddl;
434 60 : rel_psm->flag = ddl_alter_table_add_list_partition;
435 60 : rel_psm->exps = exps;
436 60 : rel_psm->card = CARD_MULTI;
437 60 : rel_psm->nrcols = 0;
438 :
439 60 : sql_exp *check_count = create_list_partition_anti_rel(query, mt, pt, with_nills, exps_copy(sql, (list*)converted_values->f));
440 60 : rel_psm = propagate_validation_to_upper_tables(query, mt, pt, rel_psm, check_count); /* this adds check_count to the rel_psm exps list */
441 60 : rel_psm->exps = list_merge(rel_psm->exps, converted_values->f, (fdup)NULL);
442 60 : return rel_psm;
443 : }
444 :
445 : static sql_rel* rel_change_base_table(mvc* sql, sql_rel* rel, sql_table* oldt, sql_table* newt);
446 :
447 : static sql_exp*
448 218 : exp_change_column_table(mvc *sql, sql_exp *e, sql_table* oldt, sql_table* newt)
449 : {
450 218 : if (mvc_highwater(sql))
451 0 : return sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
452 :
453 218 : if (!e)
454 : return NULL;
455 218 : switch(e->type) {
456 0 : case e_psm: {
457 0 : if (e->flag & PSM_RETURN) {
458 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
459 0 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
460 0 : } else if (e->flag & PSM_WHILE) {
461 0 : e->l = exp_change_column_table(sql, e->l, oldt, newt);
462 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
463 0 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
464 0 : } else if (e->flag & PSM_IF) {
465 0 : e->l = exp_change_column_table(sql, e->l, oldt, newt);
466 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
467 0 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
468 0 : if (e->f)
469 0 : for (node *n = ((list*)e->f)->h ; n ; n = n->next)
470 0 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
471 0 : } else if (e->flag & PSM_REL) {
472 0 : rel_change_base_table(sql, e->l, oldt, newt);
473 0 : } else if (e->flag & PSM_EXCEPTION) {
474 0 : e->l = exp_change_column_table(sql, e->l, oldt, newt);
475 : }
476 : } break;
477 3 : case e_convert: {
478 3 : e->l = exp_change_column_table(sql, e->l, oldt, newt);
479 3 : } break;
480 44 : case e_atom: {
481 44 : if (e->f)
482 0 : for (node *n = ((list*)e->f)->h ; n ; n = n->next)
483 0 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
484 : } break;
485 7 : case e_aggr:
486 : case e_func: {
487 7 : if (e->l)
488 21 : for (node *n = ((list*)e->l)->h ; n ; n = n->next)
489 14 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
490 7 : if (e->type == e_func && e->r)
491 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
492 0 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
493 : } break;
494 143 : case e_column: {
495 143 : if (!strcmp(e->l, oldt->base.name))
496 143 : e->l = sa_strdup(sql->sa, newt->base.name);
497 : } break;
498 21 : case e_cmp: {
499 21 : if (e->flag == cmp_in || e->flag == cmp_notin) {
500 2 : e->l = exp_change_column_table(sql, e->l, oldt, newt);
501 6 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
502 4 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
503 19 : } else if (e->flag == cmp_or || e->flag == cmp_filter) {
504 0 : for (node *n = ((list*)e->l)->h ; n ; n = n->next)
505 0 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
506 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
507 0 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
508 : } else {
509 19 : if (e->l)
510 19 : e->l = exp_change_column_table(sql, e->l, oldt, newt);
511 19 : if (e->r)
512 19 : e->r = exp_change_column_table(sql, e->r, oldt, newt);
513 19 : if (e->f)
514 0 : e->f = exp_change_column_table(sql, e->f, oldt, newt);
515 : }
516 : } break;
517 : }
518 218 : if (exp_relname(e) && !strcmp(exp_relname(e), oldt->base.name))
519 159 : e->alias.rname = newt->base.name;
520 : return e;
521 : }
522 :
523 : static sql_rel*
524 69 : rel_change_base_table(mvc* sql, sql_rel* rel, sql_table* oldt, sql_table* newt)
525 : {
526 69 : if (mvc_highwater(sql))
527 0 : return sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
528 :
529 69 : if (!rel)
530 : return NULL;
531 :
532 69 : if (rel->exps) {
533 194 : for (node *n = rel->exps->h ; n ; n = n->next)
534 125 : n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
535 69 : list_hash_clear(rel->exps);
536 : }
537 :
538 69 : switch (rel->op) {
539 24 : case op_basetable:
540 24 : if (rel->l == oldt)
541 24 : rel->l = newt;
542 : break;
543 0 : case op_table:
544 0 : if (IS_TABLE_PROD_FUNC(rel->flag) || rel->flag == TABLE_FROM_RELATION) {
545 0 : if (rel->l)
546 0 : rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
547 : }
548 : break;
549 0 : case op_join:
550 : case op_left:
551 : case op_right:
552 : case op_full:
553 : case op_semi:
554 : case op_anti:
555 : case op_union:
556 : case op_inter:
557 : case op_except:
558 : case op_insert:
559 : case op_update:
560 : case op_delete:
561 : case op_merge:
562 0 : if (rel->l)
563 0 : rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
564 0 : if (rel->r)
565 0 : rel->r = rel_change_base_table(sql, rel->r, oldt, newt);
566 : break;
567 0 : case op_munion:
568 0 : assert(rel->l);
569 0 : for (node *n = ((list*)rel->l)->h; n; n = n->next)
570 0 : n->data = rel_change_base_table(sql, n->data, oldt, newt);
571 : break;
572 45 : case op_groupby:
573 : case op_project:
574 : case op_select:
575 : case op_topn:
576 : case op_sample:
577 : case op_truncate:
578 45 : if (rel->l)
579 45 : rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
580 : break;
581 0 : case op_ddl:
582 0 : if (rel->flag == ddl_output || rel->flag == ddl_create_seq || rel->flag == ddl_alter_seq || rel->flag == ddl_alter_table || rel->flag == ddl_create_table || rel->flag == ddl_create_view) {
583 0 : if (rel->l)
584 0 : rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
585 : } else if (rel->flag == ddl_list || rel->flag == ddl_exception) {
586 0 : if (rel->l)
587 0 : rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
588 0 : if (rel->r)
589 0 : rel->r = rel_change_base_table(sql, rel->r, oldt, newt);
590 : }
591 : break;
592 : }
593 : return rel;
594 : }
595 :
596 : static sql_rel *
597 17 : rel_truncate_duplicate(mvc *sql, sql_rel *table, sql_rel *ori)
598 : {
599 17 : sql_rel *r = rel_create(sql->sa);
600 :
601 17 : r->exps = exps_copy(sql, ori->exps);
602 17 : r->op = op_truncate;
603 17 : r->l = table;
604 17 : r->r = NULL;
605 17 : return r;
606 : }
607 :
608 : static sql_rel*
609 21 : rel_generate_subdeletes(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
610 : {
611 21 : int just_one = 1;
612 21 : sql_rel *sel = NULL;
613 :
614 56 : for (node *n = t->members->h; n; n = n->next) {
615 35 : sql_part *pt = (sql_part *) n->data;
616 35 : sql_table *sub = find_sql_table_id(sql->session->tr, t->s, pt->member);
617 35 : sql_rel *s1, *dup = NULL;
618 :
619 69 : if (!update_allowed(sql, sub, sub->base.name, is_delete(rel->op) ? "DELETE": "TRUNCATE",
620 35 : is_delete(rel->op) ? "delete": "truncate", is_delete(rel->op) ? 1 : 2))
621 : return NULL;
622 :
623 35 : if (rel->r) {
624 8 : dup = rel_copy(sql, rel->r, 1);
625 8 : dup = rel_change_base_table(sql, dup, t, sub);
626 : }
627 35 : if (is_delete(rel->op))
628 18 : s1 = rel_delete(sql->sa, rel_basetable(sql, sub, sub->base.name), dup);
629 : else
630 17 : s1 = rel_truncate_duplicate(sql, rel_basetable(sql, sub, sub->base.name), rel);
631 35 : if (just_one == 0) {
632 14 : sel = rel_list(sql->sa, sel, s1);
633 : } else {
634 : sel = s1;
635 : just_one = 0;
636 : }
637 35 : (*changes)++;
638 : }
639 21 : rel_destroy(rel);
640 21 : return sel;
641 : }
642 :
643 : static sql_rel*
644 11 : rel_generate_subupdates(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
645 : {
646 11 : int just_one = 1;
647 11 : sql_rel *sel = NULL;
648 :
649 27 : for (node *n = t->members->h; n; n = n->next) {
650 16 : sql_part *pt = (sql_part *) n->data;
651 16 : sql_table *sub = find_sql_table_id(sql->session->tr, t->s, pt->member);
652 16 : sql_rel *s1, *dup = NULL;
653 16 : list *uexps = exps_copy(sql, rel->exps), *checked_updates = new_exp_list(sql->sa);
654 16 : sql_rel *bt = rel_basetable(sql, sub, sub->base.name);
655 :
656 16 : if (!update_allowed(sql, sub, sub->base.name, "UPDATE", "update", 0))
657 : return NULL;
658 :
659 48 : for (node *n = uexps->h ; n ; n = n->next) {
660 32 : sql_exp *e = (sql_exp *) n->data;
661 32 : const char *cname = exp_name(e);
662 :
663 32 : if (cname[0] != '%') { /* Skip TID column */
664 16 : sql_column *c = mvc_bind_column(sql, sub, cname);
665 :
666 16 : if (!c)
667 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42S22) "UPDATE: no such column '%s.%s'\n", sub->base.name, cname);
668 16 : rel_base_use(sql, bt, c->colnr);
669 16 : if (!(e = update_check_column(sql, sub, c, e, rel, c->base.name, "UPDATE")))
670 : return NULL;
671 : }
672 32 : list_append(checked_updates, e);
673 : }
674 :
675 16 : if (rel->r) {
676 16 : dup = rel_copy(sql, rel->r, 1);
677 16 : dup = rel_change_base_table(sql, dup, t, sub);
678 : }
679 :
680 48 : for (node *ne = checked_updates->h ; ne ; ne = ne->next)
681 32 : ne->data = exp_change_column_table(sql, (sql_exp*) ne->data, t, sub);
682 :
683 16 : s1 = rel_update(sql, bt, dup, NULL, checked_updates);
684 16 : if (just_one == 0) {
685 5 : sel = rel_list(sql->sa, sel, s1);
686 : } else {
687 : sel = s1;
688 : just_one = 0;
689 : }
690 16 : (*changes)++;
691 : }
692 11 : rel_destroy(rel);
693 11 : return sel;
694 : }
695 :
696 : static sql_rel*
697 102 : rel_generate_subinserts(sql_query *query, sql_rel *rel, sql_table *t, int *changes,
698 : const char *operation, const char *desc)
699 : {
700 102 : mvc *sql = query->sql;
701 102 : int just_one = 1, found_nils = 0, found_all_range_values = 0;
702 102 : sql_rel *new_table = NULL, *sel = NULL, *anti_rel = NULL;
703 102 : sql_exp *anti_exp = NULL, *anti_le = NULL, *anti_nils = NULL, *accum = NULL, *aggr = NULL;
704 102 : list *anti_exps = new_exp_list(sql->sa);
705 102 : sql_subfunc *cf = sql_bind_func(sql, "sys", "count", sql_bind_localtype("void"), NULL, F_AGGR, true, true);
706 102 : char buf[BUFSIZ];
707 102 : sql_subtype tp;
708 :
709 102 : find_partition_type(&tp, t);
710 102 : if (isPartitionedByColumnTable(t)) {
711 92 : anti_rel = rel_dup(rel->r);
712 10 : } else if (isPartitionedByExpressionTable(t)) {
713 10 : anti_rel = rel_create_common_relation(sql, rel, t);
714 10 : if (!anti_rel)
715 : return NULL;
716 : } else {
717 0 : assert(0);
718 : }
719 102 : anti_le = rel_generate_anti_insert_expression(sql, &anti_rel, t);
720 :
721 309 : for (node *n = t->members->h; n; n = n->next) {
722 209 : sql_part *pt = (sql_part *) n->data;
723 209 : sql_table *sub = find_sql_table_id(sql->session->tr, t->s, pt->member);
724 209 : sql_rel *s1 = NULL, *dup = NULL;
725 209 : sql_exp *le = NULL;
726 :
727 209 : if (!insert_allowed(sql, sub, sub->base.name, "INSERT", "insert"))
728 2 : return NULL;
729 :
730 207 : if (isPartitionedByColumnTable(t)) {
731 190 : dup = rel_dup(rel->r);
732 190 : le = rel_generate_anti_insert_expression(sql, &dup, t);
733 17 : } else if (isPartitionedByExpressionTable(t)) {
734 17 : dup = rel_dup(anti_rel);
735 17 : le = anti_le;
736 : } else {
737 0 : assert(0);
738 : }
739 :
740 207 : if (isRangePartitionTable(t)) {
741 114 : sql_exp *range = NULL, *full_range = NULL;
742 114 : int tpe = tp.type->localtype;
743 114 : int (*atomcmp)(const void *, const void *) = ATOMcompare(tpe);
744 114 : const void *nil = ATOMnilptr(tpe);
745 114 : bool is_min_nil = atomcmp(pt->part.range.minvalue, nil) == 0, is_max_nil = atomcmp(pt->part.range.maxvalue, nil) == 0;
746 :
747 114 : if (is_min_nil && is_max_nil) {
748 8 : found_all_range_values |= (pt->with_nills != 1);
749 8 : found_nils |= is_bit_nil(pt->with_nills);
750 8 : if (pt->with_nills == false) { /* full range without nils */
751 2 : sql_exp *nils = rel_unop_(sql, dup, le, "sys", "isnull", card_value);
752 :
753 2 : set_has_no_nil(nils);
754 2 : nils = exp_compare(sql->sa, nils, exp_atom_bool(sql->sa, 0), cmp_equal);
755 2 : full_range = range = nils; /* ugh */
756 : }
757 106 : } else if (is_min_nil) {
758 1 : full_range = range = exp_compare(sql->sa, le, exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue)), cmp_lt);
759 105 : } else if (is_max_nil) {
760 5 : full_range = range = exp_compare(sql->sa, le, exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue)), cmp_gte);
761 : } else {
762 100 : bool max_equal_min = ATOMcmp(tpe, pt->part.range.maxvalue, pt->part.range.minvalue) == 0;
763 :
764 100 : full_range = range = max_equal_min ?
765 100 : exp_compare(sql->sa, le, exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue)), cmp_equal) :
766 100 : exp_compare2(sql->sa, le, exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue)),
767 : exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue)), 1, 0);
768 : }
769 114 : if (pt->with_nills == true) { /* handle the nulls case */
770 19 : sql_exp *nils = rel_unop_(sql, dup, le, "sys", "isnull", card_value);
771 :
772 19 : set_has_no_nil(nils);
773 19 : nils = exp_compare(sql->sa, nils, exp_atom_bool(sql->sa, 1), cmp_equal);
774 19 : if (full_range) {
775 14 : full_range = exp_or(sql->sa, list_append(new_exp_list(sql->sa), full_range),
776 : list_append(new_exp_list(sql->sa), nils), 0);
777 : } else {
778 : full_range = nils;
779 : }
780 : found_nils = 1;
781 : }
782 114 : if (accum && range) {
783 55 : accum = exp_or(sql->sa, list_append(new_exp_list(sql->sa), accum),
784 55 : list_append(new_exp_list(sql->sa), exp_copy(sql, range)), 0);
785 59 : } else if (range) {
786 53 : accum = exp_copy(sql, range);
787 : }
788 114 : if (full_range) {
789 113 : dup = rel_select(sql->sa, dup, full_range);
790 113 : set_processed(dup);
791 : }
792 93 : } else if (isListPartitionTable(t)) {
793 93 : sql_exp *ein = NULL;
794 :
795 93 : if (list_length(pt->part.values)) { /* if the partition holds non-null values */
796 91 : list *exps = new_exp_list(sql->sa);
797 364 : for (node *nn = pt->part.values->h ; nn ; nn = nn->next) {
798 273 : sql_part_value *next = (sql_part_value*) nn->data;
799 273 : sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, next->value));
800 273 : list_append(exps, e1);
801 273 : list_append(anti_exps, exp_copy(sql, e1));
802 : }
803 91 : ein = exp_in(sql->sa, le, exps, cmp_in);
804 : } else {
805 2 : assert(pt->with_nills);
806 : }
807 93 : if (pt->with_nills) { /* handle the nulls case */
808 23 : sql_exp *nils = rel_unop_(sql, dup, le, "sys", "isnull", card_value);
809 :
810 23 : set_has_no_nil(nils);
811 23 : nils = exp_compare(sql->sa, nils, exp_atom_bool(sql->sa, 1), cmp_equal);
812 23 : if (ein) {
813 21 : ein = exp_or(sql->sa, list_append(new_exp_list(sql->sa), ein),
814 : list_append(new_exp_list(sql->sa), nils), 0);
815 : } else {
816 : ein = nils;
817 : }
818 : found_nils = 1;
819 : }
820 93 : dup = rel_select(sql->sa, dup, ein);
821 93 : set_processed(dup);
822 : } else {
823 0 : assert(0);
824 : }
825 :
826 207 : new_table = rel_basetable(sql, sub, sub->base.name);
827 207 : rel_base_use_all(query->sql, new_table);
828 207 : new_table = rewrite_basetable(query->sql, new_table);
829 207 : new_table->p = prop_create(sql->sa, PROP_USED, new_table->p); /* don't create infinite loops in the optimizer */
830 :
831 207 : if (isPartitionedByExpressionTable(t)) {
832 17 : sql_exp *del;
833 17 : dup = rel_project(sql->sa, dup, rel_projections(sql, dup, NULL, 1, 1));
834 17 : del = list_fetch(dup->exps, list_length(dup->exps) - 1);
835 17 : list_remove_data(dup->exps, NULL, del);
836 : }
837 :
838 207 : s1 = rel_insert(query->sql, new_table, dup);
839 207 : if (just_one == 0) {
840 107 : sel = rel_list(sql->sa, sel, s1);
841 : } else {
842 : sel = s1;
843 : just_one = 0;
844 : }
845 207 : (*changes)++;
846 : }
847 :
848 100 : if (!found_all_range_values || !found_nils) {
849 : /* generate the exception */
850 98 : if (isRangePartitionTable(t)) {
851 54 : if (accum) {
852 52 : set_anti(accum);
853 52 : anti_exp = accum;
854 : }
855 44 : } else if (isListPartitionTable(t)) {
856 44 : if (list_length(anti_exps))
857 43 : anti_exp = exp_in(sql->sa, anti_le, anti_exps, cmp_notin);
858 : } else {
859 0 : assert(0);
860 : }
861 98 : if (!found_nils) {
862 57 : assert(anti_exp);
863 57 : anti_nils = rel_unop_(sql, NULL, anti_le, "sys", "isnull", card_value);
864 57 : set_has_no_nil(anti_nils);
865 57 : anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
866 57 : anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
867 : list_append(new_exp_list(sql->sa), anti_nils), 0);
868 41 : } else if (!anti_exp) {
869 3 : anti_nils = rel_unop_(sql, NULL, exp_copy(sql, anti_le), "sys", "isnull", card_value);
870 3 : set_has_no_nil(anti_nils);
871 3 : anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
872 : }
873 : /* generate a count aggregation for the values not present in any of the partitions */
874 98 : anti_rel = rel_select(sql->sa, anti_rel, anti_exp);
875 98 : set_processed(anti_rel);
876 98 : anti_rel = rel_groupby(sql, anti_rel, NULL);
877 98 : aggr = exp_aggr(sql->sa, NULL, cf, 0, 0, anti_rel->card, 0);
878 98 : (void) rel_groupby_add_aggr(sql, anti_rel, aggr);
879 98 : set_processed(anti_rel);
880 98 : exp_label(sql->sa, aggr, ++sql->label);
881 :
882 98 : aggr = exp_ref(sql, aggr);
883 196 : snprintf(buf, BUFSIZ, "%s: the %s violates the partition %s of values", operation, desc,
884 98 : isRangePartitionTable(t) ? "range (NB higher limit exclusive)" : "list");
885 :
886 98 : sql_exp *exception = exp_exception(sql->sa, aggr, buf);
887 98 : sel = rel_exception(query->sql->sa, sel, anti_rel, list_append(new_exp_list(query->sql->sa), exception));
888 : }
889 100 : rel_destroy(rel);
890 100 : return sel;
891 : }
892 :
893 : static sql_rel*
894 102 : rel_propagate_insert(sql_query *query, sql_rel *rel, sql_table *t, int *changes)
895 : {
896 102 : return rel_generate_subinserts(query, rel, t, changes, "INSERT", "insert");
897 : }
898 :
899 : static sql_rel*
900 21 : rel_propagate_delete(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
901 : {
902 21 : return rel_generate_subdeletes(sql, rel, t, changes);
903 : }
904 :
905 : static bool
906 11 : update_move_across_partitions(sql_rel *rel, sql_table *t)
907 : {
908 33 : for (node *n = ((sql_rel*)rel->r)->exps->h; n; n = n->next) {
909 22 : sql_exp* exp = (sql_exp*) n->data;
910 22 : if (exp->type == e_column && exp->l && exp->r && !strcmp((char*)exp->l, t->base.name)) {
911 11 : char* colname = (char*)exp->r;
912 :
913 11 : if (isPartitionedByColumnTable(t)) {
914 8 : if (!strcmp(colname, t->part.pcol->base.name))
915 : return true;
916 3 : } else if (isPartitionedByExpressionTable(t)) {
917 6 : for (node *nn = t->part.pexp->cols->h; nn; nn = nn->next) {
918 3 : int next = *(int*) nn->data;
919 3 : sql_column *col = find_sql_column(t, colname);
920 3 : if (col && next == col->colnr)
921 : return true;
922 : }
923 : } else {
924 0 : assert(0);
925 : }
926 : }
927 : }
928 : return false;
929 : }
930 :
931 : static sql_rel*
932 11 : rel_propagate_update(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
933 : {
934 11 : bool found_partition_col = update_move_across_partitions(rel, t);
935 11 : sql_rel *sel = NULL;
936 :
937 11 : if (!found_partition_col) { /* easy scenario where the partitioned column is not being updated, just propagate */
938 11 : sel = rel_generate_subupdates(sql, rel, t, changes);
939 : } else { /* harder scenario, has to insert and delete across partitions. */
940 : /*sql_exp *exception = NULL;
941 : sql_rel *inserts = NULL, *deletes = NULL, *anti_rel = NULL;
942 :
943 : deletes = rel_generate_subdeletes(sql, rel, t, changes);
944 : inserts = rel_generate_subinserts(query, rel, &anti_rel, &exception, t, changes, "UPDATE", "update");
945 : inserts = rel_exception(sql->sa, inserts, anti_rel, list_append(new_exp_list(sql->sa), exception));
946 : return rel_list(sql->sa, deletes, inserts);*/
947 0 : assert(0);
948 : }
949 11 : return sel;
950 : }
951 :
952 : static sql_rel*
953 106 : rel_subtable_insert(sql_query *query, sql_rel *rel, sql_table *t, int *changes)
954 : {
955 106 : mvc *sql = query->sql;
956 106 : sql_part *upper = partition_find_part(sql->session->tr, t, NULL);
957 106 : if (!upper)
958 : return NULL;
959 106 : sql_part *pt = upper;
960 106 : sql_rel *anti_dup = rel_create_common_relation(sql, rel, upper->t), *left = rel->l;
961 106 : if (!anti_dup)
962 : return NULL;
963 60 : sql_exp *anti_exp = NULL, *anti_le = rel_generate_anti_insert_expression(sql, &anti_dup, upper->t), *aggr = NULL,
964 60 : *exception = NULL, *anti_nils = NULL;
965 60 : list *anti_exps = new_exp_list(sql->sa);
966 60 : sql_subfunc *cf = sql_bind_func(sql, "sys", "count", sql_bind_localtype("void"), NULL, F_AGGR, true, true);
967 60 : char buf[BUFSIZ];
968 60 : bool found_nils = false, found_all_range_values = false;
969 60 : sql_subtype tp;
970 :
971 60 : find_partition_type(&tp, upper->t);
972 60 : if (isRangePartitionTable(upper->t)) {
973 37 : int tpe = tp.type->localtype;
974 37 : int (*atomcmp)(const void *, const void *) = ATOMcompare(tpe);
975 37 : const void *nil = ATOMnilptr(tpe);
976 :
977 37 : if (pt->with_nills == true || is_bit_nil(pt->with_nills))
978 : found_nils = true;
979 :
980 37 : if (atomcmp(pt->part.range.minvalue, nil) == 0) {
981 7 : if (atomcmp(pt->part.range.maxvalue, nil) == 0) {
982 7 : found_all_range_values = pt->with_nills != 1;
983 7 : if (pt->with_nills == true) {
984 4 : anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
985 4 : set_has_no_nil(anti_nils);
986 4 : anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
987 : }
988 : } else {
989 0 : sql_exp *e2 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue));
990 0 : anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e2, cmp_gte);
991 : }
992 : } else {
993 30 : if (atomcmp(pt->part.range.maxvalue, nil) == 0) {
994 2 : sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue));
995 2 : anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_lt);
996 : } else {
997 28 : sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue));
998 28 : bool max_equal_min = ATOMcmp(tpe, pt->part.range.maxvalue, pt->part.range.minvalue) == 0;
999 :
1000 28 : if (max_equal_min) {
1001 0 : anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_notequal);
1002 : } else {
1003 28 : sql_exp *e2 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue)),
1004 28 : *range1 = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_lt),
1005 28 : *range2 = exp_compare(sql->sa, exp_copy(sql, anti_le), e2, cmp_gte);
1006 :
1007 28 : anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), range1),
1008 : list_append(new_exp_list(sql->sa), range2), 0);
1009 : }
1010 : }
1011 : }
1012 37 : if (!pt->with_nills) { /* handle the nulls case */
1013 30 : anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
1014 30 : set_has_no_nil(anti_nils);
1015 30 : anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
1016 30 : if (anti_exp)
1017 28 : anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
1018 : list_append(new_exp_list(sql->sa), anti_nils), 0);
1019 : else
1020 : anti_exp = anti_nils;
1021 : }
1022 23 : } else if (isListPartitionTable(upper->t)) {
1023 23 : if (list_length(pt->part.values)) { /* if the partition holds non-null values */
1024 91 : for (node *n = pt->part.values->h ; n ; n = n->next) {
1025 69 : sql_part_value *next = (sql_part_value*) n->data;
1026 69 : sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, next->value));
1027 69 : list_append(anti_exps, exp_copy(sql, e1));
1028 : }
1029 22 : anti_exp = exp_in(sql->sa, exp_copy(sql, anti_le), anti_exps, cmp_notin);
1030 :
1031 22 : if (!pt->with_nills) { /* handle the nulls case */
1032 18 : anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
1033 18 : set_has_no_nil(anti_nils);
1034 18 : anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
1035 18 : anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
1036 : list_append(new_exp_list(sql->sa), anti_nils), 0);
1037 : }
1038 : } else {
1039 1 : assert(pt->with_nills);
1040 1 : anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
1041 1 : set_has_no_nil(anti_nils);
1042 1 : anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
1043 : }
1044 : } else {
1045 0 : assert(0);
1046 : }
1047 :
1048 60 : if (!found_all_range_values || !found_nils) {
1049 : /* generate a count aggregation for the values not present in any of the partitions */
1050 59 : anti_dup = rel_select(sql->sa, anti_dup, anti_exp);
1051 59 : set_processed(anti_dup);
1052 59 : anti_dup = rel_groupby(sql, anti_dup, NULL);
1053 59 : aggr = exp_aggr(sql->sa, NULL, cf, 0, 0, anti_dup->card, 0);
1054 59 : (void) rel_groupby_add_aggr(sql, anti_dup, aggr);
1055 59 : exp_label(sql->sa, aggr, ++sql->label);
1056 59 : set_processed(anti_dup);
1057 :
1058 : /* generate the exception */
1059 59 : aggr = exp_ref(sql, aggr);
1060 118 : snprintf(buf, BUFSIZ, "INSERT: table %s.%s is part of merge table %s.%s and the insert violates the "
1061 59 : "partition %s of values", t->s->base.name, t->base.name, upper->t->s->base.name,
1062 59 : upper->t->base.name, isRangePartitionTable(upper->t) ? "range" : "list");
1063 59 : exception = exp_exception(sql->sa, aggr, buf);
1064 :
1065 59 : left->p = prop_create(sql->sa, PROP_USED, left->p);
1066 59 : (*changes)++;
1067 :
1068 59 : rel = rel_exception(sql->sa, rel, anti_dup, list_append(new_exp_list(sql->sa), exception));
1069 : }
1070 : return rel;
1071 : }
1072 :
1073 : static sql_rel*
1074 60 : rel_find_propagate( sql_rel *rel)
1075 : {
1076 60 : if (is_ddl(rel->op) && rel->flag == ddl_list)
1077 59 : return rel->r;
1078 1 : if (is_ddl(rel->op) && rel->flag == ddl_exception)
1079 0 : return rel->r;
1080 1 : assert(is_insert(rel->op));
1081 : return rel;
1082 : }
1083 :
1084 : sql_rel *
1085 1113 : rel_propagate(sql_query *query, sql_rel *rel, int *changes)
1086 : {
1087 1113 : mvc *sql = query->sql;
1088 1113 : bool isSubtable = false;
1089 1113 : sql_rel *l = rel->l, *propagate = rel;
1090 :
1091 1113 : if (l->op == op_basetable) {
1092 1097 : sql_table *t = l->l;
1093 :
1094 1097 : if (partition_find_part(sql->session->tr, t, NULL) && !find_prop(l->p, PROP_USED)) {
1095 200 : isSubtable = true;
1096 200 : if (is_insert(rel->op)) { /* insertion directly to sub-table (must do validation) */
1097 106 : sql_rel *nrel = rel_subtable_insert(query, rel, t, changes);
1098 106 : if (!nrel)
1099 : return rel;
1100 60 : rel = nrel;
1101 60 : propagate = rel_find_propagate(nrel);
1102 60 : isSubtable = (rel != propagate);
1103 : }
1104 : }
1105 1051 : if (isMergeTable(t)) {
1106 134 : assert(list_length(t->members));
1107 134 : if (is_delete(propagate->op) || is_truncate(propagate->op)) { /* propagate deletions to the partitions */
1108 21 : rel = rel_propagate_delete(sql, rel, t, changes);
1109 113 : } else if (isRangePartitionTable(t) || isListPartitionTable(t)) {
1110 113 : if (is_insert(propagate->op)) { /* on inserts create a selection for each partition */
1111 102 : if (isSubtable) {
1112 2 : rel->r = rel_propagate_insert(query, propagate, t, changes);
1113 : } else {
1114 100 : rel = rel_propagate_insert(query, rel, t, changes);
1115 : }
1116 11 : } else if (is_update(propagate->op)) { /* for updates propagate like in deletions */
1117 11 : rel = rel_propagate_update(sql, rel, t, changes);
1118 : } else {
1119 0 : assert(0);
1120 : }
1121 : } else {
1122 0 : assert(0);
1123 : }
1124 : }
1125 : }
1126 : return rel;
1127 : }
|