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_setname(sql->sa, help, 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_setname(sql->sa, help, 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 201 : exp_change_column_table(mvc *sql, sql_exp *e, sql_table* oldt, sql_table* newt)
449 : {
450 201 : if (mvc_highwater(sql))
451 0 : return sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
452 :
453 201 : if (!e)
454 : return NULL;
455 201 : 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 126 : case e_column: {
495 126 : if (!strcmp(e->l, oldt->base.name))
496 126 : 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 201 : if (exp_relname(e) && !strcmp(exp_relname(e), oldt->base.name))
519 142 : exp_setname(sql->sa, e, newt->base.name, NULL);
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 177 : for (node *n = rel->exps->h ; n ; n = n->next)
534 108 : 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 45 : case op_groupby:
568 : case op_project:
569 : case op_select:
570 : case op_topn:
571 : case op_sample:
572 : case op_truncate:
573 45 : if (rel->l)
574 45 : rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
575 : break;
576 0 : case op_ddl:
577 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) {
578 0 : if (rel->l)
579 0 : rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
580 : } else if (rel->flag == ddl_list || rel->flag == ddl_exception) {
581 0 : if (rel->l)
582 0 : rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
583 0 : if (rel->r)
584 0 : rel->r = rel_change_base_table(sql, rel->r, oldt, newt);
585 : }
586 : break;
587 : }
588 : return rel;
589 : }
590 :
591 : static sql_rel *
592 17 : rel_truncate_duplicate(mvc *sql, sql_rel *table, sql_rel *ori)
593 : {
594 17 : sql_rel *r = rel_create(sql->sa);
595 :
596 17 : r->exps = exps_copy(sql, ori->exps);
597 17 : r->op = op_truncate;
598 17 : r->l = table;
599 17 : r->r = NULL;
600 17 : return r;
601 : }
602 :
603 : static sql_rel*
604 21 : rel_generate_subdeletes(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
605 : {
606 21 : int just_one = 1;
607 21 : sql_rel *sel = NULL;
608 :
609 56 : for (node *n = t->members->h; n; n = n->next) {
610 35 : sql_part *pt = (sql_part *) n->data;
611 35 : sql_table *sub = find_sql_table_id(sql->session->tr, t->s, pt->member);
612 35 : sql_rel *s1, *dup = NULL;
613 :
614 69 : if (!update_allowed(sql, sub, sub->base.name, is_delete(rel->op) ? "DELETE": "TRUNCATE",
615 35 : is_delete(rel->op) ? "delete": "truncate", is_delete(rel->op) ? 1 : 2))
616 : return NULL;
617 :
618 35 : if (rel->r) {
619 8 : dup = rel_copy(sql, rel->r, 1);
620 8 : dup = rel_change_base_table(sql, dup, t, sub);
621 : }
622 35 : if (is_delete(rel->op))
623 18 : s1 = rel_delete(sql->sa, rel_basetable(sql, sub, sub->base.name), dup);
624 : else
625 17 : s1 = rel_truncate_duplicate(sql, rel_basetable(sql, sub, sub->base.name), rel);
626 35 : if (just_one == 0) {
627 14 : sel = rel_list(sql->sa, sel, s1);
628 : } else {
629 : sel = s1;
630 : just_one = 0;
631 : }
632 35 : (*changes)++;
633 : }
634 21 : rel_destroy(rel);
635 21 : return sel;
636 : }
637 :
638 : static sql_rel*
639 11 : rel_generate_subupdates(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
640 : {
641 11 : int just_one = 1;
642 11 : sql_rel *sel = NULL;
643 :
644 27 : for (node *n = t->members->h; n; n = n->next) {
645 16 : sql_part *pt = (sql_part *) n->data;
646 16 : sql_table *sub = find_sql_table_id(sql->session->tr, t->s, pt->member);
647 16 : sql_rel *s1, *dup = NULL;
648 16 : list *uexps = exps_copy(sql, rel->exps), *checked_updates = new_exp_list(sql->sa);
649 16 : sql_rel *bt = rel_basetable(sql, sub, sub->base.name);
650 :
651 16 : if (!update_allowed(sql, sub, sub->base.name, "UPDATE", "update", 0))
652 : return NULL;
653 :
654 48 : for (node *n = uexps->h ; n ; n = n->next) {
655 32 : sql_exp *e = (sql_exp *) n->data;
656 32 : const char *cname = exp_name(e);
657 :
658 32 : if (cname[0] != '%') { /* Skip TID column */
659 16 : sql_column *c = mvc_bind_column(sql, sub, cname);
660 :
661 16 : if (!c)
662 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42S22) "UPDATE: no such column '%s.%s'\n", sub->base.name, cname);
663 16 : rel_base_use(sql, bt, c->colnr);
664 16 : if (!(e = update_check_column(sql, sub, c, e, rel, c->base.name, "UPDATE")))
665 : return NULL;
666 : }
667 32 : list_append(checked_updates, e);
668 : }
669 :
670 16 : if (rel->r) {
671 16 : dup = rel_copy(sql, rel->r, 1);
672 16 : dup = rel_change_base_table(sql, dup, t, sub);
673 : }
674 :
675 48 : for (node *ne = checked_updates->h ; ne ; ne = ne->next)
676 32 : ne->data = exp_change_column_table(sql, (sql_exp*) ne->data, t, sub);
677 :
678 16 : s1 = rel_update(sql, bt, dup, NULL, checked_updates);
679 16 : if (just_one == 0) {
680 5 : sel = rel_list(sql->sa, sel, s1);
681 : } else {
682 : sel = s1;
683 : just_one = 0;
684 : }
685 16 : (*changes)++;
686 : }
687 11 : rel_destroy(rel);
688 11 : return sel;
689 : }
690 :
691 : static sql_rel*
692 102 : rel_generate_subinserts(sql_query *query, sql_rel *rel, sql_table *t, int *changes,
693 : const char *operation, const char *desc)
694 : {
695 102 : mvc *sql = query->sql;
696 102 : int just_one = 1, found_nils = 0, found_all_range_values = 0;
697 102 : sql_rel *new_table = NULL, *sel = NULL, *anti_rel = NULL;
698 102 : sql_exp *anti_exp = NULL, *anti_le = NULL, *anti_nils = NULL, *accum = NULL, *aggr = NULL;
699 102 : list *anti_exps = new_exp_list(sql->sa);
700 102 : sql_subfunc *cf = sql_bind_func(sql, "sys", "count", sql_bind_localtype("void"), NULL, F_AGGR, true, true);
701 102 : char buf[BUFSIZ];
702 102 : sql_subtype tp;
703 :
704 102 : find_partition_type(&tp, t);
705 102 : if (isPartitionedByColumnTable(t)) {
706 92 : anti_rel = rel_dup(rel->r);
707 10 : } else if (isPartitionedByExpressionTable(t)) {
708 10 : anti_rel = rel_create_common_relation(sql, rel, t);
709 10 : if (!anti_rel)
710 : return NULL;
711 : } else {
712 0 : assert(0);
713 : }
714 102 : anti_le = rel_generate_anti_insert_expression(sql, &anti_rel, t);
715 :
716 309 : for (node *n = t->members->h; n; n = n->next) {
717 209 : sql_part *pt = (sql_part *) n->data;
718 209 : sql_table *sub = find_sql_table_id(sql->session->tr, t->s, pt->member);
719 209 : sql_rel *s1 = NULL, *dup = NULL;
720 209 : sql_exp *le = NULL;
721 :
722 209 : if (!insert_allowed(sql, sub, sub->base.name, "INSERT", "insert"))
723 2 : return NULL;
724 :
725 207 : if (isPartitionedByColumnTable(t)) {
726 190 : dup = rel_dup(rel->r);
727 190 : le = rel_generate_anti_insert_expression(sql, &dup, t);
728 17 : } else if (isPartitionedByExpressionTable(t)) {
729 17 : dup = rel_dup(anti_rel);
730 17 : le = anti_le;
731 : } else {
732 0 : assert(0);
733 : }
734 :
735 207 : if (isRangePartitionTable(t)) {
736 114 : sql_exp *range = NULL, *full_range = NULL;
737 114 : int tpe = tp.type->localtype;
738 114 : int (*atomcmp)(const void *, const void *) = ATOMcompare(tpe);
739 114 : const void *nil = ATOMnilptr(tpe);
740 114 : bool is_min_nil = atomcmp(pt->part.range.minvalue, nil) == 0, is_max_nil = atomcmp(pt->part.range.maxvalue, nil) == 0;
741 :
742 114 : if (is_min_nil && is_max_nil) {
743 8 : found_all_range_values |= (pt->with_nills != 1);
744 8 : found_nils |= is_bit_nil(pt->with_nills);
745 8 : if (pt->with_nills == false) { /* full range without nils */
746 2 : sql_exp *nils = rel_unop_(sql, dup, le, "sys", "isnull", card_value);
747 :
748 2 : set_has_no_nil(nils);
749 2 : nils = exp_compare(sql->sa, nils, exp_atom_bool(sql->sa, 0), cmp_equal);
750 2 : full_range = range = nils; /* ugh */
751 : }
752 106 : } else if (is_min_nil) {
753 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);
754 105 : } else if (is_max_nil) {
755 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);
756 : } else {
757 100 : bool max_equal_min = ATOMcmp(tpe, pt->part.range.maxvalue, pt->part.range.minvalue) == 0;
758 :
759 100 : full_range = range = max_equal_min ?
760 100 : exp_compare(sql->sa, le, exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue)), cmp_equal) :
761 100 : exp_compare2(sql->sa, le, exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue)),
762 : exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue)), 1, 0);
763 : }
764 114 : if (pt->with_nills == true) { /* handle the nulls case */
765 19 : sql_exp *nils = rel_unop_(sql, dup, le, "sys", "isnull", card_value);
766 :
767 19 : set_has_no_nil(nils);
768 19 : nils = exp_compare(sql->sa, nils, exp_atom_bool(sql->sa, 1), cmp_equal);
769 19 : if (full_range) {
770 14 : full_range = exp_or(sql->sa, list_append(new_exp_list(sql->sa), full_range),
771 : list_append(new_exp_list(sql->sa), nils), 0);
772 : } else {
773 : full_range = nils;
774 : }
775 : found_nils = 1;
776 : }
777 114 : if (accum && range) {
778 55 : accum = exp_or(sql->sa, list_append(new_exp_list(sql->sa), accum),
779 55 : list_append(new_exp_list(sql->sa), exp_copy(sql, range)), 0);
780 59 : } else if (range) {
781 53 : accum = exp_copy(sql, range);
782 : }
783 114 : if (full_range) {
784 113 : dup = rel_select(sql->sa, dup, full_range);
785 113 : set_processed(dup);
786 : }
787 93 : } else if (isListPartitionTable(t)) {
788 93 : sql_exp *ein = NULL;
789 :
790 93 : if (list_length(pt->part.values)) { /* if the partition holds non-null values */
791 91 : list *exps = new_exp_list(sql->sa);
792 364 : for (node *nn = pt->part.values->h ; nn ; nn = nn->next) {
793 273 : sql_part_value *next = (sql_part_value*) nn->data;
794 273 : sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, next->value));
795 273 : list_append(exps, e1);
796 273 : list_append(anti_exps, exp_copy(sql, e1));
797 : }
798 91 : ein = exp_in(sql->sa, le, exps, cmp_in);
799 : } else {
800 2 : assert(pt->with_nills);
801 : }
802 93 : if (pt->with_nills) { /* handle the nulls case */
803 23 : sql_exp *nils = rel_unop_(sql, dup, le, "sys", "isnull", card_value);
804 :
805 23 : set_has_no_nil(nils);
806 23 : nils = exp_compare(sql->sa, nils, exp_atom_bool(sql->sa, 1), cmp_equal);
807 23 : if (ein) {
808 21 : ein = exp_or(sql->sa, list_append(new_exp_list(sql->sa), ein),
809 : list_append(new_exp_list(sql->sa), nils), 0);
810 : } else {
811 : ein = nils;
812 : }
813 : found_nils = 1;
814 : }
815 93 : dup = rel_select(sql->sa, dup, ein);
816 93 : set_processed(dup);
817 : } else {
818 0 : assert(0);
819 : }
820 :
821 207 : new_table = rel_basetable(sql, sub, sub->base.name);
822 207 : rel_base_use_all(query->sql, new_table);
823 207 : new_table = rewrite_basetable(query->sql, new_table);
824 207 : new_table->p = prop_create(sql->sa, PROP_USED, new_table->p); /* don't create infinite loops in the optimizer */
825 :
826 207 : if (isPartitionedByExpressionTable(t)) {
827 17 : sql_exp *del;
828 17 : dup = rel_project(sql->sa, dup, rel_projections(sql, dup, NULL, 1, 1));
829 17 : del = list_fetch(dup->exps, list_length(dup->exps) - 1);
830 17 : list_remove_data(dup->exps, NULL, del);
831 : }
832 :
833 207 : s1 = rel_insert(query->sql, new_table, dup);
834 207 : if (just_one == 0) {
835 107 : sel = rel_list(sql->sa, sel, s1);
836 : } else {
837 : sel = s1;
838 : just_one = 0;
839 : }
840 207 : (*changes)++;
841 : }
842 :
843 100 : if (!found_all_range_values || !found_nils) {
844 : /* generate the exception */
845 98 : if (isRangePartitionTable(t)) {
846 54 : if (accum) {
847 52 : set_anti(accum);
848 52 : anti_exp = accum;
849 : }
850 44 : } else if (isListPartitionTable(t)) {
851 44 : if (list_length(anti_exps))
852 43 : anti_exp = exp_in(sql->sa, anti_le, anti_exps, cmp_notin);
853 : } else {
854 0 : assert(0);
855 : }
856 98 : if (!found_nils) {
857 57 : assert(anti_exp);
858 57 : anti_nils = rel_unop_(sql, NULL, anti_le, "sys", "isnull", card_value);
859 57 : set_has_no_nil(anti_nils);
860 57 : anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
861 57 : anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
862 : list_append(new_exp_list(sql->sa), anti_nils), 0);
863 41 : } else if (!anti_exp) {
864 3 : anti_nils = rel_unop_(sql, NULL, exp_copy(sql, anti_le), "sys", "isnull", card_value);
865 3 : set_has_no_nil(anti_nils);
866 3 : anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
867 : }
868 : /* generate a count aggregation for the values not present in any of the partitions */
869 98 : anti_rel = rel_select(sql->sa, anti_rel, anti_exp);
870 98 : set_processed(anti_rel);
871 98 : anti_rel = rel_groupby(sql, anti_rel, NULL);
872 98 : aggr = exp_aggr(sql->sa, NULL, cf, 0, 0, anti_rel->card, 0);
873 98 : (void) rel_groupby_add_aggr(sql, anti_rel, aggr);
874 98 : set_processed(anti_rel);
875 98 : exp_label(sql->sa, aggr, ++sql->label);
876 :
877 98 : aggr = exp_ref(sql, aggr);
878 196 : snprintf(buf, BUFSIZ, "%s: the %s violates the partition %s of values", operation, desc,
879 98 : isRangePartitionTable(t) ? "range (NB higher limit exclusive)" : "list");
880 :
881 98 : sql_exp *exception = exp_exception(sql->sa, aggr, buf);
882 98 : sel = rel_exception(query->sql->sa, sel, anti_rel, list_append(new_exp_list(query->sql->sa), exception));
883 : }
884 100 : rel_destroy(rel);
885 100 : return sel;
886 : }
887 :
888 : static sql_rel*
889 102 : rel_propagate_insert(sql_query *query, sql_rel *rel, sql_table *t, int *changes)
890 : {
891 102 : return rel_generate_subinserts(query, rel, t, changes, "INSERT", "insert");
892 : }
893 :
894 : static sql_rel*
895 21 : rel_propagate_delete(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
896 : {
897 21 : return rel_generate_subdeletes(sql, rel, t, changes);
898 : }
899 :
900 : static bool
901 11 : update_move_across_partitions(sql_rel *rel, sql_table *t)
902 : {
903 33 : for (node *n = ((sql_rel*)rel->r)->exps->h; n; n = n->next) {
904 22 : sql_exp* exp = (sql_exp*) n->data;
905 22 : if (exp->type == e_column && exp->l && exp->r && !strcmp((char*)exp->l, t->base.name)) {
906 11 : char* colname = (char*)exp->r;
907 :
908 11 : if (isPartitionedByColumnTable(t)) {
909 8 : if (!strcmp(colname, t->part.pcol->base.name))
910 : return true;
911 3 : } else if (isPartitionedByExpressionTable(t)) {
912 6 : for (node *nn = t->part.pexp->cols->h; nn; nn = nn->next) {
913 3 : int next = *(int*) nn->data;
914 3 : sql_column *col = find_sql_column(t, colname);
915 3 : if (col && next == col->colnr)
916 : return true;
917 : }
918 : } else {
919 0 : assert(0);
920 : }
921 : }
922 : }
923 : return false;
924 : }
925 :
926 : static sql_rel*
927 11 : rel_propagate_update(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
928 : {
929 11 : bool found_partition_col = update_move_across_partitions(rel, t);
930 11 : sql_rel *sel = NULL;
931 :
932 11 : if (!found_partition_col) { /* easy scenario where the partitioned column is not being updated, just propagate */
933 11 : sel = rel_generate_subupdates(sql, rel, t, changes);
934 : } else { /* harder scenario, has to insert and delete across partitions. */
935 : /*sql_exp *exception = NULL;
936 : sql_rel *inserts = NULL, *deletes = NULL, *anti_rel = NULL;
937 :
938 : deletes = rel_generate_subdeletes(sql, rel, t, changes);
939 : inserts = rel_generate_subinserts(query, rel, &anti_rel, &exception, t, changes, "UPDATE", "update");
940 : inserts = rel_exception(sql->sa, inserts, anti_rel, list_append(new_exp_list(sql->sa), exception));
941 : return rel_list(sql->sa, deletes, inserts);*/
942 0 : assert(0);
943 : }
944 11 : return sel;
945 : }
946 :
947 : static sql_rel*
948 106 : rel_subtable_insert(sql_query *query, sql_rel *rel, sql_table *t, int *changes)
949 : {
950 106 : mvc *sql = query->sql;
951 106 : sql_part *upper = partition_find_part(sql->session->tr, t, NULL);
952 106 : if (!upper)
953 : return NULL;
954 106 : sql_part *pt = upper;
955 106 : sql_rel *anti_dup = rel_create_common_relation(sql, rel, upper->t), *left = rel->l;
956 106 : if (!anti_dup)
957 : return NULL;
958 60 : sql_exp *anti_exp = NULL, *anti_le = rel_generate_anti_insert_expression(sql, &anti_dup, upper->t), *aggr = NULL,
959 60 : *exception = NULL, *anti_nils = NULL;
960 60 : list *anti_exps = new_exp_list(sql->sa);
961 60 : sql_subfunc *cf = sql_bind_func(sql, "sys", "count", sql_bind_localtype("void"), NULL, F_AGGR, true, true);
962 60 : char buf[BUFSIZ];
963 60 : bool found_nils = false, found_all_range_values = false;
964 60 : sql_subtype tp;
965 :
966 60 : find_partition_type(&tp, upper->t);
967 60 : if (isRangePartitionTable(upper->t)) {
968 37 : int tpe = tp.type->localtype;
969 37 : int (*atomcmp)(const void *, const void *) = ATOMcompare(tpe);
970 37 : const void *nil = ATOMnilptr(tpe);
971 :
972 37 : if (pt->with_nills == true || is_bit_nil(pt->with_nills))
973 : found_nils = true;
974 :
975 37 : if (atomcmp(pt->part.range.minvalue, nil) == 0) {
976 7 : if (atomcmp(pt->part.range.maxvalue, nil) == 0) {
977 7 : found_all_range_values = pt->with_nills != 1;
978 7 : if (pt->with_nills == true) {
979 4 : anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
980 4 : set_has_no_nil(anti_nils);
981 4 : anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
982 : }
983 : } else {
984 0 : sql_exp *e2 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue));
985 0 : anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e2, cmp_gte);
986 : }
987 : } else {
988 30 : if (atomcmp(pt->part.range.maxvalue, nil) == 0) {
989 2 : sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue));
990 2 : anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_lt);
991 : } else {
992 28 : sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue));
993 28 : bool max_equal_min = ATOMcmp(tpe, pt->part.range.maxvalue, pt->part.range.minvalue) == 0;
994 :
995 28 : if (max_equal_min) {
996 0 : anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_notequal);
997 : } else {
998 28 : sql_exp *e2 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue)),
999 28 : *range1 = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_lt),
1000 28 : *range2 = exp_compare(sql->sa, exp_copy(sql, anti_le), e2, cmp_gte);
1001 :
1002 28 : anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), range1),
1003 : list_append(new_exp_list(sql->sa), range2), 0);
1004 : }
1005 : }
1006 : }
1007 37 : if (!pt->with_nills) { /* handle the nulls case */
1008 30 : anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
1009 30 : set_has_no_nil(anti_nils);
1010 30 : anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
1011 30 : if (anti_exp)
1012 28 : anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
1013 : list_append(new_exp_list(sql->sa), anti_nils), 0);
1014 : else
1015 : anti_exp = anti_nils;
1016 : }
1017 23 : } else if (isListPartitionTable(upper->t)) {
1018 23 : if (list_length(pt->part.values)) { /* if the partition holds non-null values */
1019 91 : for (node *n = pt->part.values->h ; n ; n = n->next) {
1020 69 : sql_part_value *next = (sql_part_value*) n->data;
1021 69 : sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, next->value));
1022 69 : list_append(anti_exps, exp_copy(sql, e1));
1023 : }
1024 22 : anti_exp = exp_in(sql->sa, exp_copy(sql, anti_le), anti_exps, cmp_notin);
1025 :
1026 22 : if (!pt->with_nills) { /* handle the nulls case */
1027 18 : anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
1028 18 : set_has_no_nil(anti_nils);
1029 18 : anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
1030 18 : anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
1031 : list_append(new_exp_list(sql->sa), anti_nils), 0);
1032 : }
1033 : } else {
1034 1 : assert(pt->with_nills);
1035 1 : anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
1036 1 : set_has_no_nil(anti_nils);
1037 1 : anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
1038 : }
1039 : } else {
1040 0 : assert(0);
1041 : }
1042 :
1043 60 : if (!found_all_range_values || !found_nils) {
1044 : /* generate a count aggregation for the values not present in any of the partitions */
1045 59 : anti_dup = rel_select(sql->sa, anti_dup, anti_exp);
1046 59 : set_processed(anti_dup);
1047 59 : anti_dup = rel_groupby(sql, anti_dup, NULL);
1048 59 : aggr = exp_aggr(sql->sa, NULL, cf, 0, 0, anti_dup->card, 0);
1049 59 : (void) rel_groupby_add_aggr(sql, anti_dup, aggr);
1050 59 : exp_label(sql->sa, aggr, ++sql->label);
1051 59 : set_processed(anti_dup);
1052 :
1053 : /* generate the exception */
1054 59 : aggr = exp_ref(sql, aggr);
1055 118 : snprintf(buf, BUFSIZ, "INSERT: table %s.%s is part of merge table %s.%s and the insert violates the "
1056 59 : "partition %s of values", t->s->base.name, t->base.name, upper->t->s->base.name,
1057 59 : upper->t->base.name, isRangePartitionTable(upper->t) ? "range" : "list");
1058 59 : exception = exp_exception(sql->sa, aggr, buf);
1059 :
1060 59 : left->p = prop_create(sql->sa, PROP_USED, left->p);
1061 59 : (*changes)++;
1062 :
1063 59 : rel = rel_exception(sql->sa, rel, anti_dup, list_append(new_exp_list(sql->sa), exception));
1064 : }
1065 : return rel;
1066 : }
1067 :
1068 : static sql_rel*
1069 60 : rel_find_propagate( sql_rel *rel)
1070 : {
1071 60 : if (is_ddl(rel->op) && rel->flag == ddl_list)
1072 59 : return rel->r;
1073 1 : if (is_ddl(rel->op) && rel->flag == ddl_exception)
1074 0 : return rel->r;
1075 1 : assert(is_insert(rel->op));
1076 : return rel;
1077 : }
1078 :
1079 : sql_rel *
1080 1102 : rel_propagate(sql_query *query, sql_rel *rel, int *changes)
1081 : {
1082 1102 : mvc *sql = query->sql;
1083 1102 : bool isSubtable = false;
1084 1102 : sql_rel *l = rel->l, *propagate = rel;
1085 :
1086 1102 : if (l->op == op_basetable) {
1087 1086 : sql_table *t = l->l;
1088 :
1089 1086 : if (partition_find_part(sql->session->tr, t, NULL) && !find_prop(l->p, PROP_USED)) {
1090 200 : isSubtable = true;
1091 200 : if (is_insert(rel->op)) { /* insertion directly to sub-table (must do validation) */
1092 106 : sql_rel *nrel = rel_subtable_insert(query, rel, t, changes);
1093 106 : if (!nrel)
1094 : return rel;
1095 60 : rel = nrel;
1096 60 : propagate = rel_find_propagate(nrel);
1097 60 : isSubtable = (rel != propagate);
1098 : }
1099 : }
1100 1040 : if (isMergeTable(t)) {
1101 134 : assert(list_length(t->members));
1102 134 : if (is_delete(propagate->op) || is_truncate(propagate->op)) { /* propagate deletions to the partitions */
1103 21 : rel = rel_propagate_delete(sql, rel, t, changes);
1104 113 : } else if (isRangePartitionTable(t) || isListPartitionTable(t)) {
1105 113 : if (is_insert(propagate->op)) { /* on inserts create a selection for each partition */
1106 102 : if (isSubtable) {
1107 2 : rel->r = rel_propagate_insert(query, propagate, t, changes);
1108 : } else {
1109 100 : rel = rel_propagate_insert(query, rel, t, changes);
1110 : }
1111 11 : } else if (is_update(propagate->op)) { /* for updates propagate like in deletions */
1112 11 : rel = rel_propagate_update(sql, rel, t, changes);
1113 : } else {
1114 0 : assert(0);
1115 : }
1116 : } else {
1117 0 : assert(0);
1118 : }
1119 : }
1120 : }
1121 : return rel;
1122 : }
|