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 15193 : partition_find_mergetables(mvc *sql, sql_table *t)
27 : {
28 15193 : sql_trans *tr = sql->session->tr;
29 15193 : list *res = NULL;
30 15193 : sql_part *pt = NULL;
31 :
32 45667 : for (; t; t = pt?pt->t:NULL) {
33 15237 : 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 15193 : 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 6874 : sql_partition_validate_key(mvc *sql, sql_table *nt, sql_key *k, const char* op)
56 : {
57 6874 : if (k->type != fkey && k->type != ckey) {
58 5917 : const char *keys = (k->type == pkey) ? "primary" : k->type == unndkey ? "nndunique" : "unique";
59 5917 : assert(k->type == pkey || k->type == ukey || k->type == unndkey);
60 :
61 5917 : 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 5911 : } else if (isPartitionedByExpressionTable(nt)) {
75 4 : list *kcols, *pcols;
76 4 : 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_munion:
147 0 : for (node *n = ((list*)rel->l)->h; n; n = n->next)
148 0 : rel_find_table_columns(sql, n->data, t, cols);
149 : break;
150 0 : case op_semi:
151 : case op_anti:
152 : case op_groupby:
153 : case op_project:
154 : case op_select:
155 : case op_topn:
156 : case op_sample:
157 0 : if (rel->l)
158 : rel_find_table_columns(sql, rel->l, t, cols);
159 : break;
160 0 : case op_insert:
161 : case op_update:
162 : case op_delete:
163 0 : if (rel->r)
164 : rel_find_table_columns(sql, rel->r, t, cols);
165 : break;
166 0 : case op_ddl:
167 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) {
168 0 : if (rel->l)
169 : rel_find_table_columns(sql, rel->l, t, cols);
170 : } else if (rel->flag == ddl_list || rel->flag == ddl_exception) {
171 0 : if (rel->l)
172 0 : rel_find_table_columns(sql, rel->l, t, cols);
173 0 : if (rel->r)
174 : rel_find_table_columns(sql, rel->r, t, cols);
175 : }
176 : break;
177 : }
178 : }
179 :
180 : static void
181 72 : exp_find_table_columns(mvc *sql, sql_exp *e, sql_table *t, list *cols)
182 : {
183 73 : if (mvc_highwater(sql)) {
184 0 : (void) sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
185 0 : return;
186 : }
187 :
188 73 : if (!e)
189 : return;
190 73 : switch (e->type) {
191 0 : case e_psm: {
192 0 : if (e->flag & PSM_RETURN) {
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_WHILE) {
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 : } else if (e->flag & PSM_IF) {
200 0 : exp_find_table_columns(sql, e->l, t, cols);
201 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
202 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
203 0 : if (e->f)
204 0 : for (node *n = ((list*)e->f)->h ; n ; n = n->next)
205 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
206 0 : } else if (e->flag & PSM_REL) {
207 0 : rel_find_table_columns(sql, e->l, t, cols);
208 0 : } else if (e->flag & PSM_EXCEPTION) {
209 0 : exp_find_table_columns(sql, e->l, t, cols);
210 : }
211 : } break;
212 1 : case e_convert: {
213 1 : exp_find_table_columns(sql, e->l, t, cols);
214 1 : } break;
215 17 : case e_atom: {
216 17 : if (e->f)
217 0 : for (node *n = ((list*)e->f)->h ; n ; n = n->next)
218 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
219 : } break;
220 28 : case e_aggr:
221 : case e_func: {
222 28 : if (e->l)
223 78 : for (node *n = ((list*)e->l)->h ; n ; n = n->next)
224 50 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
225 28 : if (e->type == e_func && e->r)
226 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
227 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
228 : } break;
229 27 : case e_column: {
230 27 : if (!strcmp(e->l, t->base.name)) {
231 27 : sql_column *col = find_sql_column(t, e->r);
232 27 : if (col) {
233 27 : int *cnr = SA_NEW(cols->sa, int);
234 27 : *cnr = col->colnr;
235 27 : list_append(cols, cnr);
236 : }
237 : }
238 : } break;
239 0 : case e_cmp: {
240 0 : if (e->flag == cmp_in || e->flag == cmp_notin) {
241 0 : exp_find_table_columns(sql, e->l, t, cols);
242 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
243 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
244 0 : } else if (e->flag == cmp_or || e->flag == cmp_filter) {
245 0 : for (node *n = ((list*)e->l)->h ; n ; n = n->next)
246 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
247 0 : for (node *n = ((list*)e->r)->h ; n ; n = n->next)
248 0 : exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
249 : } else {
250 0 : if (e->l)
251 0 : exp_find_table_columns(sql, e->l, t, cols);
252 0 : if (e->r)
253 0 : exp_find_table_columns(sql, e->r, t, cols);
254 0 : if (e->f)
255 : exp_find_table_columns(sql, e->f, t, cols);
256 : }
257 : } break;
258 : }
259 : }
260 :
261 : str
262 25 : bootstrap_partition_expression(mvc *sql, sql_table *mt, int instantiate)
263 : {
264 25 : sql_exp *exp;
265 25 : char *query, *msg = NULL;
266 25 : sql_class sql_ec;
267 25 : sql_rel *r;
268 :
269 25 : assert(isPartitionedByExpressionTable(mt));
270 :
271 25 : if (sql->emode == m_prepare)
272 0 : throw(SQL,"sql.partition", SQLSTATE(42000) "Partition expressions not compilable with prepared statements");
273 :
274 25 : r = rel_basetable(sql, mt, mt->base.name);
275 25 : query = mt->part.pexp->exp;
276 25 : if (!(exp = rel_parse_val(sql, mt->s, query, NULL, sql->emode, r))) {
277 3 : if (*sql->errstr) {
278 3 : if (strlen(sql->errstr) > 6 && sql->errstr[5] == '!')
279 3 : throw(SQL, "sql.partition", "%s", sql->errstr);
280 : else
281 0 : throw(SQL, "sql.partition", SQLSTATE(42000) "%s", sql->errstr);
282 : }
283 0 : throw(SQL,"sql.partition", SQLSTATE(42000) "Incorrect expression '%s'", query);
284 : }
285 :
286 22 : assert(mt->part.pexp->cols);
287 22 : exp_find_table_columns(sql, exp, mt, mt->part.pexp->cols);
288 :
289 22 : mt->part.pexp->type = *exp_subtype(exp);
290 22 : sql_ec = mt->part.pexp->type.type->eclass;
291 22 : if (!(sql_ec == EC_BIT || EC_VARCHAR(sql_ec) || EC_TEMP(sql_ec) || sql_ec == EC_POS || sql_ec == EC_NUM ||
292 0 : EC_INTERVAL(sql_ec)|| sql_ec == EC_DEC || sql_ec == EC_BLOB)) {
293 0 : char *err = sql_subtype_string(sql->ta, &(mt->part.pexp->type));
294 0 : if (!err) {
295 0 : throw(SQL, "sql.partition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
296 : } else {
297 0 : msg = createException(SQL, "sql.partition",
298 : SQLSTATE(42000) "Column type %s not supported for the expression return value", err);
299 : }
300 : }
301 :
302 22 : if (instantiate && !msg) {
303 19 : r = rel_project(sql->sa, r, NULL);
304 19 : sql_rel *base = r->l, *nr = r;
305 19 : r->l = NULL; /* omit table from list of dependencies */
306 19 : (void) rel_project_add_exp(sql, r, exp);
307 :
308 19 : nr = sql_processrelation(sql, nr, 0, instantiate, 0, 0);
309 19 : if (nr) {
310 19 : list *blist = rel_dependencies(sql, nr);
311 19 : if (mvc_create_dependencies(sql, blist, mt->base.id, FUNC_DEPENDENCY))
312 0 : msg = createException(SQL, "sql.partition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
313 : }
314 19 : r->l = base;
315 : }
316 :
317 : return msg;
318 : }
319 :
320 : str
321 45 : parse_sql_parts(mvc *sql, sql_table *mt)
322 : {
323 45 : str res = NULL;
324 :
325 45 : if (isPartitionedByExpressionTable(mt) && (res = bootstrap_partition_expression(sql, mt, 0)) != NULL)
326 : return res;
327 : return res;
328 : }
|