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