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 :
15 : #include "sql_partition.h"
16 : #include "rel_rel.h"
17 : #include "rel_basetable.h"
18 : #include "rel_exp.h"
19 : #include "sql_mvc.h"
20 : #include "sql_catalog.h"
21 : #include "sql_relation.h"
22 : #include "rel_updates.h"
23 : #include "mal_exception.h"
24 :
25 : list *
26 14480 : partition_find_mergetables(mvc *sql, sql_table *t)
27 : {
28 14480 : sql_trans *tr = sql->session->tr;
29 14480 : list *res = NULL;
30 14480 : sql_part *pt = NULL;
31 :
32 43528 : for (; t; t = pt?pt->t:NULL) {
33 14524 : if ((pt=partition_find_part(tr, t, NULL))) {
34 44 : if (!res)
35 44 : res = sa_list(sql->sa);
36 44 : list_append(res, pt);
37 : }
38 : }
39 14480 : return res;
40 : }
41 :
42 : static int
43 4 : key_column_colnr(sql_kc *pkey)
44 : {
45 4 : return pkey->c->colnr;
46 : }
47 :
48 : static int
49 4 : table_column_colnr(int *colnr)
50 : {
51 4 : return *colnr;
52 : }
53 :
54 : str
55 6680 : sql_partition_validate_key(mvc *sql, sql_table *nt, sql_key *k, const char* op)
56 : {
57 6680 : if (k->type != fkey) {
58 5758 : const char *keys = (k->type == pkey) ? "primary" : "unique";
59 5758 : assert(k->type == pkey || k->type == ukey);
60 :
61 5758 : if (isPartitionedByColumnTable(nt)) {
62 6 : assert(nt->part.pcol);
63 6 : if (list_length(k->columns) != 1) {
64 0 : throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
65 0 : "columns must match the columns used in the partition definition", op, nt->s->base.name,
66 : nt->base.name, keys);
67 : } else {
68 6 : sql_kc *kcol = k->columns->h->data;
69 6 : if (kcol->c->colnr != nt->part.pcol->colnr)
70 3 : throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
71 3 : "columns must match the columns used in the partition definition", op, nt->s->base.name,
72 : nt->base.name, keys);
73 : }
74 5752 : } else if (isPartitionedByExpressionTable(nt)) {
75 4 : list *kcols, *pcols;
76 4 : sql_allocator *p1, *p2;
77 :
78 4 : assert(nt->part.pexp->cols);
79 4 : if (list_length(k->columns) != list_length(nt->part.pexp->cols))
80 1 : throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
81 1 : "columns must match the columns used in the partition definition", op, nt->s->base.name,
82 : nt->base.name, keys);
83 :
84 3 : p1 = k->columns->sa; /* save the original sql allocators */
85 3 : p2 = nt->part.pexp->cols->sa;
86 3 : k->columns->sa = sql->sa;
87 3 : nt->part.pexp->cols->sa = sql->sa;
88 3 : kcols = list_sort(k->columns, (fkeyvalue)&key_column_colnr, NULL);
89 3 : pcols = list_sort(nt->part.pexp->cols, (fkeyvalue)&table_column_colnr, NULL);
90 3 : k->columns->sa = p1;
91 3 : nt->part.pexp->cols->sa = p2;
92 :
93 5 : for (node *nn = kcols->h, *mm = pcols->h; nn && mm; nn = nn->next, mm = mm->next) {
94 4 : sql_kc *kcol = nn->data;
95 4 : int *colnr = mm->data;
96 4 : if (kcol->c->colnr != *colnr)
97 2 : throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
98 2 : "columns must match the columns used in the partition definition", op, nt->s->base.name,
99 : nt->base.name, keys);
100 : }
101 : }
102 : }
103 : return NULL;
104 : }
105 :
106 : static void exp_find_table_columns(mvc *sql, sql_exp *e, sql_table *t, list *cols);
107 :
108 : static void
109 0 : rel_find_table_columns(mvc* sql, sql_rel* rel, sql_table *t, list *cols)
110 : {
111 0 : if (mvc_highwater(sql)) {
112 0 : (void) sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
113 0 : return;
114 : }
115 :
116 0 : if (!rel)
117 : return;
118 :
119 0 : if (rel->exps)
120 0 : for (node *n = rel->exps->h ; n ; n = n->next)
121 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
122 :
123 0 : switch (rel->op) {
124 : case op_basetable:
125 : case op_truncate:
126 : break;
127 0 : case op_table:
128 0 : if (IS_TABLE_PROD_FUNC(rel->flag) || rel->flag == TABLE_FROM_RELATION) {
129 0 : if (rel->l)
130 : rel_find_table_columns(sql, rel->l, t, cols);
131 : }
132 : break;
133 0 : case op_join:
134 : case op_left:
135 : case op_right:
136 : case op_full:
137 : case op_union:
138 : case op_inter:
139 : case op_except:
140 : case op_merge:
141 0 : if (rel->l)
142 0 : rel_find_table_columns(sql, rel->l, t, cols);
143 0 : if (rel->r)
144 : rel_find_table_columns(sql, rel->r, t, cols);
145 : break;
146 0 : case op_semi:
147 : case op_anti:
148 : case op_groupby:
149 : case op_project:
150 : case op_select:
151 : case op_topn:
152 : case op_sample:
153 0 : if (rel->l)
154 : rel_find_table_columns(sql, rel->l, t, cols);
155 : break;
156 0 : case op_insert:
157 : case op_update:
158 : case op_delete:
159 0 : if (rel->r)
160 : rel_find_table_columns(sql, rel->r, t, cols);
161 : break;
162 0 : case op_ddl:
163 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) {
164 0 : if (rel->l)
165 : rel_find_table_columns(sql, rel->l, t, cols);
166 : } else if (rel->flag == ddl_list || rel->flag == ddl_exception) {
167 0 : if (rel->l)
168 0 : rel_find_table_columns(sql, rel->l, t, cols);
169 0 : if (rel->r)
170 : rel_find_table_columns(sql, rel->r, t, cols);
171 : }
172 : break;
173 : }
174 : }
175 :
176 : static void
177 72 : exp_find_table_columns(mvc *sql, sql_exp *e, sql_table *t, list *cols)
178 : {
179 90 : if (mvc_highwater(sql)) {
180 0 : (void) sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
181 0 : return;
182 : }
183 :
184 90 : if (!e)
185 : return;
186 90 : switch (e->type) {
187 0 : case e_psm: {
188 0 : if (e->flag & PSM_RETURN) {
189 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
190 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
191 0 : } else if (e->flag & PSM_WHILE) {
192 0 : exp_find_table_columns(sql, e->l, t, cols);
193 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
194 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
195 0 : } else if (e->flag & PSM_IF) {
196 0 : exp_find_table_columns(sql, e->l, t, cols);
197 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
198 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
199 0 : if (e->f)
200 0 : for (node *n = ((list*)e->f)->h ; n ; n = n->next)
201 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
202 0 : } else if (e->flag & PSM_REL) {
203 0 : rel_find_table_columns(sql, e->l, t, cols);
204 0 : } else if (e->flag & PSM_EXCEPTION) {
205 0 : exp_find_table_columns(sql, e->l, t, cols);
206 : }
207 : } break;
208 18 : case e_convert: {
209 18 : exp_find_table_columns(sql, e->l, t, cols);
210 18 : } break;
211 17 : case e_atom: {
212 17 : if (e->f)
213 0 : for (node *n = ((list*)e->f)->h ; n ; n = n->next)
214 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
215 : } break;
216 28 : case e_aggr:
217 : case e_func: {
218 28 : if (e->l)
219 78 : for (node *n = ((list*)e->l)->h ; n ; n = n->next)
220 50 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
221 28 : if (e->type == e_func && e->r)
222 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
223 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
224 : } break;
225 27 : case e_column: {
226 27 : if (!strcmp(e->l, t->base.name)) {
227 27 : sql_column *col = find_sql_column(t, e->r);
228 27 : if (col) {
229 27 : int *cnr = SA_NEW(cols->sa, int);
230 27 : *cnr = col->colnr;
231 27 : list_append(cols, cnr);
232 : }
233 : }
234 : } break;
235 0 : case e_cmp: {
236 0 : if (e->flag == cmp_in || e->flag == cmp_notin) {
237 0 : exp_find_table_columns(sql, e->l, t, cols);
238 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
239 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
240 0 : } else if (e->flag == cmp_or || e->flag == cmp_filter) {
241 0 : for (node *n = ((list*)e->l)->h ; n ; n = n->next)
242 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
243 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
244 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
245 : } else {
246 0 : if (e->l)
247 0 : exp_find_table_columns(sql, e->l, t, cols);
248 0 : if (e->r)
249 0 : exp_find_table_columns(sql, e->r, t, cols);
250 0 : if (e->f)
251 : exp_find_table_columns(sql, e->f, t, cols);
252 : }
253 : } break;
254 : }
255 : }
256 :
257 : str
258 25 : bootstrap_partition_expression(mvc *sql, sql_table *mt, int instantiate)
259 : {
260 25 : sql_exp *exp;
261 25 : char *query, *msg = NULL;
262 25 : sql_class sql_ec;
263 25 : sql_rel *r;
264 :
265 25 : assert(isPartitionedByExpressionTable(mt));
266 :
267 25 : if (sql->emode == m_prepare)
268 0 : throw(SQL,"sql.partition", SQLSTATE(42000) "Partition expressions not compilable with prepared statements");
269 :
270 25 : r = rel_basetable(sql, mt, mt->base.name);
271 25 : query = mt->part.pexp->exp;
272 25 : if (!(exp = rel_parse_val(sql, mt->s, query, NULL, sql->emode, r))) {
273 3 : if (*sql->errstr) {
274 3 : if (strlen(sql->errstr) > 6 && sql->errstr[5] == '!')
275 3 : throw(SQL, "sql.partition", "%s", sql->errstr);
276 : else
277 0 : throw(SQL, "sql.partition", SQLSTATE(42000) "%s", sql->errstr);
278 : }
279 0 : throw(SQL,"sql.partition", SQLSTATE(42000) "Incorrect expression '%s'", query);
280 : }
281 :
282 22 : assert(mt->part.pexp->cols);
283 22 : exp_find_table_columns(sql, exp, mt, mt->part.pexp->cols);
284 :
285 22 : mt->part.pexp->type = *exp_subtype(exp);
286 22 : sql_ec = mt->part.pexp->type.type->eclass;
287 22 : if (!(sql_ec == EC_BIT || EC_VARCHAR(sql_ec) || EC_TEMP(sql_ec) || sql_ec == EC_POS || sql_ec == EC_NUM ||
288 0 : EC_INTERVAL(sql_ec)|| sql_ec == EC_DEC || sql_ec == EC_BLOB)) {
289 0 : char *err = sql_subtype_string(sql->ta, &(mt->part.pexp->type));
290 0 : if (!err) {
291 0 : throw(SQL, "sql.partition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
292 : } else {
293 0 : msg = createException(SQL, "sql.partition",
294 : SQLSTATE(42000) "Column type %s not supported for the expression return value", err);
295 : }
296 : }
297 :
298 22 : if (instantiate && !msg) {
299 19 : r = rel_project(sql->sa, r, NULL);
300 19 : sql_rel *base = r->l, *nr = r;
301 19 : r->l = NULL; /* omit table from list of dependencies */
302 19 : (void) rel_project_add_exp(sql, r, exp);
303 :
304 19 : nr = sql_processrelation(sql, nr, 0, 0, 0, 0);
305 19 : if (nr) {
306 19 : list *blist = rel_dependencies(sql, nr);
307 19 : if (mvc_create_dependencies(sql, blist, mt->base.id, FUNC_DEPENDENCY))
308 0 : msg = createException(SQL, "sql.partition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
309 : }
310 19 : r->l = base;
311 : }
312 :
313 : return msg;
314 : }
315 :
316 : str
317 45 : parse_sql_parts(mvc *sql, sql_table *mt)
318 : {
319 45 : str res = NULL;
320 :
321 45 : if (isPartitionedByExpressionTable(mt) && (res = bootstrap_partition_expression(sql, mt, 0)) != NULL)
322 : return res;
323 : return res;
324 : }
|