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