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
11#include "sql_partition.h"
12#include "rel_rel.h"
13#include "sql_mvc.h"
14#include "sql_catalog.h"
15#include "sql_relation.h"
16#include "rel_unnest.h"
17#include "rel_optimizer.h"
18#include "rel_updates.h"
19#include "mal_exception.h"
20
21static int
22key_column_colnr(sql_kc *pkey)
23{
24 return pkey->c->colnr;
25}
26
27static int
28table_column_colnr(int *colnr)
29{
30 return *colnr;
31}
32
33str
34sql_partition_validate_key(mvc *sql, sql_table *nt, sql_key *k, const char* op)
35{
36 if (k->type != fkey) {
37 const char *keys = (k->type == pkey) ? "primary" : "unique";
38 assert(k->type == pkey || k->type == ukey);
39
40 if (isPartitionedByColumnTable(nt)) {
41 assert(nt->part.pcol);
42 if (list_length(k->columns) != 1) {
43 throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
44 "columns must match the columns used in the partition definition", op, nt->s->base.name,
45 nt->base.name, keys);
46 } else {
47 sql_kc *kcol = k->columns->h->data;
48 if (kcol->c->colnr != nt->part.pcol->colnr)
49 throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
50 "columns must match the columns used in the partition definition", op, nt->s->base.name,
51 nt->base.name, keys);
52 }
53 } else if (isPartitionedByExpressionTable(nt)) {
54 list *kcols, *pcols;
55 sql_allocator *p1, *p2;
56
57 assert(nt->part.pexp->cols);
58 if (list_length(k->columns) != list_length(nt->part.pexp->cols))
59 throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
60 "columns must match the columns used in the partition definition", op, nt->s->base.name,
61 nt->base.name, keys);
62
63 p1 = k->columns->sa; /* save the original sql allocators */
64 p2 = nt->part.pexp->cols->sa;
65 k->columns->sa = sql->sa;
66 nt->part.pexp->cols->sa = sql->sa;
67 kcols = list_sort(k->columns, (fkeyvalue)&key_column_colnr, NULL);
68 pcols = list_sort(nt->part.pexp->cols, (fkeyvalue)&table_column_colnr, NULL);
69 k->columns->sa = p1;
70 nt->part.pexp->cols->sa = p2;
71
72 for (node *nn = kcols->h, *mm = pcols->h; nn && mm; nn = nn->next, mm = mm->next) {
73 sql_kc *kcol = nn->data;
74 int *colnr = mm->data;
75 if (kcol->c->colnr != *colnr)
76 throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
77 "columns must match the columns used in the partition definition", op, nt->s->base.name,
78 nt->base.name, keys);
79 }
80 }
81 }
82 return NULL;
83}
84
85static void exp_find_table_columns(mvc *sql, sql_exp *e, sql_table *t, list *cols);
86
87static void
88rel_find_table_columns(mvc* sql, sql_rel* rel, sql_table *t, list *cols)
89{
90 if (THRhighwater()) {
91 (void) sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
92 return;
93 }
94
95 if (!rel)
96 return;
97
98 if (rel->exps)
99 for (node *n = rel->exps->h ; n ; n = n->next)
100 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
101
102 switch (rel->op) {
103 case op_table:
104 case op_basetable:
105 case op_ddl:
106 break;
107 case op_join:
108 case op_left:
109 case op_right:
110 case op_full:
111 case op_union:
112 case op_inter:
113 case op_except:
114 if (rel->l)
115 rel_find_table_columns(sql, rel->l, t, cols);
116 if (rel->r)
117 rel_find_table_columns(sql, rel->r, t, cols);
118 break;
119 case op_semi:
120 case op_anti:
121 case op_groupby:
122 case op_project:
123 case op_select:
124 case op_topn:
125 case op_sample:
126 if (rel->l)
127 rel_find_table_columns(sql, rel->l, t, cols);
128 break;
129 case op_insert:
130 case op_update:
131 case op_delete:
132 case op_truncate:
133 if (rel->r)
134 rel_find_table_columns(sql, rel->r, t, cols);
135 break;
136 }
137}
138
139static void
140exp_find_table_columns(mvc *sql, sql_exp *e, sql_table *t, list *cols)
141{
142 if (THRhighwater()) {
143 (void) sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
144 return;
145 }
146
147 if (!e)
148 return;
149 switch(e->type) {
150 case e_psm: {
151 if (e->flag & PSM_RETURN) {
152 for(node *n = ((list*)e->r)->h ; n ; n = n->next)
153 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
154 } else if (e->flag & PSM_WHILE) {
155 exp_find_table_columns(sql, e->l, t, cols);
156 for(node *n = ((list*)e->r)->h ; n ; n = n->next)
157 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
158 } else if (e->flag & PSM_IF) {
159 exp_find_table_columns(sql, e->l, t, cols);
160 for(node *n = ((list*)e->r)->h ; n ; n = n->next)
161 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
162 if (e->f)
163 for(node *n = ((list*)e->f)->h ; n ; n = n->next)
164 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
165 } else if (e->flag & PSM_REL) {
166 rel_find_table_columns(sql, e->l, t, cols);
167 } else if (e->flag & PSM_EXCEPTION) {
168 exp_find_table_columns(sql, e->l, t, cols);
169 }
170 } break;
171 case e_convert: {
172 exp_find_table_columns(sql, e->l, t, cols);
173 } break;
174 case e_atom:
175 break;
176 case e_func: {
177 for(node *n = ((list*)e->l)->h ; n ; n = n->next)
178 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
179 if (e->r)
180 for(node *n = ((list*)e->r)->h ; n ; n = n->next)
181 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
182 } break;
183 case e_aggr: {
184 if (e->l)
185 for(node *n = ((list*)e->l)->h ; n ; n = n->next)
186 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
187 } break;
188 case e_column: {
189 if(!strcmp(e->l, t->base.name)) {
190 sql_column *col = find_sql_column(t, e->r);
191 if(col) {
192 int *cnr = sa_alloc(cols->sa, sizeof(int));
193 *cnr = col->colnr;
194 list_append(cols, cnr);
195 }
196 }
197 } break;
198 case e_cmp: {
199 if (e->flag == cmp_in || e->flag == cmp_notin) {
200 exp_find_table_columns(sql, e->l, t, cols);
201 for(node *n = ((list*)e->r)->h ; n ; n = n->next)
202 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
203 } else if (get_cmp(e) == cmp_or || get_cmp(e) == cmp_filter) {
204 for(node *n = ((list*)e->l)->h ; n ; n = n->next)
205 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
206 for(node *n = ((list*)e->r)->h ; n ; n = n->next)
207 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
208 } else {
209 if(e->l)
210 exp_find_table_columns(sql, e->l, t, cols);
211 if(e->r)
212 exp_find_table_columns(sql, e->r, t, cols);
213 if(e->f)
214 exp_find_table_columns(sql, e->f, t, cols);
215 }
216 } break;
217 }
218}
219
220static str
221find_expression_type(sql_exp *e, sql_subtype *tpe)
222{
223 switch(e->type) {
224 case e_convert: {
225 assert(list_length(e->r) == 2);
226 *tpe = *(sql_subtype *)list_fetch(e->r, 1);
227 } break;
228 case e_atom: {
229 if (e->l) {
230 atom *a = e->l;
231 *tpe = a->tpe;
232 } else if (e->r) {
233 *tpe = e->tpe;
234 } else if (e->f) {
235 throw(SQL,"sql.partition", SQLSTATE(42000) "List of values not allowed in expressions");
236 } else {
237 throw(SQL,"sql.partition", SQLSTATE(42000) "Variables/parameters are not allowed in expressions");
238 }
239 } break;
240 case e_func: {
241 sql_subfunc *f = e->f;
242 sql_func *func = f->func;
243 if (list_length(func->res) != 1)
244 throw(SQL,"sql.partition", SQLSTATE(42000) "An expression should return a single value");
245 *tpe = *(sql_subtype *)f->res->h->data;
246 } break;
247 case e_cmp: {
248 sql_subtype *other = sql_bind_localtype("bit");
249 *tpe = *other;
250 } break;
251 case e_column: {
252 *tpe = e->tpe;
253 } break;
254 case e_psm:
255 throw(SQL,"sql.partition", SQLSTATE(42000) "PSM calls are not allowed in expressions");
256 case e_aggr:
257 throw(SQL,"sql.partition", SQLSTATE(42000) "Aggregation functions are not allowed in expressions");
258 }
259 return NULL;
260}
261
262str
263bootstrap_partition_expression(mvc* sql, sql_allocator *rsa, sql_table *mt, int instantiate)
264{
265 sql_exp *exp;
266 char *query, *msg = NULL;
267 sql_class sql_ec;
268 sql_rel *r;
269
270 assert(isPartitionedByExpressionTable(mt));
271
272 if (sql->emode == m_prepare)
273 throw(SQL,"sql.partition", SQLSTATE(42000) "Partition expressions not compilable with prepared statements");
274
275 r = rel_basetable(sql, mt, mt->base.name);
276 query = mt->part.pexp->exp;
277 if((exp = rel_parse_val(sql, sa_message(sql->sa, "select %s;", query), sql->emode, r)) == NULL) {
278 if(*sql->errstr) {
279 if (strlen(sql->errstr) > 6 && sql->errstr[5] == '!')
280 throw(SQL, "sql.partition", "%s", sql->errstr);
281 else
282 throw(SQL, "sql.partition", SQLSTATE(42000) "%s", sql->errstr);
283 }
284 throw(SQL,"sql.partition", SQLSTATE(42000) "Incorrect expression '%s'", query);
285 }
286
287 if(!mt->part.pexp->cols)
288 mt->part.pexp->cols = sa_list(rsa);
289 exp_find_table_columns(sql, exp, mt, mt->part.pexp->cols);
290
291 if((msg = find_expression_type(exp, &(mt->part.pexp->type))) != NULL)
292 return msg;
293
294 sql_ec = mt->part.pexp->type.type->eclass;
295 if(!(sql_ec == EC_BIT || EC_VARCHAR(sql_ec) || EC_TEMP(sql_ec) || sql_ec == EC_POS || sql_ec == EC_NUM ||
296 EC_INTERVAL(sql_ec)|| sql_ec == EC_DEC || sql_ec == EC_BLOB)) {
297 char *err = sql_subtype_string(&(mt->part.pexp->type));
298 if (!err) {
299 throw(SQL, "sql.partition", SQLSTATE(HY001) MAL_MALLOC_FAIL);
300 } else {
301 msg = createException(SQL, "sql.partition",
302 SQLSTATE(42000) "Column type %s not supported for the expression return value", err);
303 GDKfree(err);
304 }
305 }
306
307 if(instantiate) {
308 r = rel_project(sql->sa, r, NULL);
309 r->exps = sa_list(sql->sa);
310 list_append(r->exps, exp);
311
312 if (r)
313 r = rel_unnest(sql, r);
314 if (r)
315 r = rel_optimizer(sql, r, 0);
316 if (r) {
317 node *n, *found = NULL;
318 list *id_l = rel_dependencies(sql, r);
319 for (n = id_l->h ; n ; n = n->next) //remove the table itself from the list of dependencies
320 if(*(sqlid *) n->data == mt->base.id)
321 found = n;
322 assert(found);
323 list_remove_node(id_l, found);
324 mvc_create_dependencies(sql, id_l, mt->base.id, TABLE_DEPENDENCY);
325 }
326 }
327
328 return msg;
329}
330
331void
332find_partition_type(sql_subtype *tpe, sql_table *mt)
333{
334 if(isPartitionedByColumnTable(mt)) {
335 *tpe = mt->part.pcol->type;
336 } else if(isPartitionedByExpressionTable(mt)) {
337 *tpe = mt->part.pexp->type;
338 } else {
339 assert(0);
340 }
341}
342
343str
344initialize_sql_parts(mvc* sql, sql_table *mt)
345{
346 str res = NULL;
347 sql_subtype found;
348 int localtype;
349
350 if(isPartitionedByExpressionTable(mt) && (res = bootstrap_partition_expression(sql, sql->session->tr->sa, mt, 0)) != NULL)
351 return res;
352 find_partition_type(&found, mt);
353 localtype = found.type->localtype;
354
355 if(localtype != TYPE_str && mt->members.set && list_length(mt->members.set)) {
356 list *new = sa_list(sql->session->tr->sa), *old = sa_list(sql->session->tr->sa);
357
358 for (node *n = mt->members.set->h; n; n = n->next) {
359 sql_part* next = (sql_part*) n->data, *p = SA_ZNEW(sql->session->tr->sa, sql_part);
360 sql_table* pt = find_sql_table(mt->s, next->base.name);
361
362 base_init(sql->session->tr->sa, &p->base, pt->base.id, TR_NEW, pt->base.name);
363 p->t = pt;
364 p->tpe = found;
365 p->with_nills = next->with_nills;
366
367 if(isListPartitionTable(mt)) {
368 p->part.values = sa_list(sql->session->tr->sa);
369
370 for (node *m = next->part.values->h; m; m = m->next) {
371 sql_part_value *v = (sql_part_value*) m->data, *nv = SA_ZNEW(sql->session->tr->sa, sql_part_value);
372 ValRecord vvalue;
373 ptr ok;
374
375 nv->tpe = found;
376 vvalue = (ValRecord) {.vtype = TYPE_void,};
377 ok = VALinit(&vvalue, TYPE_str, v->value);
378 if(ok)
379 ok = VALconvert(localtype, &vvalue);
380 if(ok) {
381 nv->value = sa_alloc(sql->session->tr->sa, vvalue.len);
382 memcpy(nv->value, VALget(&vvalue), vvalue.len);
383 nv->length = vvalue.len;
384 }
385 list_append(p->part.values, nv);
386 VALclear(&vvalue);
387 if(!ok) {
388 res = createException(SQL, "sql.partition",
389 SQLSTATE(42000) "Internal error while bootstrapping partitioned tables");
390 goto finish;
391 }
392 }
393 } else if(isRangePartitionTable(mt)) {
394 ValRecord vmin, vmax;
395 ptr ok;
396
397 vmin = vmax = (ValRecord) {.vtype = TYPE_void,};
398 ok = VALinit(&vmin, TYPE_str, next->part.range.minvalue);
399 if(ok)
400 ok = VALinit(&vmax, TYPE_str, next->part.range.maxvalue);
401 if(ok) {
402 if (strcmp((const char *)VALget(&vmin), str_nil) == 0 &&
403 strcmp((const char *)VALget(&vmax), str_nil) == 0) {
404 int tpe = found.type->localtype;
405 const void *nil_ptr = ATOMnilptr(tpe);
406 size_t nil_len = ATOMlen(tpe, nil_ptr);
407
408 assert(p->with_nills && next->with_nills);
409 p->part.range.minvalue = sa_alloc(sql->session->tr->sa, nil_len);
410 p->part.range.maxvalue = sa_alloc(sql->session->tr->sa, nil_len);
411 memcpy(p->part.range.minvalue, nil_ptr, nil_len);
412 memcpy(p->part.range.maxvalue, nil_ptr, nil_len);
413 p->part.range.minlength = nil_len;
414 p->part.range.maxlength = nil_len;
415 } else {
416 ok = VALconvert(localtype, &vmin);
417 if(ok)
418 ok = VALconvert(localtype, &vmax);
419 if(ok) {
420 p->part.range.minvalue = sa_alloc(sql->session->tr->sa, vmin.len);
421 p->part.range.maxvalue = sa_alloc(sql->session->tr->sa, vmax.len);
422 memcpy(p->part.range.minvalue, VALget(&vmin), vmin.len);
423 memcpy(p->part.range.maxvalue, VALget(&vmax), vmax.len);
424 p->part.range.minlength = vmin.len;
425 p->part.range.maxlength = vmax.len;
426 }
427 }
428 }
429 VALclear(&vmin);
430 VALclear(&vmax);
431 if(!ok) {
432 res = createException(SQL, "sql.partition",
433 SQLSTATE(42000) "Internal error while bootstrapping partitioned tables");
434 goto finish;
435 }
436 }
437 list_append(new, p);
438 list_append(old, next);
439 }
440 for (node *n = old->h; n; n = n->next) { //remove the old
441 sql_part* next = (sql_part*) n->data;
442 sql_table* pt = find_sql_table(mt->s, next->base.name);
443
444 pt->p = NULL;
445 cs_del(&mt->members, n, next->base.flags);
446 sql_trans_drop_dependency(sql->session->tr, next->base.id, mt->base.id, TABLE_DEPENDENCY);
447 }
448 for (node *n = new->h; n; n = n->next) {
449 sql_part* next = (sql_part*) n->data;
450 sql_table* pt = find_sql_table(mt->s, next->base.name);
451 sql_part *err = NULL;
452
453 pt->p = mt;
454 if(isRangePartitionTable(mt) || isListPartitionTable(mt)) {
455 err = cs_add_with_validate(&mt->members, next, TR_NEW,
456 isRangePartitionTable(mt) ?
457 sql_range_part_validate_and_insert : sql_values_part_validate_and_insert);
458 } else {
459 assert(0);
460 }
461 if(err) {
462 res = createException(SQL, "sql.partition",
463 SQLSTATE(42000) "Internal error while bootstrapping partitioned tables");
464 goto finish;
465 }
466 pt->s->base.wtime = pt->base.wtime = sql->session->tr->wtime = sql->session->tr->wstime;
467 sql_trans_create_dependency(sql->session->tr, pt->base.id, mt->base.id, TABLE_DEPENDENCY);
468 }
469 mt->s->base.wtime = mt->base.wtime = sql->session->tr->wtime = sql->session->tr->wstime;
470 }
471finish:
472 return res;
473}
474