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, 2025 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #define LINESIZE 160
15 : #define TABSTOP 2
16 :
17 : #include "rel_dump.h"
18 : #include "rel_rel.h"
19 : #include "rel_basetable.h"
20 : #include "rel_exp.h"
21 : #include "rel_unnest.h"
22 : #include "rel_updates.h"
23 : #include "rel_select.h"
24 : #include "rel_remote.h"
25 : #include "rel_rewriter.h"
26 : #include "rel_optimizer.h"
27 : #include "sql_privileges.h"
28 :
29 : static void
30 3931 : print_indent(mvc *sql, stream *fout, int depth, int decorate)
31 : {
32 3931 : char buf[LINESIZE+1];
33 3931 : int i;
34 :
35 3931 : (void)sql;
36 3931 : if (!decorate) {
37 1055 : mnstr_printf(fout, "\n");
38 1055 : return ;
39 : }
40 2876 : depth *= TABSTOP;
41 2876 : if (depth > LINESIZE)
42 : depth = LINESIZE;
43 22122 : for (i = 0; i < depth; i++){
44 19246 : if ((i % TABSTOP) == 0)
45 9623 : buf[i] = '|';
46 : else
47 9623 : buf[i] = ' ';
48 : }
49 2876 : buf[i] = 0;
50 2876 : mnstr_printf(fout, "\n=%s", buf);
51 : }
52 :
53 : static void
54 687 : cmp_print(mvc *sql, stream *fout, int cmp)
55 : {
56 687 : char *r = NULL;
57 :
58 687 : (void)sql;
59 687 : switch(cmp) {
60 32 : case cmp_gt: r = ">"; break;
61 10 : case cmp_gte: r = ">="; break;
62 107 : case cmp_lte: r = "<="; break;
63 57 : case cmp_lt: r = "<"; break;
64 366 : case cmp_equal: r = "="; break;
65 10 : case cmp_notequal: r = "!="; break;
66 :
67 0 : case cmp_filter: r = "filter"; break;
68 51 : case cmp_or: r = "or"; break;
69 46 : case cmp_in: r = "in"; break;
70 8 : case cmp_notin: r = "notin"; break;
71 :
72 0 : case cmp_all:
73 : case cmp_project:
74 : case cmp_joined:
75 : case cmp_left_project:
76 0 : r = "inner"; break;
77 : }
78 687 : mnstr_printf(fout, " %s ", r);
79 687 : }
80 :
81 : static const char *
82 18993 : dump_escape_ident(allocator *sa, const char *s)
83 : {
84 18993 : char *res = NULL;
85 18993 : if (s) {
86 18993 : size_t l = strlen(s);
87 18993 : char *r = SA_NEW_ARRAY(sa, char, (l * 2) + 1);
88 :
89 18993 : res = r;
90 98930 : while (*s) {
91 79937 : if (*s == '"' || *s == '\\')
92 12 : *r++ = '\\';
93 79937 : *r++ = *s++;
94 : }
95 18993 : *r = '\0';
96 : }
97 18993 : return res;
98 : }
99 :
100 : static char *
101 1365 : dump_sql_subtype(allocator *sa, sql_subtype *t)
102 : {
103 1365 : char buf[BUFSIZ];
104 :
105 1365 : if (t->digits && t->scale)
106 33 : snprintf(buf, BUFSIZ, "%s(%u,%u)", t->type->base.name, t->digits, t->scale);
107 1332 : else if (t->digits)
108 1176 : snprintf(buf, BUFSIZ, "%s(%u)", t->type->base.name, t->digits);
109 : else
110 156 : snprintf(buf, BUFSIZ, "%s", t->type->base.name);
111 1365 : return sa_strdup(sa, buf);
112 : }
113 :
114 : static void exps_print(mvc *sql, stream *fout, list *exps, int depth, list *refs, int alias, int brackets, int decorate, int expbrk);
115 :
116 : static void rel_print_rel(mvc *sql, stream *fout, sql_rel *rel, int depth, list *refs, int decorate);
117 :
118 : void
119 8878 : exp_print(mvc *sql, stream *fout, sql_exp *e, int depth, list *refs, int comma, int alias, int decorate)
120 : {
121 8878 : (void)sql;
122 8878 : if (!e)
123 : return;
124 : /*mnstr_printf(fout, "%p ", e);*/
125 8878 : if (mvc_debug_on(sql, 4) && e->alias.label < 0)
126 0 : mnstr_printf(fout, "%d: ", e->alias.label);
127 8878 : switch(e->type) {
128 0 : case e_psm: {
129 0 : if (e->flag & PSM_SET) {
130 0 : const char *rname = exp_relname(e);
131 0 : int level = GET_PSM_LEVEL(e->flag);
132 0 : if (rname)
133 0 : mnstr_printf(fout, "\"%s\".", dump_escape_ident(sql->ta, rname));
134 0 : mnstr_printf(fout, "\"%s\" = ", dump_escape_ident(sql->ta, exp_name(e)));
135 0 : exp_print(sql, fout, e->l, depth, refs, 0, 0, decorate);
136 0 : mnstr_printf(fout, " FRAME %d ", level);
137 0 : alias = 0;
138 0 : } else if (e->flag & PSM_VAR) {
139 : // todo output table def (from e->f)
140 0 : const char *rname = exp_relname(e);
141 0 : char *type_str = e->f ? NULL : dump_sql_subtype(sql->ta, exp_subtype(e));
142 0 : int level = GET_PSM_LEVEL(e->flag);
143 0 : mnstr_printf(fout, "declare ");
144 0 : if (rname)
145 0 : mnstr_printf(fout, "\"%s\".", dump_escape_ident(sql->ta, rname));
146 0 : mnstr_printf(fout, "\"%s\" %s FRAME %d ", dump_escape_ident(sql->ta, exp_name(e)), type_str ? type_str : "", level);
147 0 : alias = 0;
148 0 : } else if (e->flag & PSM_RETURN) {
149 0 : int level = GET_PSM_LEVEL(e->flag);
150 0 : mnstr_printf(fout, "return ");
151 0 : exp_print(sql, fout, e->l, depth, refs, 0, 0, decorate);
152 0 : mnstr_printf(fout, " FRAME %d ", level);
153 0 : alias = 0;
154 0 : } else if (e->flag & PSM_WHILE) {
155 0 : mnstr_printf(fout, "while ");
156 0 : exp_print(sql, fout, e->l, depth, refs, 0, 0, decorate);
157 0 : exps_print(sql, fout, e->r, depth, refs, 0, 0, decorate, 0);
158 0 : alias = 0;
159 0 : } else if (e->flag & PSM_IF) {
160 0 : mnstr_printf(fout, "if ");
161 0 : exp_print(sql, fout, e->l, depth, refs, 0, 0, decorate);
162 0 : exps_print(sql, fout, e->r, depth, refs, 0, 0, decorate, 0);
163 0 : if (e->f)
164 0 : exps_print(sql, fout, e->f, depth, refs, 0, 0, decorate, 0);
165 : alias = 0;
166 0 : } else if (e->flag & PSM_REL) {
167 0 : rel_print_rel(sql, fout, e->l, depth+10, refs, 1);
168 0 : } else if (e->flag & PSM_EXCEPTION) {
169 0 : mnstr_printf(fout, "except ");
170 0 : exp_print(sql, fout, e->l, depth, refs, 0, 0, decorate);
171 0 : mnstr_printf(fout, " error %s", (const char *) e->r);
172 0 : alias = 0;
173 : }
174 : break;
175 : }
176 87 : case e_convert: {
177 87 : char *to_type = dump_sql_subtype(sql->ta, exp_subtype(e));
178 87 : mnstr_printf(fout, "%s[", to_type);
179 87 : exp_print(sql, fout, e->l, depth, refs, 0, 0, decorate);
180 87 : mnstr_printf(fout, "]");
181 87 : break;
182 : }
183 1354 : case e_atom: {
184 1354 : if (e->l) {
185 1280 : atom *a = e->l;
186 1280 : if (atom_type(a)->type->localtype == TYPE_ptr) {
187 2 : sql_table *t = a->data.val.pval;
188 2 : mnstr_printf(fout, "%s(\"%s\")",
189 2 : isMergeTable(t)?"merge table":
190 2 : isReplicaTable(t)?"replica table":"table",
191 2 : dump_escape_ident(sql->ta, t->base.name));
192 : } else {
193 1278 : char *t = dump_sql_subtype(sql->ta, atom_type(a));
194 1278 : if (a->isnull)
195 69 : mnstr_printf(fout, "%s NULL", t);
196 : else {
197 1209 : char *s = ATOMformat(a->data.vtype, VALptr(&a->data));
198 1209 : if (s && *s == '"')
199 142 : mnstr_printf(fout, "%s %s", t, s);
200 1067 : else if (s)
201 1067 : mnstr_printf(fout, "%s \"%s\"", t, s);
202 1209 : GDKfree(s);
203 : }
204 : }
205 : } else { /* variables */
206 74 : if (e->r) { /* named parameters and declared variables */
207 22 : sql_var_name *vname = (sql_var_name*) e->r;
208 22 : if (vname->sname)
209 3 : mnstr_printf(fout, "\"%s\".", dump_escape_ident(sql->ta, vname->sname));
210 22 : mnstr_printf(fout, "\"%s\"", dump_escape_ident(sql->ta, vname->name));
211 52 : } else if (e->f) { /* values list */
212 52 : list *l = e->f;
213 52 : exps_print(sql, fout, l, depth, refs, 0, 0, decorate, 0);
214 : } else { /* numbered arguments */
215 0 : mnstr_printf(fout, "A%u", e->flag);
216 : }
217 : }
218 : } break;
219 371 : case e_func: {
220 371 : sql_subfunc *f = e->f;
221 371 : mnstr_printf(fout, "\"%s\".\"%s\"",
222 371 : f->func->s?dump_escape_ident(sql->ta, f->func->s->base.name):"sys",
223 371 : dump_escape_ident(sql->ta, f->func->base.name));
224 371 : exps_print(sql, fout, e->l, depth, refs, 0, 1, decorate, 0);
225 371 : if (e->r) { /* list of optional lists */
226 0 : list *l = e->r;
227 0 : for(node *n = l->h; n; n = n->next)
228 0 : exps_print(sql, fout, n->data, depth, refs, 0, 1, decorate, 0);
229 : }
230 371 : if (e->flag && is_compare_func(f))
231 0 : mnstr_printf(fout, " %s", e->flag==1?"ANY":"ALL");
232 : } break;
233 96 : case e_aggr: {
234 96 : sql_subfunc *a = e->f;
235 96 : mnstr_printf(fout, "\"%s\".\"%s\"",
236 96 : a->func->s?dump_escape_ident(sql->ta, a->func->s->base.name):"sys",
237 96 : dump_escape_ident(sql->ta, a->func->base.name));
238 96 : if (need_distinct(e))
239 4 : mnstr_printf(fout, " unique ");
240 96 : if (need_no_nil(e))
241 33 : mnstr_printf(fout, " no nil ");
242 96 : if (zero_if_empty(e))
243 0 : mnstr_printf(fout, " zero if empty ");
244 96 : if (e->l)
245 37 : exps_print(sql, fout, e->l, depth, refs, 0, 1, decorate, 0);
246 : else
247 59 : mnstr_printf(fout, "()");
248 96 : if (e->r) { /* order by exps */
249 0 : list *r = e->r;
250 0 : list *obes = r->h->data;
251 0 : exps_print(sql, fout, obes, depth, refs, 0, 1, decorate, 0);
252 0 : if (r->h->next) {
253 0 : list *exps = r->h->next->data;
254 0 : exps_print(sql, fout, exps, depth, refs, 0, 1, decorate, 0);
255 : }
256 : }
257 : } break;
258 6340 : case e_column: {
259 6340 : if (is_freevar(e))
260 0 : mnstr_printf(fout, "!!!FREE!!! ");
261 6340 : if (mvc_debug_on(sql, 4) && e->nid)
262 0 : mnstr_printf(fout, "<%d", e->nid);
263 6340 : if (e->l)
264 6236 : mnstr_printf(fout, "\"%s\".", dump_escape_ident(sql->ta, (char*)e->l));
265 6340 : mnstr_printf(fout, "\"%s\"", dump_escape_ident(sql->ta, (char*)e->r));
266 6340 : if (exp_relname(e) && exp_name(e) && e->l && e->r &&
267 6170 : strcmp(exp_relname(e), e->l) == 0 &&
268 4459 : strcmp(exp_name(e), e->r) == 0)
269 6340 : alias = 0;
270 6340 : if(!exp_relname(e) && exp_name(e) && !e->l && strcmp(exp_name(e), e->r)==0)
271 76 : alias = 0;
272 : } break;
273 630 : case e_cmp:
274 630 : if (e->flag == cmp_in || e->flag == cmp_notin) {
275 54 : mnstr_printf(fout, "(");
276 54 : exp_print(sql, fout, e->l, depth+1, refs, 0, 0, decorate);
277 54 : mnstr_printf(fout, ")");
278 54 : if (is_anti(e))
279 0 : mnstr_printf(fout, " !");
280 54 : cmp_print(sql, fout, e->flag);
281 54 : exps_print(sql, fout, e->r, depth, refs, 0, 1, decorate, 0);
282 576 : } else if (e->flag == cmp_or) {
283 51 : exps_print(sql, fout, e->l, depth, refs, 0, 1, decorate, 0);
284 51 : if (is_anti(e))
285 0 : mnstr_printf(fout, " !");
286 51 : cmp_print(sql, fout, e->flag);
287 51 : exps_print(sql, fout, e->r, depth, refs, 0, 1, decorate, 0);
288 525 : } else if (e->flag == cmp_filter) {
289 18 : sql_subfunc *f = e->f;
290 :
291 18 : exps_print(sql, fout, e->l, depth, refs, 0, 1, decorate, 0);
292 18 : if (is_anti(e))
293 2 : mnstr_printf(fout, " !");
294 18 : mnstr_printf(fout, " FILTER \"%s\".\"%s\"",
295 18 : f->func->s?dump_escape_ident(sql->ta, f->func->s->base.name):"sys",
296 18 : dump_escape_ident(sql->ta, f->func->base.name));
297 18 : exps_print(sql, fout, e->r, depth, refs, 0, 1, decorate, 0);
298 507 : } else if (e->f) {
299 75 : mnstr_printf(fout, "(");
300 75 : exp_print(sql, fout, e->r, depth+1, refs, 0, 0, decorate);
301 75 : mnstr_printf(fout, ")");
302 75 : if (is_anti(e))
303 6 : mnstr_printf(fout, " !");
304 75 : cmp_print(sql, fout, swap_compare(range2lcompare(e->flag)));
305 75 : mnstr_printf(fout, "(");
306 75 : exp_print(sql, fout, e->l, depth+1, refs, 0, 0, decorate);
307 75 : mnstr_printf(fout, ")");
308 75 : if (is_anti(e))
309 6 : mnstr_printf(fout, " !");
310 75 : cmp_print(sql, fout, range2rcompare(e->flag));
311 75 : mnstr_printf(fout, "(");
312 75 : exp_print(sql, fout, e->f, depth+1, refs, 0, 0, decorate);
313 75 : mnstr_printf(fout, ")");
314 75 : if (is_symmetric(e))
315 1 : mnstr_printf(fout, " SYM");
316 : } else {
317 432 : mnstr_printf(fout, "(");
318 432 : exp_print(sql, fout, e->l, depth+1, refs, 0, 0, decorate);
319 432 : mnstr_printf(fout, ")");
320 432 : if (is_anti(e))
321 14 : mnstr_printf(fout, " !");
322 432 : if (is_semantics(e))
323 32 : mnstr_printf(fout, " *");
324 432 : if (is_any(e))
325 2 : mnstr_printf(fout, " +");
326 432 : cmp_print(sql, fout, e->flag);
327 :
328 432 : mnstr_printf(fout, "(");
329 432 : exp_print(sql, fout, e->r, depth+1, refs, 0, 0, decorate);
330 432 : mnstr_printf(fout, ")");
331 : }
332 : break;
333 8878 : default:
334 8878 : ;
335 : }
336 8878 : if (e->type != e_atom && e->type != e_cmp && is_partitioning(e))
337 31 : mnstr_printf(fout, " PART");
338 8878 : if (e->type != e_atom && e->type != e_cmp && is_ascending(e))
339 85 : mnstr_printf(fout, " ASC");
340 8878 : if (e->type != e_atom && e->type != e_cmp && nulls_last(e))
341 24 : mnstr_printf(fout, " NULLS LAST");
342 8878 : if (e->type != e_atom && e->type != e_cmp && !has_nil(e))
343 3289 : mnstr_printf(fout, " NOT NULL");
344 8878 : if (e->type != e_atom && e->type != e_cmp && is_unique(e))
345 1454 : mnstr_printf(fout, " UNIQUE");
346 : /* don't show properties on value lists */
347 8878 : if (decorate && e->p && e->type != e_atom && !exp_is_atom(e)) {
348 9529 : for (prop *p = e->p; p; p = p->p) {
349 : /* Don't show min/max/unique est on atoms, or when running tests with forcemito */
350 6945 : if ((ATOMIC_GET(&GDKdebug) & TESTINGMASK) == 0 ||
351 6945 : (p->kind != PROP_MIN && p->kind != PROP_MAX && p->kind != PROP_NUNIQUES)) {
352 478 : char *pv = propvalue2string(sql->ta, p);
353 478 : mnstr_printf(fout, " %s %s", propkind2string(p), pv);
354 : }
355 : }
356 : }
357 8878 : if (exp_name(e) && alias) {
358 2212 : mnstr_printf(fout, " as ");
359 2212 : if (exp_relname(e))
360 2129 : mnstr_printf(fout, "\"%s\".", dump_escape_ident(sql->ta, exp_relname(e)));
361 2212 : mnstr_printf(fout, "\"%s\"", dump_escape_ident(sql->ta, exp_name(e)));
362 : }
363 :
364 8878 : if (e->comment) {
365 32 : str s = ATOMformat(TYPE_str, e->comment);
366 32 : mnstr_printf(fout, " COMMENT %s ", s);
367 32 : GDKfree(s);
368 : }
369 8878 : if (comma)
370 4487 : mnstr_printf(fout, ", ");
371 : }
372 :
373 :
374 : str
375 188 : rel2str( mvc *sql, sql_rel *rel)
376 : {
377 188 : buffer *b = NULL;
378 188 : stream *s = NULL;
379 188 : list *refs = NULL;
380 188 : char *res = NULL;
381 :
382 188 : b = buffer_create(1024);
383 188 : if(b == NULL)
384 0 : goto cleanup;
385 188 : s = buffer_wastream(b, "rel_dump");
386 188 : if(s == NULL)
387 0 : goto cleanup;
388 188 : refs = sa_list(sql->sa);
389 188 : if (!refs)
390 0 : goto cleanup;
391 :
392 188 : rel_print_refs(sql, s, rel, 0, refs, 0);
393 188 : rel_print_(sql, s, rel, 0, refs, 0);
394 188 : mnstr_printf(s, "\n");
395 188 : res = buffer_get_buf(b);
396 :
397 188 : cleanup:
398 188 : if(b)
399 188 : buffer_destroy(b);
400 188 : if(s)
401 188 : close_stream(s);
402 :
403 188 : char* fres = SA_STRDUP(sql->sa, res);
404 188 : free (res);
405 188 : return fres;
406 : }
407 :
408 : str
409 32 : exp2str( mvc *sql, sql_exp *exp)
410 : {
411 32 : buffer *b = NULL;
412 32 : stream *s = NULL;
413 32 : char *res = NULL;
414 :
415 32 : b = buffer_create(1024);
416 32 : if(b == NULL)
417 0 : goto cleanup;
418 32 : s = buffer_wastream(b, "exp_dump");
419 32 : if(s == NULL)
420 0 : goto cleanup;
421 :
422 32 : exp_print(sql, s, exp, 0, NULL, 0, 0, 0);
423 32 : res = buffer_get_buf(b);
424 :
425 32 : cleanup:
426 32 : if(b)
427 32 : buffer_destroy(b);
428 32 : if(s)
429 32 : close_stream(s);
430 :
431 32 : char* fres = SA_STRDUP(sql->sa, res);
432 32 : free (res);
433 32 : return fres;
434 : }
435 :
436 : static void
437 3236 : exps_print(mvc *sql, stream *fout, list *exps, int depth, list *refs, int alias, int brackets, int decorate, int expbrk)
438 : {
439 3236 : node *en;
440 :
441 3236 : if (brackets)
442 600 : mnstr_printf(fout, "(");
443 : else
444 2636 : mnstr_printf(fout, " [ ");
445 3236 : if (exps)
446 10742 : for (en = exps->h; en; en = en->next){
447 7608 : exp_print(sql, fout, en->data, depth+1, refs, (en->next!=NULL), alias, decorate);
448 : /* break for each entry of the expression list */
449 7608 : if (expbrk && en->next!=NULL)
450 0 : print_indent(sql, fout, depth+2, decorate);
451 : }
452 3236 : int multi_exps = expbrk && (list_length(exps) > 1);
453 0 : if (multi_exps)
454 0 : print_indent(sql, fout, depth+1, decorate);
455 3236 : if (brackets)
456 600 : mnstr_printf(fout, ")");
457 : else
458 5272 : mnstr_printf(fout, multi_exps?"]":" ]");
459 3236 : }
460 :
461 : static int
462 30 : find_ref( list *refs, sql_rel *rel )
463 : {
464 30 : node *n;
465 30 : int nr = 1;
466 :
467 34 : for(n=refs->h; n; n = n->next, nr++){
468 27 : if (n->data == rel)
469 : return nr;
470 : }
471 : return 0;
472 : }
473 :
474 : static void
475 2384 : rel_print_rel(mvc *sql, stream *fout, sql_rel *rel, int depth, list *refs, int decorate)
476 : {
477 2384 : char *r = NULL;
478 :
479 2384 : if (!rel)
480 : return;
481 :
482 2384 : if (rel_is_ref(rel)) {
483 7 : int nr = list_length(refs) + 1;
484 7 : int cnt = rel->ref.refcnt;
485 11 : mnstr_printf(fout, "\n%cREF %d (%d)", decorate?'=':' ', nr, cnt);
486 : }
487 :
488 2384 : print_indent(sql, fout, depth, decorate);
489 :
490 2384 : if (is_single(rel))
491 7 : mnstr_printf(fout, "single ");
492 :
493 2384 : switch (rel->op) {
494 771 : case op_basetable: {
495 771 : sql_table *t = rel->l;
496 771 : const char *sname = t->s ? t->s->base.name : NULL; /* All tables, but declared ones on the stack have schema */
497 771 : const char *tname = t->base.name;
498 :
499 771 : if (isRemote(t)) {
500 217 : const char *uri = t->query;
501 :
502 217 : sname = mapiuri_schema( uri, sql->sa, sname);
503 217 : tname = mapiuri_table( uri, sql->sa, tname);
504 : }
505 771 : if (sname)
506 1542 : mnstr_printf(fout, "%s(\"%s\".\"%s\")",
507 771 : isRemote(t)&&decorate?"REMOTE":
508 752 : isReplicaTable(t)?"REPLICA":"table",
509 : dump_escape_ident(sql->ta, sname), dump_escape_ident(sql->ta, tname));
510 : else
511 0 : mnstr_printf(fout, "%s(\"%s\")",
512 0 : isRemote(t)&&decorate?"REMOTE":
513 0 : isReplicaTable(t)?"REPLICA":"table",
514 : dump_escape_ident(sql->ta, tname));
515 771 : if (rel->exps)
516 771 : exps_print(sql, fout, rel->exps, depth, refs, 1, 0, decorate, 0);
517 : else
518 0 : rel_base_dump_exps(fout, rel);
519 : } break;
520 20 : case op_table:
521 20 : mnstr_printf(fout, "table (");
522 :
523 20 : if (rel->r)
524 8 : exp_print(sql, fout, rel->r, depth, refs, 1, 0, decorate);
525 20 : if (rel->l) {
526 17 : if (rel->flag == TRIGGER_WRAPPER)
527 0 : mnstr_printf(fout, "rel_dump not yet implemented for trigger input");
528 : else
529 17 : rel_print_rel(sql, fout, rel->l, depth+1, refs, decorate);
530 : }
531 20 : print_indent(sql, fout, depth, decorate);
532 20 : mnstr_printf(fout, ")");
533 20 : if (rel->exps)
534 20 : exps_print(sql, fout, rel->exps, depth, refs, 1, 0, decorate, 0);
535 : break;
536 2 : case op_ddl:
537 2 : mnstr_printf(fout, "ddl");
538 2 : if (rel->l)
539 0 : rel_print_rel(sql, fout, rel->l, depth+1, refs, decorate);
540 2 : if (rel->r)
541 0 : rel_print_rel(sql, fout, rel->r, depth+1, refs, decorate);
542 2 : if (rel->exps && (rel->flag == ddl_psm || rel->flag == ddl_exception || rel->flag == ddl_list))
543 0 : exps_print(sql, fout, rel->exps, depth, refs, 1, 0, decorate, 0);
544 : break;
545 259 : case op_join:
546 : case op_left:
547 : case op_right:
548 : case op_full:
549 : case op_semi:
550 : case op_anti:
551 : case op_union:
552 : case op_inter:
553 : case op_except:
554 259 : r = "join";
555 259 : if (rel->op == op_left)
556 45 : r = rel->attr?"left outer group join":"left outer join";
557 : else if (rel->op == op_right)
558 : r = "right outer join";
559 : else if (rel->op == op_full)
560 : r = "full outer join";
561 : else if (rel->op == op_semi)
562 : r = "semijoin";
563 : else if (rel->op == op_anti)
564 : r = "antijoin";
565 : else if (rel->op == op_union)
566 : r = "union";
567 : else if (rel->op == op_inter)
568 : r = "intersect";
569 : else if (rel->op == op_except)
570 : r = "except";
571 173 : else if (rel->op == op_join) {
572 173 : if (list_empty(rel->exps))
573 28 : r = rel->attr?"group crossproduct":"crossproduct";
574 : else
575 145 : r = rel->attr?"group join":"join";
576 : }
577 :
578 259 : if (is_dependent(rel))
579 0 : mnstr_printf(fout, "dependent ");
580 259 : if (need_distinct(rel))
581 8 : mnstr_printf(fout, "distinct ");
582 259 : mnstr_printf(fout, "%s (", r);
583 259 : if (rel->l) {
584 259 : if (rel_is_ref(rel->l)) {
585 3 : int nr = find_ref(refs, rel->l);
586 3 : print_indent(sql, fout, depth+1, decorate);
587 3 : mnstr_printf(fout, "& REF %d ", nr);
588 : } else
589 256 : rel_print_rel(sql, fout, rel->l, depth+1, refs, decorate);
590 : }
591 259 : mnstr_printf(fout, ",");
592 259 : if (rel->r) {
593 259 : if (rel_is_ref(rel->r)) {
594 0 : int nr = find_ref(refs, rel->r);
595 0 : print_indent(sql, fout, depth+1, decorate);
596 0 : mnstr_printf(fout, "& REF %d ", nr);
597 : } else
598 259 : rel_print_rel(sql, fout, rel->r, depth+1, refs, decorate);
599 : }
600 259 : print_indent(sql, fout, depth, decorate);
601 259 : mnstr_printf(fout, ")");
602 259 : exps_print(sql, fout, rel->exps, depth, refs, 1, 0, decorate, 0);
603 259 : if (is_join(rel->op) && rel->attr) /* group joins */
604 2 : exps_print(sql, fout, rel->attr, depth, refs, 1, 0, decorate, 0);
605 : break;
606 41 : case op_munion:
607 41 : r = "munion";
608 41 : if (is_dependent(rel))
609 0 : mnstr_printf(fout, "dependent ");
610 41 : if (need_distinct(rel))
611 5 : mnstr_printf(fout, "distinct ");
612 41 : if (is_recursive(rel))
613 0 : mnstr_printf(fout, "recursive ");
614 41 : mnstr_printf(fout, "%s (", r);
615 41 : assert(rel->l);
616 145 : for (node *n = ((list*)rel->l)->h; n; n = n->next) {
617 104 : if (rel_is_ref(n->data)) {
618 0 : int nr = find_ref(refs, n->data);
619 0 : print_indent(sql, fout, depth+1, decorate);
620 0 : mnstr_printf(fout, "& REF %d ", nr);
621 : } else {
622 104 : rel_print_rel(sql, fout, n->data, depth+1, refs, decorate);
623 : }
624 104 : if (n->next)
625 63 : mnstr_printf(fout, ",");
626 : }
627 41 : print_indent(sql, fout, depth, decorate);
628 41 : mnstr_printf(fout, ")");
629 41 : exps_print(sql, fout, rel->exps, depth, refs, 1, 0, decorate, 0);
630 41 : break;
631 1278 : case op_project:
632 : case op_select:
633 : case op_groupby:
634 : case op_topn:
635 : case op_sample:
636 1278 : r = "project";
637 1278 : if (rel->op == op_select)
638 266 : r = "select";
639 1278 : if (rel->op == op_groupby)
640 123 : r = "group by";
641 1278 : if (rel->op == op_topn)
642 4 : r = "top N";
643 1278 : if (rel->op == op_sample)
644 0 : r = "sample";
645 :
646 1278 : if (rel->l) {
647 1200 : if (need_distinct(rel))
648 0 : mnstr_printf(fout, "distinct ");
649 1200 : mnstr_printf(fout, "%s (", r);
650 1200 : if (rel_is_ref(rel->l)) {
651 6 : int nr = find_ref(refs, rel->l);
652 6 : print_indent(sql, fout, depth+1, decorate);
653 6 : mnstr_printf(fout, "& REF %d ", nr);
654 : } else
655 1194 : rel_print_rel(sql, fout, rel->l, depth+1, refs, decorate);
656 1200 : print_indent(sql, fout, depth, decorate);
657 1200 : mnstr_printf(fout, ")");
658 : }
659 1278 : if (rel->op == op_groupby) /* group by columns */
660 123 : exps_print(sql, fout, rel->r, depth, refs, 1, 0, decorate, 0);
661 1278 : exps_print(sql, fout, rel->exps, depth, refs, 1, 0, decorate, 0);
662 1278 : if (rel->r && rel->op == op_project) /* order by columns */
663 87 : exps_print(sql, fout, rel->r, depth, refs, 1, 0, decorate, 0);
664 : break;
665 13 : case op_insert:
666 : case op_update:
667 : case op_delete:
668 : case op_truncate:
669 : case op_merge: {
670 :
671 13 : if (rel->op == op_insert)
672 3 : mnstr_printf(fout, "insert(");
673 10 : else if (rel->op == op_update)
674 3 : mnstr_printf(fout, "update(");
675 7 : else if (rel->op == op_delete)
676 2 : mnstr_printf(fout, "delete(");
677 5 : else if (rel->op == op_merge)
678 0 : mnstr_printf(fout, "merge(");
679 5 : else if (rel->op == op_truncate) {
680 5 : assert(list_length(rel->exps) == 2);
681 5 : sql_exp *first = (sql_exp*) rel->exps->h->data, *second = (sql_exp*) rel->exps->h->next->data;
682 5 : int restart_sequences = ((atom*)first->l)->data.val.ival,
683 5 : drop_action = ((atom*)second->l)->data.val.ival;
684 13 : mnstr_printf(fout, "truncate %s identity, %s(", restart_sequences ? "restart" : "continue",
685 : drop_action ? "cascade" : "restrict");
686 : }
687 :
688 13 : if (rel->l) {
689 13 : if (rel_is_ref(rel->l)) {
690 5 : int nr = find_ref(refs, rel->l);
691 5 : print_indent(sql, fout, depth+1, decorate);
692 5 : mnstr_printf(fout, "& REF %d ", nr);
693 : } else
694 8 : rel_print_rel(sql, fout, rel->l, depth+1, refs, decorate);
695 : }
696 13 : if (rel->r) {
697 8 : if (rel_is_ref(rel->r)) {
698 0 : int nr = find_ref(refs, rel->r);
699 0 : print_indent(sql, fout, depth+1, decorate);
700 0 : mnstr_printf(fout, "& REF %d ", nr);
701 : } else
702 8 : rel_print_rel(sql, fout, rel->r, depth+1, refs, decorate);
703 : }
704 13 : print_indent(sql, fout, depth, decorate);
705 13 : mnstr_printf(fout, ")");
706 13 : if (rel->op != op_truncate && rel->op != op_merge && rel->exps)
707 3 : exps_print(sql, fout, rel->exps, depth, refs, 1, 0, decorate, 0);
708 : } break;
709 : default:
710 0 : assert(0);
711 : }
712 2384 : if (decorate && rel->p) {
713 3280 : for (prop *p = rel->p; p; p = p->p) {
714 1640 : if (p->kind != PROP_COUNT || (ATOMIC_GET(&GDKdebug) & TESTINGMASK) == 0) {
715 12 : char *pv = propvalue2string(sql->ta, p);
716 12 : mnstr_printf(fout, " %s %s", propkind2string(p), pv);
717 : }
718 : }
719 : }
720 : }
721 :
722 : void
723 2409 : rel_print_refs(mvc *sql, stream* fout, sql_rel *rel, int depth, list *refs, int decorate)
724 : {
725 2409 : if (!rel)
726 : return;
727 2409 : switch (rel->op) {
728 804 : case op_basetable:
729 : case op_table:
730 804 : if (rel->op == op_table && rel->l && rel->flag != TRIGGER_WRAPPER) {
731 17 : rel_print_refs(sql, fout, rel->l, depth, refs, decorate);
732 17 : if (rel_is_ref(rel->l) && !find_ref(refs, rel->l)) {
733 0 : rel_print_rel(sql, fout, rel->l, depth, refs, decorate);
734 0 : list_append(refs, rel->l);
735 : }
736 : }
737 : break;
738 2 : case op_ddl:
739 2 : if (rel->flag == ddl_list || rel->flag == ddl_exception) {
740 0 : if (rel->l) {
741 0 : rel_print_refs(sql, fout, rel->l, depth, refs, decorate);
742 0 : if (rel_is_ref(rel->l) && !find_ref(refs, rel->l)) {
743 0 : rel_print_rel(sql, fout, rel->l, depth, refs, decorate);
744 0 : list_append(refs, rel->l);
745 : }
746 : }
747 0 : if (rel->r) {
748 0 : rel_print_refs(sql, fout, rel->r, depth, refs, decorate);
749 0 : if (rel_is_ref(rel->r) && !find_ref(refs, rel->r)) {
750 0 : rel_print_rel(sql, fout, rel->r, depth, refs, decorate);
751 0 : list_append(refs, rel->r);
752 : }
753 : }
754 : }
755 : break;
756 265 : case op_join:
757 : case op_left:
758 : case op_right:
759 : case op_full:
760 : case op_semi:
761 : case op_anti:
762 : case op_union:
763 : case op_inter:
764 : case op_except:
765 265 : if (rel->l)
766 265 : rel_print_refs(sql, fout, rel->l, depth, refs, decorate);
767 265 : if (rel->r)
768 265 : rel_print_refs(sql, fout, rel->r, depth, refs, decorate);
769 269 : if (rel->l && rel_is_ref(rel->l) && !find_ref(refs, rel->l)) {
770 0 : rel_print_rel(sql, fout, rel->l, depth, refs, decorate);
771 0 : list_append(refs, rel->l);
772 : }
773 265 : if (rel->r && rel_is_ref(rel->r) && !find_ref(refs, rel->r)) {
774 0 : rel_print_rel(sql, fout, rel->r, depth, refs, decorate);
775 0 : list_append(refs, rel->r);
776 : }
777 : break;
778 1284 : case op_project:
779 : case op_select:
780 : case op_groupby:
781 : case op_topn:
782 : case op_sample:
783 1284 : if (rel->l)
784 1206 : rel_print_refs(sql, fout, rel->l, depth, refs, decorate);
785 1291 : if (rel->l && rel_is_ref(rel->l) && !find_ref(refs, rel->l)) {
786 2 : rel_print_rel(sql, fout, rel->l, depth, refs, decorate);
787 2 : list_append(refs, rel->l);
788 : }
789 : break;
790 41 : case op_munion:
791 41 : assert(rel->l);
792 145 : for (node *n = ((list*)rel->l)->h; n; n = n->next) {
793 : // TODO: do we need to check n->data?
794 104 : if (n->data)
795 104 : rel_print_refs(sql, fout, n->data, depth, refs, decorate);
796 104 : if (n->data && rel_is_ref(n->data) && !find_ref(refs, n->data)) {
797 0 : rel_print_rel(sql, fout, n->data, depth, refs, decorate);
798 0 : list_append(refs, n->data);
799 : }
800 : }
801 : break;
802 13 : case op_insert:
803 : case op_update:
804 : case op_delete:
805 : case op_truncate:
806 : case op_merge:
807 13 : if (rel->l)
808 13 : rel_print_refs(sql, fout, rel->l, depth, refs, decorate);
809 18 : if (rel->l && rel_is_ref(rel->l) && !find_ref(refs, rel->l)) {
810 5 : rel_print_rel(sql, fout, rel->l, depth, refs, decorate);
811 5 : list_append(refs, rel->l);
812 : }
813 13 : if (rel->r)
814 8 : rel_print_refs(sql, fout, rel->r, depth, refs, decorate);
815 13 : if (rel->r && rel_is_ref(rel->r) && !find_ref(refs, rel->r)) {
816 0 : rel_print_rel(sql, fout, rel->r, depth, refs, decorate);
817 0 : list_append(refs, rel->r);
818 : }
819 : break;
820 : }
821 : }
822 :
823 : void
824 531 : rel_print_(mvc *sql, stream *fout, sql_rel *rel, int depth, list *refs, int decorate)
825 : {
826 531 : rel_print_rel(sql, fout, rel, depth, refs, decorate);
827 531 : if (sql->runs) {
828 0 : for (int i = 0 ; i < NSQLREWRITERS ; i++) {
829 0 : sql_optimizer_run *run = &(sql->runs[i]);
830 :
831 0 : if (run->name) { /* if name is set, then the optimizer did run */
832 0 : print_indent(sql, fout, depth, decorate);
833 0 : mnstr_printf(fout, "# %-36s %3d actions " LLFMT " usec",
834 : run->name, run->nchanges, run->time);
835 : }
836 : }
837 : }
838 531 : }
839 :
840 : static void
841 31896 : skipWS( char *r, int *pos)
842 : {
843 47056 : while(r[*pos] && (isspace((unsigned char) r[*pos]) || r[*pos] == '|'))
844 15160 : (*pos)++;
845 31896 : }
846 :
847 : static void
848 0 : skipUntilWS( char *r, int *pos)
849 : {
850 0 : while(r[*pos] && (!isspace((unsigned char) r[*pos]) || r[*pos] == '|'))
851 0 : (*pos)++;
852 0 : }
853 :
854 : static void
855 11414 : skipIdent( char *r, int *pos)
856 : {
857 11414 : if (r[*pos] == '"') {
858 10715 : (*pos)++;
859 59951 : while (r[*pos] && r[*pos] != '"') {
860 : /* We send escaped '"' and '\' characters */
861 49236 : if (r[*pos] == '\\' && (r[*pos + 1] == '"' || r[*pos + 1] == '\\'))
862 12 : (*pos)+=2;
863 : else
864 49224 : (*pos)++;
865 : }
866 : } else {
867 3445 : while(r[*pos] && (isalnum((unsigned char) r[*pos]) || r[*pos] == '_' || r[*pos] == '%'))
868 2746 : (*pos)++;
869 : }
870 11414 : }
871 :
872 : static void
873 11604 : convertIdent(char *r)
874 : {
875 11604 : int i = 0, j = 0;
876 64692 : while (r[i] && r[i] != '"') {
877 : /* We send escaped '"' and '\' characters */
878 53088 : if (r[i] == '\\' && (r[i + 1] == '"' || r[i + 1] == '\\')) {
879 12 : r[j++] = r[i + 1];
880 12 : i+=2;
881 : } else {
882 53076 : r[j++] = r[i++];
883 : }
884 : }
885 11604 : r[j] = '\0';
886 11604 : }
887 :
888 : static void
889 3590 : skipIdentOrSymbol( char *r, int *pos)
890 : {
891 3590 : if (r[*pos] == '"') {
892 3590 : skipIdent(r, pos);
893 : } else {
894 0 : while(r[*pos] && (isalnum((unsigned char) r[*pos]) ||
895 : r[*pos] == '=' ||
896 : r[*pos] == '_' || r[*pos] == '%' ||
897 : r[*pos] == '<' || r[*pos] == '>' ||
898 : r[*pos] == '/' || r[*pos] == '*' ||
899 : r[*pos] == '-' || r[*pos] == '+' ||
900 : r[*pos] == '~' || r[*pos] == '^' ))
901 0 : (*pos)++;
902 : }
903 3589 : }
904 :
905 : static int
906 517 : readInt( char *r, int *pos)
907 : {
908 517 : int res = 0;
909 :
910 1294 : while (isdigit((unsigned char) r[*pos])) {
911 777 : res *= 10;
912 777 : res += r[*pos]-'0';
913 777 : (*pos)++;
914 : }
915 517 : return res;
916 : }
917 :
918 : static void *
919 602 : readAtomString(int localtype, char *r, int *pos)
920 : {
921 602 : void *res = NULL;
922 602 : size_t nbytes = 0;
923 602 : int firstpos = 0, rtype = ATOMstorage(localtype) == TYPE_str ? TYPE_str : localtype;
924 :
925 : /* TODO I had issues with the 'external' flag on the JSONfromString function, maybe something is missing there? */
926 602 : assert(r[*pos] == '"'); /* skip first '"' */
927 602 : (*pos)++;
928 :
929 602 : firstpos = *pos;
930 602 : if (rtype == TYPE_str) /* string reads require double quotes at the beginning */
931 167 : firstpos--;
932 3085 : while (r[*pos] && r[*pos] != '"') { /* compute end of atom string */
933 2483 : if (r[*pos] == '\\')
934 9 : (*pos)+=2;
935 : else
936 2474 : (*pos)++;
937 : }
938 602 : if (!r[*pos])
939 : return NULL;
940 :
941 602 : assert(r[*pos] == '"'); /* skip second '"' */
942 602 : if (rtype != TYPE_str) /* string reads require double quotes at the end */
943 435 : r[*pos] = '\0';
944 602 : (*pos)++;
945 :
946 602 : if (ATOMfromstr(rtype, &res, &nbytes, r + firstpos, true) < 0) {
947 0 : GDKfree(res);
948 0 : return NULL;
949 : }
950 602 : return res;
951 : }
952 :
953 : static sql_exp*
954 8414 : read_prop(mvc *sql, sql_exp *exp, char *r, int *pos, bool *found)
955 : {
956 : /* PROPs */
957 8414 : if (strncmp(r+*pos, "JOINIDX", strlen("JOINIDX")) == 0) {
958 0 : char *sname, *tname, *iname;
959 0 : sql_schema *s = NULL;
960 0 : prop *p;
961 :
962 0 : (*pos)+= (int) strlen("JOINIDX");
963 0 : skipWS(r, pos);
964 : /* schema.table.index */
965 0 : sname = r+*pos + 1;
966 0 : skipIdent(r,pos);
967 0 : convertIdent(sname);
968 0 : (*pos)++;
969 0 : if (r[*pos] != '.')
970 0 : return sql_error(sql, -1, SQLSTATE(42000) "JOINIDX: missing '.'\n");
971 0 : (*pos)++;
972 0 : tname = r+*pos + 1;
973 0 : skipIdent(r,pos);
974 0 : convertIdent(tname);
975 0 : (*pos)++;
976 0 : if (r[*pos] != '.')
977 0 : return sql_error(sql, -1, SQLSTATE(42000) "JOINIDX: missing '.'\n");
978 0 : (*pos)++;
979 0 : iname = r+*pos + 1;
980 0 : skipIdent(r,pos);
981 0 : convertIdent(iname);
982 0 : (*pos)++;
983 :
984 0 : (void) tname;
985 0 : s = mvc_bind_schema(sql, sname);
986 0 : if (sname && !s)
987 0 : return sql_error(sql, -1, SQLSTATE(42000) "Schema %s missing\n", sname);
988 0 : if (!find_prop(exp->p, PROP_JOINIDX)) {
989 0 : p = exp->p = prop_create(sql->sa, PROP_JOINIDX, exp->p);
990 0 : if (!(p->value.pval = mvc_bind_idx(sql, s, iname)))
991 0 : return sql_error(sql, -1, SQLSTATE(42000) "Index %s missing\n", iname);
992 : }
993 0 : skipWS(r,pos);
994 0 : if (found)
995 0 : *found = true;
996 : }
997 : return exp;
998 : }
999 :
1000 : static list*
1001 1255 : read_exps(mvc *sql, sql_rel *lrel, sql_rel *rrel, list *top_exps, char *r, int *pos, char bracket, int grp, int top)
1002 : {
1003 1255 : list *exps = new_exp_list(sql->sa);
1004 1255 : sql_exp *e;
1005 1255 : char ebracket = (bracket == '[')?']':')';
1006 :
1007 1255 : if (r[*pos] == bracket) {
1008 1255 : skipWS( r, pos);
1009 :
1010 1255 : (*pos)++;
1011 1255 : skipWS( r, pos);
1012 1808 : e = exp_read(sql, lrel, rrel, top ? exps : top_exps, r, pos, grp);
1013 1255 : if (!e && r[*pos] != ebracket) {
1014 1 : return sql_error(sql, -1, SQLSTATE(42000) "Missing closing %c\n", ebracket);
1015 86 : } else if (!e) {
1016 86 : (*pos)++;
1017 86 : skipWS(r, pos);
1018 86 : return sql->errstr[0] ? NULL : exps; /* A function call might not have any input expressions, so return empty exps on that case */
1019 : }
1020 1168 : append(exps, e);
1021 1168 : skipWS( r, pos);
1022 1168 : if (!read_prop(sql, e, r, pos, NULL))
1023 : return NULL;
1024 4079 : while (r[*pos] == ',') {
1025 :
1026 2910 : (*pos)++;
1027 2910 : skipWS( r, pos);
1028 2910 : e = exp_read(sql, lrel, rrel, top ? exps : top_exps, r, pos, grp);
1029 2911 : if (!e)
1030 : return NULL;
1031 2911 : append(exps, e);
1032 2911 : skipWS( r, pos);
1033 2911 : if (!read_prop(sql, e, r, pos, NULL))
1034 : return NULL;
1035 : }
1036 1169 : if (r[*pos] != ebracket)
1037 0 : return sql_error(sql, -1, SQLSTATE(42000) "Missing closing %c\n", ebracket);
1038 1169 : (*pos)++;
1039 1169 : skipWS( r, pos);
1040 : }
1041 : return exps;
1042 : }
1043 :
1044 : static sql_exp*
1045 52 : exp_read_min_or_max(mvc *sql, sql_exp *exp, char *r, int *pos, const char *prop_str, rel_prop kind)
1046 : {
1047 52 : atom *a;
1048 52 : sql_subtype *tpe = exp_subtype(exp);
1049 :
1050 52 : (*pos)+= (int) strlen(prop_str);
1051 52 : skipWS(r, pos);
1052 :
1053 52 : if (strncmp(r+*pos, "NULL", strlen("NULL")) == 0) {
1054 0 : (*pos)+= (int) strlen("NULL");
1055 0 : a = atom_general(sql->sa, tpe, NULL, 0);
1056 : } else {
1057 52 : void *ptr = readAtomString(tpe->type->localtype, r, pos);
1058 52 : if (!ptr)
1059 0 : return sql_error(sql, -1, SQLSTATE(42000) "Invalid atom string\n");
1060 52 : a = atom_general_ptr(sql->sa, tpe, ptr);
1061 52 : GDKfree(ptr);
1062 : }
1063 52 : if (!find_prop(exp->p, kind)) {
1064 4 : prop *p = exp->p = prop_create(sql->sa, kind, exp->p);
1065 4 : p->value.pval = a;
1066 : }
1067 52 : skipWS(r, pos);
1068 52 : return exp;
1069 : }
1070 :
1071 : static sql_exp*
1072 30 : exp_read_nuniques(mvc *sql, sql_exp *exp, char *r, int *pos)
1073 : {
1074 30 : void *ptr = NULL;
1075 30 : size_t nbytes = 0;
1076 30 : ssize_t res = 0;
1077 30 : sql_subtype *tpe = sql_bind_localtype("dbl");
1078 :
1079 30 : (*pos)+= (int) strlen("NUNIQUES");
1080 30 : skipWS(r, pos);
1081 :
1082 30 : if ((res = ATOMfromstr(tpe->type->localtype, &ptr, &nbytes, r + *pos, true)) < 0) {
1083 0 : GDKfree(ptr);
1084 0 : return sql_error(sql, -1, SQLSTATE(42000) "Invalid atom string\n");
1085 : }
1086 :
1087 30 : if (!find_prop(exp->p, PROP_NUNIQUES)) {
1088 4 : prop *p = exp->p = prop_create(sql->sa, PROP_NUNIQUES, exp->p);
1089 4 : p->value.dval = *(dbl*)ptr;
1090 : }
1091 30 : (*pos) += (int) res; /* it should always fit */
1092 30 : GDKfree(ptr);
1093 30 : skipWS(r, pos);
1094 30 : return exp;
1095 : }
1096 :
1097 : static sql_exp*
1098 4254 : read_exp_properties(mvc *sql, sql_exp *exp, char *r, int *pos)
1099 : {
1100 4254 : bool found = true;
1101 8589 : while (found) {
1102 4335 : found = false;
1103 :
1104 4335 : if (strncmp(r+*pos, "COUNT", strlen("COUNT")) == 0) {
1105 0 : (*pos)+= (int) strlen("COUNT");
1106 0 : if (!find_prop(exp->p, PROP_COUNT))
1107 0 : exp->p = prop_create(sql->sa, PROP_COUNT, exp->p);
1108 0 : skipWS(r,pos);
1109 0 : found = true;
1110 4335 : } else if (strncmp(r+*pos, "HASHIDX", strlen("HASHIDX")) == 0) {
1111 0 : (*pos)+= (int) strlen("HASHIDX");
1112 0 : if (!find_prop(exp->p, PROP_HASHIDX))
1113 0 : exp->p = prop_create(sql->sa, PROP_HASHIDX, exp->p);
1114 0 : skipWS(r,pos);
1115 0 : found = true;
1116 4335 : } else if (strncmp(r+*pos, "HASHCOL", strlen("HASHCOL")) == 0) {
1117 0 : (*pos)+= (int) strlen("HASHCOL");
1118 0 : if (!find_prop(exp->p, PROP_HASHCOL))
1119 0 : exp->p = prop_create(sql->sa, PROP_HASHCOL, exp->p);
1120 0 : skipWS(r,pos);
1121 0 : found = true;
1122 4335 : } else if (strncmp(r+*pos, "MIN", strlen("MIN")) == 0) {
1123 26 : if (!exp_read_min_or_max(sql, exp, r, pos, "MIN", PROP_MIN))
1124 : return NULL;
1125 26 : found = true;
1126 4309 : } else if (strncmp(r+*pos, "MAX", strlen("MAX")) == 0) {
1127 26 : if (!exp_read_min_or_max(sql, exp, r, pos, "MAX", PROP_MAX))
1128 : return NULL;
1129 26 : found = true;
1130 4283 : } else if (strncmp(r+*pos, "NUNIQUES", strlen("NUNIQUES")) == 0) {
1131 30 : if (!exp_read_nuniques(sql, exp, r, pos))
1132 : return NULL;
1133 30 : found = true;
1134 : }
1135 4335 : if (!read_prop(sql, exp, r, pos, &found))
1136 : return NULL;
1137 : }
1138 : return exp;
1139 : }
1140 :
1141 : static sql_exp*
1142 465 : parse_atom(mvc *sql, char *r, int *pos, sql_subtype *tpe)
1143 : {
1144 465 : if (strncmp(r+*pos, "NULL", strlen("NULL")) == 0) {
1145 30 : (*pos)+= (int) strlen("NULL");
1146 30 : return exp_atom(sql->sa, atom_general(sql->sa, tpe, NULL, 0));
1147 : } else {
1148 435 : void *ptr = readAtomString(tpe->type->localtype, r, pos);
1149 435 : if (!ptr)
1150 0 : return sql_error(sql, -1, SQLSTATE(42000) "Invalid atom string\n");
1151 435 : sql_exp *res = exp_atom(sql->sa, atom_general_ptr(sql->sa, tpe, ptr));
1152 435 : GDKfree(ptr);
1153 435 : return res;
1154 : }
1155 : }
1156 :
1157 : static sql_exp*
1158 1 : function_error_string(mvc *sql, const char *schema, const char *fname, list *exps, bool found, sql_ftype type)
1159 : {
1160 1 : char *arg_list = NULL, *F = NULL, *fn = NULL;
1161 :
1162 1 : FUNC_TYPE_STR(type, F, fn)
1163 :
1164 1 : (void) F;
1165 1 : if (!list_empty(exps)) {
1166 2 : for (node *n = exps->h; n ; n = n->next) {
1167 1 : sql_subtype *t = exp_subtype(n->data);
1168 1 : char *tpe = t ? sql_subtype_string(sql->ta, t) : "?";
1169 :
1170 1 : if (arg_list) {
1171 0 : arg_list = sa_message(sql->ta, "%s, %s", arg_list, tpe);
1172 : } else {
1173 : arg_list = tpe;
1174 : }
1175 : }
1176 : }
1177 2 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42000) "%s %s %s%s%s'%s'(%s)",
1178 : found ? "Insufficient privileges for" : "No such", fn, schema ? "'":"", schema ? schema : "",
1179 : schema ? "'.":"", fname, arg_list ? arg_list : "");
1180 : }
1181 :
1182 : static unsigned int /* keep updating the label count */
1183 3082 : try_update_label_count(mvc *sql, const char *label)
1184 : {
1185 3082 : if (label && label[0] == '%' && isdigit((unsigned char) label[1])) {
1186 369 : char *eptr = NULL;
1187 369 : unsigned int value = (unsigned int) strtol(label + 1, &eptr, 10);
1188 368 : if (eptr && eptr[0] == '\0') {
1189 368 : sql->label = MAX(sql->label, value);
1190 368 : return value;
1191 : }
1192 : }
1193 : return 0;
1194 : }
1195 :
1196 : sql_exp*
1197 4342 : exp_read(mvc *sql, sql_rel *lrel, sql_rel *rrel, list *top_exps, char *r, int *pos, int grp)
1198 : {
1199 4342 : int old, d=0, s=0, unique = 0, no_nils = 0, quote = 0, zero_if_empty = 0;
1200 4342 : char *tname = NULL, *cname = NULL, *var_cname = NULL, *e, *b = r + *pos;
1201 4342 : sql_exp *exp = NULL;
1202 4342 : list *exps = NULL;
1203 4342 : sql_type *t = NULL;
1204 4342 : sql_subtype tpe;
1205 :
1206 4342 : quote = (r[*pos] == '"');
1207 4342 : b += quote;
1208 4342 : skipIdent(r, pos);
1209 4342 : e = r+*pos;
1210 4342 : (*pos) += quote;
1211 4342 : skipWS(r, pos);
1212 4342 : switch(r[*pos]) {
1213 3590 : case '.':
1214 3590 : *e = 0;
1215 3590 : (*pos)++;
1216 3590 : tname = b;
1217 3590 : convertIdent(tname);
1218 3590 : cname = r + *pos + quote;
1219 3590 : skipIdentOrSymbol(r, pos);
1220 3589 : e = r+*pos;
1221 3589 : if (quote) {
1222 3589 : old = ' ';
1223 3589 : convertIdent(cname);
1224 : } else {
1225 0 : old = *e;
1226 : }
1227 3590 : *e = 0;
1228 :
1229 3590 : tname = sa_strdup(sql->sa, tname);
1230 3590 : cname = sa_strdup(sql->sa, cname);
1231 3590 : *e = old;
1232 3590 : skipWS(r, pos);
1233 3590 : if (r[*pos] != '(') { /* if there's a function/aggregate call next don't attempt to bind columns */
1234 3235 : if (top_exps) {
1235 3078 : exp = exps_bind_column2(top_exps, tname, cname, NULL);
1236 3078 : if (exp)
1237 36 : exp = exp_ref(sql, exp);
1238 : }
1239 3235 : if (!exp && lrel) {
1240 3199 : exp = rel_bind_column2(sql, lrel, tname, cname, 0);
1241 3199 : if (!exp && rrel)
1242 19 : exp = rel_bind_column2(sql, rrel, tname, cname, 0);
1243 36 : } else if (!exp) {
1244 0 : exp = exp_column(sql->sa, tname, cname, NULL, CARD_ATOM, 1, 0, cname[0] == '%');
1245 0 : exp->alias.label = -(sql->nid++);
1246 : }
1247 : }
1248 : break;
1249 : /* atom */
1250 557 : case '(':
1251 557 : if (b == (r+*pos)) { /* comparison expression */
1252 85 : int anti = 0, sym = 0, semantics = 0, any = 0;
1253 85 : comp_type ctype = cmp_all, ctype2 = cmp_all;
1254 85 : list *lexps = NULL, *rexps = NULL, *fexps = NULL;
1255 85 : char *sname = NULL, *fname = NULL;
1256 :
1257 85 : if (!(lexps = read_exps(sql, lrel, rrel, top_exps, r, pos, '(', 0, 0)))
1258 : return NULL;
1259 85 : skipWS(r, pos);
1260 85 : if (r[*pos] == '!') {
1261 5 : anti = 1;
1262 5 : (*pos)++;
1263 5 : skipWS(r, pos);
1264 : }
1265 85 : if (r[*pos] == '*') {
1266 5 : semantics = 1;
1267 5 : (*pos)++;
1268 5 : skipWS(r, pos);
1269 : }
1270 85 : if (r[*pos] == '+') {
1271 1 : any = 1;
1272 1 : (*pos)++;
1273 1 : skipWS(r, pos);
1274 : }
1275 :
1276 85 : switch(r[*pos]) {
1277 1 : case 'n':
1278 1 : if (strncmp(r+*pos, "notin", strlen("notin")) == 0) {
1279 1 : (*pos)+= (int) strlen("notin");
1280 1 : ctype = cmp_notin;
1281 : }
1282 : break;
1283 3 : case 'F':
1284 3 : if (strncmp(r+*pos, "FILTER", strlen("FILTER")) == 0) {
1285 3 : (*pos)+= (int) strlen("FILTER");
1286 3 : ctype = cmp_filter;
1287 3 : skipWS(r, pos);
1288 3 : sname = r+*pos + 1;
1289 3 : skipIdent(r, pos);
1290 3 : convertIdent(sname);
1291 3 : (*pos)+=2;
1292 3 : fname = r+*pos + 1;
1293 3 : skipIdent(r, pos);
1294 3 : convertIdent(fname);
1295 3 : (*pos)++;
1296 : }
1297 : break;
1298 1 : case 'i':
1299 1 : if (strncmp(r+*pos, "in", strlen("in")) == 0) {
1300 1 : (*pos)+= (int) strlen("in");
1301 1 : ctype = cmp_in;
1302 : }
1303 : break;
1304 4 : case 'o':
1305 4 : if (strncmp(r+*pos, "or", strlen("or")) == 0) {
1306 4 : (*pos)+= (int) strlen("or");
1307 4 : ctype = cmp_or;
1308 : }
1309 : break;
1310 0 : case '!':
1311 0 : ctype = cmp_notequal;
1312 0 : (*pos)++;
1313 0 : if (r[(*pos)] == '=')
1314 0 : (*pos)++;
1315 : break;
1316 45 : case '=':
1317 45 : ctype = cmp_equal;
1318 45 : (*pos)++;
1319 45 : break;
1320 22 : case '<':
1321 22 : ctype = cmp_lt;
1322 22 : (*pos)++;
1323 22 : if (r[(*pos)] == '=') {
1324 15 : ctype = cmp_lte;
1325 15 : (*pos)++;
1326 : }
1327 : break;
1328 9 : case '>':
1329 9 : ctype = cmp_gt;
1330 9 : (*pos)++;
1331 9 : if (r[(*pos)] == '=') {
1332 3 : ctype = cmp_gte;
1333 3 : (*pos)++;
1334 : }
1335 : break;
1336 0 : default:
1337 0 : return sql_error(sql, -1, SQLSTATE(42000) "Type: missing comparison type\n");
1338 : }
1339 :
1340 85 : skipWS(r, pos);
1341 85 : if (!(rexps = read_exps(sql, lrel, rrel, top_exps, r, pos, '(', 0, 0)))
1342 : return NULL;
1343 85 : skipWS(r, pos);
1344 :
1345 85 : switch (ctype) {
1346 76 : case cmp_gt:
1347 : case cmp_gte:
1348 : case cmp_lte:
1349 : case cmp_lt:
1350 : case cmp_equal:
1351 : case cmp_notequal:
1352 76 : if (r[*pos] == '!' || r[*pos] == '<' || r[*pos] == '>') { /* BETWEEN case */
1353 17 : if (r[*pos] == '!') { /* ignore next anti */
1354 2 : (*pos)++;
1355 2 : skipWS(r, pos);
1356 : }
1357 17 : switch(r[*pos]) {
1358 17 : case '<':
1359 17 : ctype2 = cmp_lt;
1360 17 : (*pos)++;
1361 17 : if (r[(*pos)] == '=') {
1362 14 : ctype2 = cmp_lte;
1363 14 : (*pos)++;
1364 : }
1365 : break;
1366 0 : case '>':
1367 0 : ctype2 = cmp_gt;
1368 0 : (*pos)++;
1369 0 : if (r[(*pos)] == '=') {
1370 0 : ctype2 = cmp_gte;
1371 0 : (*pos)++;
1372 : }
1373 : break;
1374 0 : default:
1375 0 : return sql_error(sql, -1, SQLSTATE(42000) "Type: missing comparison type\n");
1376 : }
1377 17 : skipWS(r, pos);
1378 17 : if (!(fexps = read_exps(sql, lrel, rrel, top_exps, r, pos, '(', 0, 0)))
1379 : return NULL;
1380 17 : skipWS(r, pos);
1381 17 : if (strncmp(r+*pos, "SYM", strlen("SYM")) == 0) {
1382 1 : (*pos)+= (int) strlen("SYM");
1383 1 : skipWS(r, pos);
1384 1 : sym = 1;
1385 : }
1386 17 : exp = exp_compare2(sql->sa, rexps->h->data, lexps->h->data, fexps->h->data, compare2range(swap_compare(ctype), ctype2), sym);
1387 : } else {
1388 59 : exp = exp_compare(sql->sa, lexps->h->data, rexps->h->data, ctype);
1389 59 : if (semantics)
1390 5 : set_semantics(exp);
1391 59 : if (any)
1392 1 : set_any(exp);
1393 : }
1394 76 : if (anti)
1395 5 : set_anti(exp);
1396 76 : assert(list_length(lexps) == 1 && list_length(rexps) == 1 && (!fexps || list_length(fexps) == 1));
1397 : break;
1398 2 : case cmp_in:
1399 : case cmp_notin:
1400 2 : assert(list_length(lexps) == 1);
1401 2 : exp = exp_in(sql->sa, lexps->h->data, rexps, ctype);
1402 2 : if (anti)
1403 0 : set_anti(exp);
1404 : break;
1405 3 : case cmp_filter: {
1406 3 : sql_subfunc *f = NULL;
1407 3 : list *tl = sa_list(sql->sa);
1408 :
1409 3 : if (!list_empty(lexps)) {
1410 6 : for (node *n = lexps->h; n; n = n->next){
1411 3 : sql_exp *e = n->data;
1412 :
1413 3 : list_append(tl, exp_subtype(e));
1414 : }
1415 : }
1416 3 : if (!list_empty(rexps)) {
1417 12 : for (node *n = rexps->h; n; n = n->next){
1418 9 : sql_exp *e = n->data;
1419 :
1420 9 : list_append(tl, exp_subtype(e));
1421 : }
1422 : }
1423 :
1424 3 : if (sname && !mvc_bind_schema(sql, sname))
1425 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(3F000) "No such schema '%s'\n", sname);
1426 3 : if (!(f = sql_bind_func_(sql, sname, fname, tl, F_FILT, true, false)))
1427 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42000) "Filter: missing function '%s'.'%s'\n", sname, fname);
1428 3 : if (!execute_priv(sql, f->func))
1429 0 : return sql_error(sql, -1, SQLSTATE(42000) "Filter: no privilege to call filter function '%s'.'%s'\n", sname, fname);
1430 3 : exp = exp_filter(sql->sa, lexps, rexps, f, anti);
1431 3 : } break;
1432 4 : case cmp_or:
1433 4 : exp = exp_or(sql->sa, lexps, rexps, anti);
1434 4 : break;
1435 0 : default:
1436 0 : return sql_error(sql, -1, SQLSTATE(42000) "Type: missing comparison type\n");
1437 : }
1438 : break;
1439 : }
1440 : /* fall through */
1441 : case '[':
1442 483 : tname = b;
1443 483 : if (tname && *tname == '[') { /* list of values */
1444 4 : if (!(exps = read_exps(sql, lrel, rrel, top_exps, r, pos, '[', 0, 0)))
1445 : return NULL;
1446 4 : exp = exp_values(sql->sa, exps);
1447 : } else {
1448 479 : old = *e;
1449 479 : *e = 0;
1450 479 : if (old != '[') {
1451 472 : (*pos)++;
1452 472 : d = readInt(r,pos);
1453 472 : if (r[*pos] != ')' && r[*pos] != ',')
1454 0 : return sql_error(sql, -1, SQLSTATE(42000) "Type: missing ')' or ','\n");
1455 472 : if (r[*pos] == ',') {
1456 29 : (*pos)++;
1457 29 : s = readInt(r,pos);
1458 : }
1459 472 : if (r[*pos] != ')')
1460 0 : return sql_error(sql, -1, SQLSTATE(42000) "Type: missing ')'\n");
1461 472 : (*pos)++;
1462 : }
1463 479 : convertIdent(tname);
1464 479 : if (!sql_find_subtype(&tpe, tname, d, s)) {
1465 0 : if (!(t = mvc_bind_type(sql, tname))) /* try an external type */
1466 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42000) "SQL type %s(%d, %d) not found\n", tname, d, s);
1467 0 : sql_init_subtype(&tpe, t, d, s);
1468 : }
1469 480 : skipWS(r, pos);
1470 480 : *e = old;
1471 480 : if (r[*pos] == '[') { /* convert */
1472 61 : (*pos)++;
1473 61 : skipWS(r, pos);
1474 61 : if (!(exp = exp_read(sql, lrel, rrel, top_exps, r, pos, 0)))
1475 : return NULL;
1476 60 : if (r[*pos] != ']')
1477 0 : return sql_error(sql, -1, SQLSTATE(42000) "Convert: missing ']'\n");
1478 60 : (*pos)++;
1479 60 : skipWS(r, pos);
1480 60 : exp = exp_convert(sql, exp, exp_subtype(exp), &tpe);
1481 : } else {
1482 419 : if (!(exp = parse_atom(sql, r, pos, &tpe)))
1483 : return NULL;
1484 419 : skipWS(r, pos);
1485 : }
1486 : }
1487 : break;
1488 54 : case '\"':
1489 : case 'N': /* for NULL values, but 'NOT NULL' and 'NULLS LAST' cannot match here */
1490 54 : if (r[*pos] == '\"' || (strncmp(r+*pos, "NULL", strlen("NULL")) == 0 && r[*pos+4] != 'S')) {
1491 46 : *e = 0;
1492 46 : tname = b;
1493 46 : convertIdent(tname);
1494 46 : if (!sql_find_subtype(&tpe, tname, 0, 0)) {
1495 18 : if (!(t = mvc_bind_type(sql, tname))) /* try an external type */
1496 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42000) "SQL type %s not found\n", tname);
1497 18 : sql_init_subtype(&tpe, t, 0, 0);
1498 : }
1499 46 : if (!(exp = parse_atom(sql, r, pos, &tpe)))
1500 : return NULL;
1501 46 : skipWS(r, pos);
1502 : }
1503 : break;
1504 4342 : default:
1505 4342 : (void)sql;
1506 : }
1507 :
1508 : /* func or aggr */
1509 4342 : if (grp) {
1510 58 : skipWS(r, pos);
1511 58 : if (r[*pos] == 'u') {
1512 0 : unique = 1;
1513 0 : (*pos)+= (int) strlen("unique");
1514 0 : skipWS(r, pos);
1515 : }
1516 58 : if (r[*pos] == 'n') {
1517 6 : no_nils = 1;
1518 6 : (*pos)+= (int) strlen("no nil");
1519 6 : skipWS(r, pos);
1520 : }
1521 58 : if (r[*pos] == 'z') {
1522 0 : zero_if_empty = 1;
1523 0 : (*pos)+= (int) strlen("zero if empty");
1524 0 : skipWS(r, pos);
1525 : }
1526 : }
1527 4342 : if (r[*pos] == '(') {
1528 361 : sql_subfunc *f = NULL;
1529 :
1530 361 : if (!(exps = read_exps(sql, lrel, rrel, top_exps, r, pos, '(', 0, 0)))
1531 : return NULL;
1532 361 : tname = b;
1533 361 : *e = 0;
1534 361 : convertIdent(tname);
1535 361 : if (tname && !mvc_bind_schema(sql, tname))
1536 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(3F000) "No such schema '%s'\n", tname);
1537 361 : if (grp) {
1538 60 : if (exps && exps->h) {
1539 14 : list *ops = sa_list(sql->sa);
1540 29 : for( node *n = exps->h; n; n = n->next)
1541 15 : append(ops, exp_subtype(n->data));
1542 14 : f = sql_bind_func_(sql, tname, cname, ops, F_AGGR, true, false);
1543 : } else {
1544 32 : f = sql_bind_func(sql, tname, cname, sql_bind_localtype("void"), NULL, F_AGGR, true, true); /* count(*) */
1545 : }
1546 46 : if (!f)
1547 0 : return function_error_string(sql, tname, cname, exps, false, F_AGGR);
1548 46 : if (!execute_priv(sql, f->func))
1549 0 : return function_error_string(sql, tname, cname, exps, true, F_AGGR);
1550 46 : exp = exp_aggr(sql->sa, exps, f, unique, no_nils, CARD_ATOM, 1);
1551 45 : if (zero_if_empty)
1552 0 : set_zero_if_empty(exp);
1553 : } else {
1554 315 : int nops = list_length(exps);
1555 315 : if (!strcmp(tname, "sys") && (!strcmp(cname, "case") || !strcmp(cname, "casewhen") || !strcmp(cname, "coalesce") || !strcmp(cname, "nullif"))) {
1556 : /* these functions are bound on a different way */
1557 25 : if ((f = sql_find_func(sql, NULL, cname, 2, F_FUNC, true, NULL))) {
1558 25 : if (!execute_priv(sql, f->func))
1559 0 : return function_error_string(sql, tname, cname, exps, true, F_FUNC);
1560 25 : sql_exp *res = exps->t->data;
1561 25 : sql_subtype *restype = exp_subtype(res), *condtype = NULL;
1562 25 : f->res->h->data = sql_create_subtype(sql->sa, restype->type, restype->digits, restype->scale);
1563 : /* As the inner functions may return smaller types (because of statistics and optimization),
1564 : * ie upcast here */
1565 : /* case exps are lists of (result, condition) ending with single value */
1566 : /* casewhen exps are lists of first (fe) a expression followed by (resultN, valueN) ending with single last result (fe == value1 -> result1 etc else last result */
1567 : /* nullif is list of values */
1568 : /* coalesce is list of values */
1569 25 : bool skip = false;
1570 25 : node *n = exps->h;
1571 25 : if (strcmp(cname, "case") == 0 && n->next) {
1572 25 : skip = true;
1573 25 : n = n->next;
1574 : }
1575 25 : if (strcmp(cname, "casewhen") == 0) {
1576 18 : sql_exp *e = n->data;
1577 18 : condtype = exp_subtype(e);
1578 18 : n = n->next;
1579 : }
1580 82 : for (; n; n = n->next) {
1581 57 : sql_exp *e = n->data;
1582 :
1583 57 : if (condtype && n->next) {
1584 22 : n->data = exp_check_type(sql, condtype, NULL, e, type_equal);
1585 22 : n = n->next;
1586 22 : e = n->data;
1587 : }
1588 57 : n->data = exp_check_type(sql, restype, NULL, e, type_equal);
1589 :
1590 57 : if (skip && n->next && n->next->next)
1591 57 : n = n->next;
1592 : }
1593 : }
1594 : } else {
1595 290 : list *ops = sa_list(sql->sa);
1596 848 : for( node *n = exps->h; n; n = n->next)
1597 558 : append(ops, exp_subtype(n->data));
1598 :
1599 290 : f = sql_bind_func_(sql, tname, cname, ops, F_FUNC, true, false);
1600 289 : if (!f) {
1601 11 : sql->session->status = 0; /* if the function was not found clean the error */
1602 11 : sql->errstr[0] = '\0';
1603 11 : f = sql_bind_func_(sql, tname, cname, ops, F_ANALYTIC, true, false);
1604 : }
1605 289 : if (!f && nops > 1) { /* window functions without frames get 2 extra arguments */
1606 6 : sql->session->status = 0; /* if the function was not found clean the error */
1607 6 : sql->errstr[0] = '\0';
1608 6 : list_remove_node(ops, NULL, ops->t);
1609 6 : list_remove_node(ops, NULL, ops->t);
1610 6 : f = sql_bind_func_(sql, tname, cname, ops, F_ANALYTIC, true, false);
1611 : }
1612 289 : if (!f && nops > 4) { /* window functions with frames get 5 extra arguments */
1613 4 : sql->session->status = 0; /* if the function was not found clean the error */
1614 4 : sql->errstr[0] = '\0';
1615 16 : for (int i = 0 ; i < 3 ; i++)
1616 12 : list_remove_node(ops, NULL, ops->t);
1617 4 : f = sql_bind_func_(sql, tname, cname, ops, F_ANALYTIC, true, false);
1618 : }
1619 289 : if (f)
1620 288 : exps = check_arguments_and_find_largest_any_type(sql, NULL, exps, f, 0, true);
1621 :
1622 288 : if (f && !execute_priv(sql, f->func))
1623 0 : return function_error_string(sql, tname, cname, exps, true, F_FUNC);
1624 : /* apply scale fixes if needed */
1625 288 : if (f && f->func->type != F_ANALYTIC) {
1626 278 : if (list_length(exps) == 2) {
1627 227 : sql_exp *l = exps->h->data;
1628 227 : sql_exp *r = exps->h->next->data;
1629 :
1630 : /* Find converted value type for division and update function output type */
1631 227 : if (f->func->fix_scale == SCALE_DIV) {
1632 2 : sql_subtype *lt = exp_subtype(l);
1633 2 : sql_subtype *rt = exp_subtype(r);
1634 :
1635 2 : if (lt->type->scale == SCALE_FIX && rt->scale && strcmp(sql_func_imp(f->func), "/") == 0) {
1636 : /* TODO move into exps_scale_algebra (with internal flag) */
1637 2 : sql_subtype *res = f->res->h->data;
1638 2 : unsigned int scale = lt->scale - rt->scale;
1639 2 : unsigned int digits = (lt->digits > rt->digits) ? lt->digits : rt->digits;
1640 :
1641 : #ifdef HAVE_HGE
1642 2 : if (res->type->radix == 10 && digits > 38)
1643 2 : digits = 38;
1644 2 : if (res->type->radix == 2 && digits > 127)
1645 2 : digits = 127;
1646 : #else
1647 : if (res->type->radix == 10 && digits > 18)
1648 : digits = 18;
1649 : if (res->type->radix == 2 && digits > 63)
1650 : digits = 63;
1651 : #endif
1652 :
1653 2 : sql_find_subtype(res, lt->type->base.name, digits, scale);
1654 : }
1655 : }
1656 : }
1657 : }
1658 : }
1659 : if (f) {
1660 315 : exp = exp_op(sql->sa, list_empty(exps) ? NULL : exps, f);
1661 312 : if (is_compare_func(f)) { /* has to parse any/all */
1662 142 : skipWS(r,pos);
1663 : /* [ ANY|ALL ] */
1664 142 : if (strncmp(r+*pos, "ANY", strlen("ANY")) == 0) {
1665 0 : (*pos)+= (int) strlen("ANY");
1666 0 : skipWS(r, pos);
1667 0 : exp->flag = 1;
1668 : }
1669 142 : if (strncmp(r+*pos, "ALL", strlen("ALL")) == 0) {
1670 0 : (*pos)+= (int) strlen("ALL");
1671 0 : skipWS(r, pos);
1672 0 : exp->flag = 2;
1673 : }
1674 : }
1675 : } else {
1676 1 : return function_error_string(sql, tname, cname, exps, false, F_FUNC);
1677 : }
1678 : }
1679 : }
1680 :
1681 4338 : if (!exp && lrel && b != e) { /* simple ident */
1682 57 : int amb = 0, mul = 0;
1683 :
1684 57 : old = *e;
1685 57 : *e = 0;
1686 57 : convertIdent(b);
1687 57 : var_cname = sa_strdup(sql->sa, b);
1688 57 : if (top_exps) {
1689 57 : exp = exps_bind_column(top_exps, var_cname, &amb, &mul, 1);
1690 57 : if (exp)
1691 0 : exp = exp_ref(sql, exp);
1692 : }
1693 57 : (void)amb;
1694 57 : (void)mul;
1695 57 : assert(amb == 0 && mul == 0);
1696 57 : if (!exp && lrel)
1697 57 : exp = rel_bind_column(sql, lrel, var_cname, 0, 1);
1698 57 : if (!exp && rrel)
1699 1 : exp = rel_bind_column(sql, rrel, var_cname, 0, 1);
1700 57 : *e = old;
1701 57 : skipWS(r,pos);
1702 : }
1703 :
1704 4338 : if (!exp && (cname || var_cname)) { /* Try a variable */
1705 29 : sql_var *var = NULL;
1706 29 : sql_subtype *tpe = NULL;
1707 29 : int level = 0;
1708 29 : sql_arg *a = NULL;
1709 29 : bool has_tname = cname && tname && strcmp(tname, cname) != 0;
1710 :
1711 54 : if (find_variable_on_scope(sql, has_tname ? tname : NULL, cname ? cname : var_cname, &var, &a, &tpe, &level, "SELECT")) {
1712 28 : if (var) /* if variable is known from the stack or a global var */
1713 31 : exp = exp_param_or_declared(sql->sa, var->sname ? sa_strdup(sql->sa, var->sname) : NULL, sa_strdup(sql->sa, var->name), &(var->var.tpe), level);
1714 28 : if (a) /* if variable is a parameter */
1715 0 : exp = exp_param_or_declared(sql->sa, NULL, sa_strdup(sql->sa, cname), &(a->type), level);
1716 : }
1717 : }
1718 :
1719 114 : if (!exp) {
1720 86 : if (cname) {
1721 1 : bool has_tname = tname && strcmp(tname, cname) != 0;
1722 1 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42000) "Identifier %s%s%s doesn't exist\n", has_tname ? tname : "", has_tname ? "." : "", cname);
1723 85 : } else if (var_cname) {
1724 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42000) "Identifier %s doesn't exist\n", var_cname);
1725 : }
1726 : return NULL;
1727 : }
1728 :
1729 : /* [ PART ] */
1730 4252 : if (strncmp(r+*pos, "PART", strlen("PART")) == 0) {
1731 3 : (*pos)+= (int) strlen("PART");
1732 3 : skipWS(r, pos);
1733 3 : set_partitioning(exp);
1734 : }
1735 : /* [ ASC ] */
1736 4252 : if (strncmp(r+*pos, "ASC", strlen("ASC")) == 0) {
1737 12 : (*pos)+= (int) strlen("ASC");
1738 12 : skipWS(r, pos);
1739 12 : set_ascending(exp);
1740 : }
1741 : /* [ NULLS LAST ] */
1742 4252 : if (strncmp(r+*pos, "NULLS LAST", strlen("NULLS LAST")) == 0) {
1743 9 : (*pos)+= (int) strlen("NULLS LAST");
1744 9 : skipWS(r, pos);
1745 9 : set_nulls_last(exp);
1746 : }
1747 : /* [ NOT NULL ] */
1748 4252 : if (strncmp(r+*pos, "NOT NULL", strlen("NOT NULL")) == 0) {
1749 361 : (*pos)+= (int) strlen("NOT NULL");
1750 361 : skipWS(r, pos);
1751 361 : set_has_no_nil(exp);
1752 : }
1753 : /* [ UNIQUE ] */
1754 4252 : if (strncmp(r+*pos, "UNIQUE", strlen("UNIQUE")) == 0) {
1755 260 : (*pos)+= (int) strlen("UNIQUE");
1756 260 : skipWS(r, pos);
1757 260 : set_unique(exp);
1758 : }
1759 :
1760 4252 : if (!(exp = read_exp_properties(sql, exp, r, pos)))
1761 : return NULL;
1762 :
1763 : /* as alias */
1764 4254 : if (strncmp(r+*pos, "as", 2) == 0) {
1765 1519 : unsigned int rlabel = 0, nlabel = 0;
1766 1519 : (*pos)+=2;
1767 1519 : skipWS(r, pos);
1768 :
1769 1519 : tname = r+*pos+1;
1770 1519 : skipIdent(r, pos);
1771 1519 : convertIdent(tname);
1772 1519 : (*pos)++;
1773 1519 : if (r[*pos] != '.') {
1774 20 : cname = tname;
1775 20 : tname = NULL;
1776 20 : exp_setname(sql, exp, NULL, sa_strdup(sql->sa, cname));
1777 20 : skipWS(r, pos);
1778 : } else {
1779 1499 : (*pos)++;
1780 1499 : cname = r+*pos+1;
1781 1499 : skipIdent(r, pos);
1782 1499 : convertIdent(cname);
1783 1499 : (*pos)++;
1784 1499 : skipWS(r, pos);
1785 1499 : exp_setname(sql, exp, sa_strdup(sql->sa, tname), sa_strdup(sql->sa, cname));
1786 : }
1787 1518 : rlabel = try_update_label_count(sql, tname);
1788 1519 : nlabel = try_update_label_count(sql, cname);
1789 1519 : if (rlabel && rlabel == nlabel)
1790 163 : exp->alias.label = rlabel;
1791 : }
1792 :
1793 4254 : skipWS(r, pos);
1794 :
1795 :
1796 : //void *ptr = readAtomString(tpe->type->localtype, r, pos);
1797 4254 : if (strncmp(r+*pos, "COMMENT", strlen("COMMENT")) == 0) {
1798 115 : (*pos)+= (int) strlen("COMMENT");
1799 115 : skipWS(r, pos);
1800 115 : str comment = readAtomString(TYPE_str, r, pos);
1801 115 : exp->comment = sa_strdup(sql->sa, comment);
1802 115 : GDKfree(comment);
1803 : }
1804 :
1805 : return exp;
1806 : }
1807 :
1808 : static int
1809 2 : rel_set_types(mvc *sql, sql_rel *rel)
1810 : {
1811 2 : list *iexps = rel_projections( sql, rel->l, NULL, 0, 1);
1812 2 : node *n, *m;
1813 :
1814 2 : if (!iexps || list_length(iexps) > list_length(rel->exps))
1815 0 : return -1;
1816 7 : for(n=iexps->h, m=rel->exps->h; n && m; n = n->next, m = m->next) {
1817 5 : sql_exp *e = m->data;
1818 :
1819 5 : if (!e->tpe.type)
1820 0 : e->tpe = *exp_subtype( n->data );
1821 : }
1822 : return 0;
1823 : }
1824 :
1825 : static int
1826 0 : rel_set_types_n_ary(mvc *sql, sql_rel *rel)
1827 : {
1828 0 : list *rels = rel->l;
1829 0 : list *iexps = rel_projections( sql, rels->h->data, NULL, 0, 1);
1830 0 : node *n, *m;
1831 :
1832 0 : if (!iexps || list_length(iexps) > list_length(rel->exps))
1833 0 : return -1;
1834 0 : for(n=iexps->h, m=rel->exps->h; n && m; n = n->next, m = m->next) {
1835 0 : sql_exp *e = m->data;
1836 :
1837 0 : if (!e->tpe.type)
1838 0 : e->tpe = *exp_subtype( n->data );
1839 : }
1840 : return 0;
1841 : }
1842 :
1843 : static sql_rel*
1844 18 : rel_read_count(mvc *sql, sql_rel *rel, char *r, int *pos)
1845 : {
1846 18 : void *ptr = NULL;
1847 18 : size_t nbytes = 0;
1848 18 : ssize_t res = 0;
1849 18 : sql_subtype *tpe = sql_bind_localtype("oid");
1850 :
1851 18 : (*pos)+= (int) strlen("COUNT");
1852 18 : skipWS(r, pos);
1853 :
1854 18 : if ((res = ATOMfromstr(tpe->type->localtype, &ptr, &nbytes, r + *pos, true)) < 0) {
1855 0 : GDKfree(ptr);
1856 0 : return sql_error(sql, -1, SQLSTATE(42000) "Invalid atom string\n");
1857 : }
1858 :
1859 18 : set_count_prop(sql->sa, rel, *(BUN*)ptr);
1860 18 : (*pos) += (int) res; /* it should always fit */
1861 18 : GDKfree(ptr);
1862 18 : skipWS(r, pos);
1863 18 : return rel;
1864 : }
1865 :
1866 : static sql_rel*
1867 630 : read_rel_properties(mvc *sql, sql_rel *rel, char *r, int *pos)
1868 : {
1869 630 : bool found = true;
1870 630 : while (found) {
1871 648 : found = false;
1872 :
1873 648 : if (strncmp(r+*pos, "COUNT", strlen("COUNT")) == 0) {
1874 18 : if (!rel_read_count(sql, rel, r, pos))
1875 : return NULL;
1876 : found = true;
1877 630 : } else if (strncmp(r+*pos, "REMOTE", strlen("REMOTE")) == 0) { /* Remote tables under remote tables not supported, so remove REMOTE property */
1878 0 : (*pos)+= (int) strlen("REMOTE");
1879 0 : skipWS(r, pos);
1880 0 : skipUntilWS(r, pos);
1881 0 : skipWS(r, pos);
1882 0 : found = true;
1883 630 : } else if (strncmp(r+*pos, "USED", strlen("USED")) == 0) {
1884 0 : (*pos)+= (int) strlen("USED");
1885 0 : if (!find_prop(rel->p, PROP_USED))
1886 0 : rel->p = prop_create(sql->sa, PROP_USED, rel->p);
1887 0 : skipWS(r, pos);
1888 0 : found = true;
1889 630 : } else if (strncmp(r+*pos, "GROUPINGS", strlen("GROUPINGS")) == 0) {
1890 0 : (*pos)+= (int) strlen("GROUPINGS");
1891 0 : if (!find_prop(rel->p, PROP_GROUPINGS))
1892 0 : rel->p = prop_create(sql->sa, PROP_GROUPINGS, rel->p);
1893 0 : skipWS(r, pos);
1894 0 : found = true;
1895 : }
1896 : }
1897 : return rel;
1898 : }
1899 :
1900 : sql_rel*
1901 646 : rel_read(mvc *sql, char *r, int *pos, list *refs)
1902 : {
1903 646 : sql_rel *rel = NULL, *nrel, *lrel, *rrel = NULL;
1904 646 : list *exps, *gexps, *rels = NULL;
1905 646 : int distinct = 0, dependent = 0, single = 0, recursive = 0;
1906 646 : operator_type j = op_basetable;
1907 646 : bool groupjoin = false;
1908 :
1909 646 : skipWS(r,pos);
1910 646 : if (r[*pos] == 'R') {
1911 4 : *pos += (int) strlen("REF");
1912 :
1913 4 : skipWS(r, pos);
1914 4 : (void)readInt(r,pos);
1915 4 : skipWS(r, pos);
1916 4 : (*pos)++; /* ( */
1917 4 : int cnt = readInt(r,pos);
1918 4 : (*pos)++; /* ) */
1919 4 : if (!(rel = rel_read(sql, r, pos, refs)))
1920 : return NULL;
1921 4 : rel->ref.refcnt = cnt;
1922 4 : append(refs, rel);
1923 4 : skipWS(r,pos);
1924 : }
1925 646 : if (r[*pos] == '&') {
1926 8 : int nr;
1927 8 : (*pos)++;
1928 8 : skipWS(r, pos);
1929 8 : *pos += (int) strlen("REF");
1930 8 : skipWS(r, pos);
1931 8 : nr = readInt(r,pos); /* skip nr refs */
1932 8 : return list_fetch(refs, nr-1);
1933 : }
1934 :
1935 638 : if (r[*pos] == 'i' && r[*pos+1] == 'n' && r[*pos+2] == 's') {
1936 2 : sql_table *t;
1937 :
1938 2 : *pos += (int) strlen("insert");
1939 2 : skipWS(r, pos);
1940 2 : (*pos)++; /* ( */
1941 2 : if (!(lrel = rel_read(sql, r, pos, refs))) /* to be inserted relation */
1942 : return NULL;
1943 2 : skipWS(r,pos);
1944 2 : if (!(rrel = rel_read(sql, r, pos, refs))) /* the inserts relation */
1945 : return NULL;
1946 2 : skipWS(r,pos);
1947 2 : (*pos)++; /* ) */
1948 :
1949 2 : t = get_table(lrel);
1950 2 : if (!insert_allowed(sql, t, t->base.name, "INSERT", "insert"))
1951 : return NULL;
1952 :
1953 2 : if (!(rel = rel_insert(sql, lrel, rrel)) || !(rel = read_rel_properties(sql, rel, r, pos)))
1954 0 : return NULL;
1955 : return rel;
1956 : }
1957 :
1958 636 : if (r[*pos] == 'd' && r[*pos+1] == 'e' && r[*pos+2] == 'l') {
1959 2 : sql_table *t;
1960 :
1961 2 : *pos += (int) strlen("delete");
1962 2 : skipWS(r, pos);
1963 2 : (*pos)++; /* ( */
1964 2 : if (!(lrel = rel_read(sql, r, pos, refs))) /* to be deleted relation */
1965 : return NULL;
1966 2 : skipWS(r,pos);
1967 2 : if (!(rrel = rel_read(sql, r, pos, refs))) /* the deletes relation */
1968 : return NULL;
1969 2 : skipWS(r,pos);
1970 2 : (*pos)++; /* ) */
1971 :
1972 2 : t = get_table(lrel);
1973 2 : if (!update_allowed(sql, t, t->base.name, "DELETE", "delete", 1))
1974 : return NULL;
1975 :
1976 2 : if (!(rel = rel_delete(sql->sa, lrel, rrel)) || !(rel = read_rel_properties(sql, rel, r, pos)))
1977 0 : return NULL;
1978 :
1979 : return rel;
1980 : }
1981 :
1982 634 : if (r[*pos] == 't' && r[*pos+1] == 'r' && r[*pos+2] == 'u') {
1983 0 : sql_table *t;
1984 0 : int restart_sequences = 0, drop_action = 0;
1985 :
1986 0 : *pos += (int) strlen("truncate ");
1987 0 : if (r[*pos] == 'r') {
1988 0 : restart_sequences = 1;
1989 0 : *pos += (int) strlen("restart identity, ");
1990 : } else {
1991 0 : *pos += (int) strlen("continue identity, ");
1992 : }
1993 0 : if (r[*pos] == 'c') {
1994 0 : drop_action = 1;
1995 0 : *pos += (int) strlen("cascade");
1996 : } else {
1997 0 : *pos += (int) strlen("restrict");
1998 : }
1999 0 : skipWS(r, pos);
2000 0 : (*pos)++; /* ( */
2001 0 : if (!(lrel = rel_read(sql, r, pos, refs))) /* to be truncated relation */
2002 : return NULL;
2003 0 : skipWS(r,pos);
2004 0 : (*pos)++; /* ) */
2005 :
2006 0 : t = get_table(lrel);
2007 0 : if (!update_allowed(sql, t, t->base.name, "TRUNCATE", "truncate", 2))
2008 : return NULL;
2009 :
2010 0 : if (!(rel = rel_truncate(sql->sa, lrel, restart_sequences, drop_action)) || !(rel = read_rel_properties(sql, rel, r, pos)))
2011 0 : return NULL;
2012 : }
2013 :
2014 634 : if (r[*pos] == 'u' && r[*pos+1] == 'p' && r[*pos+2] == 'd') {
2015 2 : sql_table *t;
2016 2 : list *nexps = new_exp_list(sql->sa);
2017 :
2018 2 : *pos += (int) strlen("update");
2019 2 : skipWS(r, pos);
2020 2 : (*pos)++; /* ( */
2021 2 : if (!(lrel = rel_read(sql, r, pos, refs))) /* to be updated relation */
2022 : return NULL;
2023 2 : skipWS(r,pos);
2024 2 : if (!(rrel = rel_read(sql, r, pos, refs))) /* the updates relation */
2025 : return NULL;
2026 2 : skipWS(r,pos);
2027 2 : (*pos)++; /* ) */
2028 :
2029 2 : t = get_table(lrel);
2030 2 : if (!update_allowed(sql, t, t->base.name, "UPDATE", "update", 0) )
2031 : return NULL;
2032 :
2033 2 : skipWS(r, pos);
2034 2 : if (!(exps = read_exps(sql, lrel, rrel, NULL, r, pos, '[', 0, 1))) /* columns to be updated */
2035 : return NULL;
2036 :
2037 6 : for (node *n = exps->h ; n ; n = n->next) {
2038 4 : sql_exp *e = (sql_exp *) n->data;
2039 4 : const char *cname = exp_name(e);
2040 :
2041 4 : if (cname[0] != '%') { /* Skip TID column */
2042 2 : sql_column *c = mvc_bind_column(sql, t, cname);
2043 :
2044 2 : if (!c)
2045 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42S22) "UPDATE: no such column '%s.%s'\n", t->base.name, cname);
2046 2 : if (!(e = update_check_column(sql, t, c, e, rrel, c->base.name, "UPDATE")))
2047 : return NULL;
2048 : }
2049 4 : list_append(nexps, e);
2050 : }
2051 :
2052 2 : if (!(rel = rel_update(sql, lrel, rrel, NULL, nexps)) || !(rel = read_rel_properties(sql, rel, r, pos)))
2053 0 : return NULL;
2054 :
2055 : return rel;
2056 : }
2057 :
2058 632 : if (r[*pos] == 'm' && r[*pos+1] == 'e' && r[*pos+2] == 'r')
2059 0 : return sql_error(sql, -1, SQLSTATE(42000) "Merge statements not supported in remote plans\n");
2060 :
2061 632 : if (r[*pos] == 'd' && r[*pos+1] == 'i') {
2062 0 : *pos += (int) strlen("distinct");
2063 0 : skipWS(r, pos);
2064 0 : distinct = 1;
2065 : }
2066 632 : if (r[*pos] == 's' && r[*pos+1] == 'i') {
2067 0 : *pos += (int) strlen("single");
2068 0 : skipWS(r, pos);
2069 0 : single = 1;
2070 : }
2071 632 : if (r[*pos] == 'd' && r[*pos+1] == 'e') {
2072 0 : *pos += (int) strlen("dependent");
2073 0 : skipWS(r, pos);
2074 0 : dependent = 1;
2075 : }
2076 632 : if (r[*pos] == 'r' && r[*pos+1] == 'e' && r[*pos+2] == 'c') {
2077 0 : *pos += (int) strlen("recursive");
2078 0 : skipWS(r, pos);
2079 0 : recursive = 1;
2080 : }
2081 :
2082 632 : switch(r[*pos]) {
2083 211 : case 't':
2084 211 : if (r[*pos+1] == 'a') {
2085 208 : sql_schema *s = NULL;
2086 208 : sql_table *t = NULL;
2087 208 : char *sname, *tname;
2088 208 : *pos += (int) strlen("table");
2089 208 : skipWS(r, pos);
2090 208 : if (r[*pos] != '(')
2091 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table: missing '('\n");
2092 208 : (*pos)++;
2093 208 : skipWS(r, pos);
2094 208 : sname = r+*pos + 1;
2095 208 : skipIdent(r, pos);
2096 208 : convertIdent(sname);
2097 208 : (*pos)++;
2098 208 : if (r[*pos] != '.')
2099 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table: missing '.' in table name\n");
2100 208 : (*pos)++;
2101 208 : tname = r+*pos + 1;
2102 208 : skipIdent(r, pos);
2103 208 : convertIdent(tname);
2104 208 : (*pos)++;
2105 208 : skipWS(r, pos);
2106 208 : if (r[*pos] == '(') { /* table returning function */
2107 : node *m;
2108 : sql_exp *tudf, *next;
2109 : list *inputs, *outputs;
2110 : sql_subfunc *sf;
2111 : int x = *pos, y; /* save current position, after parsing the input relation we have to parse the input parameters */
2112 : bool inside_identifier = false;
2113 :
2114 422 : while (r[*pos] && (inside_identifier || r[*pos] != '\n')) { /* the input parameters must be parsed after the input relation, skip them for now */
2115 208 : if (inside_identifier && r[*pos] == '\\' && (r[*pos + 1] == '"' || r[*pos + 1] == '\\')) {
2116 1 : (*pos)+=2;
2117 416 : } else if (r[*pos] == '"') {
2118 102 : inside_identifier = !inside_identifier;
2119 102 : (*pos)++;
2120 : } else {
2121 314 : (*pos)++;
2122 : }
2123 : }
2124 5 : if (r[*pos] != '\n')
2125 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: missing ']' for output parameters\n");
2126 :
2127 5 : skipWS(r, pos); /* now parse the input relation */
2128 5 : if (!(lrel = rel_read(sql, r, pos, refs)))
2129 : return NULL;
2130 5 : y = *pos; /* later we have to return here to parse the output identifiers */
2131 5 : *pos = x;
2132 5 : if (!(inputs = read_exps(sql, lrel, NULL, NULL, r, pos, '(', 0, 1)))
2133 : return NULL;
2134 :
2135 5 : if (!mvc_bind_schema(sql, sname))
2136 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(3F000) "No such schema '%s'\n", sname);
2137 5 : if (!(tudf = find_table_function(sql, sname, tname, list_empty(inputs) ? NULL : inputs, list_empty(inputs) ? NULL : exp_types(sql->sa, inputs), F_UNION)))
2138 : return NULL;
2139 5 : sf = tudf->f;
2140 5 : if (tudf->type != e_func || sf->func->type != F_UNION)
2141 0 : return sql_error(sql, 02, SQLSTATE(42000) "'%s' does not return a table\n", exp_func_name(tudf));
2142 :
2143 5 : *pos = y; /* now at the end of the input relation */
2144 5 : skipWS(r, pos);
2145 5 : if (r[*pos] != ')')
2146 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: missing ')' at the end of the input relation\n");
2147 5 : (*pos)++;
2148 5 : skipWS(r, pos);
2149 :
2150 : /* Parse identifiers manually, we cannot use read_exps because the labels may not match */
2151 5 : if (r[*pos] != '[')
2152 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: missing '[' for output parameters\n");
2153 5 : (*pos)++;
2154 5 : skipWS(r, pos);
2155 5 : m = sf->func->res->h;
2156 5 : outputs = new_exp_list(sql->sa);
2157 27 : while (r[*pos] && r[*pos] != ']' && m) {
2158 22 : sql_arg *a = m->data;
2159 22 : unsigned int rlabel, nlabel;
2160 22 : char *nrname, *ncname;
2161 :
2162 22 : if (r[*pos] != '"')
2163 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: missing identifier for output parameters\n");
2164 22 : nrname = r+*pos + 1;
2165 22 : skipIdent(r, pos);
2166 22 : if (r[*pos] != '"')
2167 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: missing identifier for output parameters\n");
2168 22 : convertIdent(nrname);
2169 22 : (*pos)++;
2170 22 : if (r[*pos] != '.')
2171 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: missing '.' for output parameters\n");
2172 22 : (*pos)++; /* skip '.' */
2173 22 : if (r[*pos] != '"')
2174 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: missing identifier for output parameters\n");
2175 22 : ncname = r+*pos + 1;
2176 22 : skipIdent(r, pos);
2177 22 : if (r[*pos] != '"')
2178 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: missing identifier for output parameters\n");
2179 22 : convertIdent(ncname);
2180 22 : (*pos)++;
2181 22 : if (r[*pos] == ',')
2182 17 : (*pos)++;
2183 :
2184 22 : next = exp_column(sql->sa, nrname, ncname, &a->type, CARD_MULTI, 1, 0, 0);
2185 22 : next->alias.label = -(sql->nid++);
2186 22 : rlabel = try_update_label_count(sql, nrname);
2187 22 : nlabel = try_update_label_count(sql, ncname);
2188 22 : if (rlabel && rlabel == nlabel)
2189 0 : next->alias.label = rlabel;
2190 22 : set_basecol(next);
2191 22 : append(outputs, next);
2192 22 : m = m->next;
2193 22 : skipWS(r, pos);
2194 : }
2195 5 : if (r[*pos] != ']')
2196 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: missing ']' for output parameters\n");
2197 5 : (*pos)++;
2198 5 : skipWS(r, pos);
2199 5 : if (list_length(outputs) != list_length(sf->func->res))
2200 0 : return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: the number of output parameters don't match the table ones relation outputs: %d != function outputs: %d\n",
2201 0 : list_length(outputs), list_length(sf->func->res));
2202 5 : rel = rel_table_func(sql->sa, lrel, tudf, outputs, TABLE_FROM_RELATION);
2203 5 : set_processed(rel);
2204 : } else {
2205 203 : if (r[*pos] != ')')
2206 0 : sql_error(sql, -1, SQLSTATE(42000) "Table: missing ')'\n");
2207 203 : (*pos)++;
2208 203 : skipWS(r, pos);
2209 203 : if (!(s = mvc_bind_schema(sql, sname)))
2210 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(3F000) "No such schema '%s'\n", sname);
2211 203 : if (stack_has_frame(sql, "ALTER TABLE ADD CONSTRAINT CHECK") && !(t = frame_find_table(sql, tname)))
2212 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42S02) "Table missing '%s.%s'\n", sname, tname);
2213 203 : if (!t && !(t = mvc_bind_table(sql, s, tname)))
2214 0 : return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42S02) "Table missing '%s.%s'\n", sname, tname);
2215 203 : if (isMergeTable(t))
2216 1 : return sql_error(sql, -1, SQLSTATE(42000) "Merge tables not supported under remote connections\n");
2217 202 : if (isRemote(t))
2218 1 : return sql_error(sql, -1, SQLSTATE(42000) "Remote tables not supported under remote connections\n");
2219 201 : if (isReplicaTable(t))
2220 0 : return sql_error(sql, -1, SQLSTATE(42000) "Replica tables not supported under remote connections\n");
2221 201 : bool allowed = true;
2222 201 : if (!table_privs(sql, t, PRIV_SELECT))
2223 0 : allowed = false;
2224 201 : if (isView(t)) {
2225 1 : rel = rel_parse(sql, t->s, t->query, m_instantiate);
2226 1 : rel = rel_unnest(sql, rel);
2227 :
2228 1 : if (!rel)
2229 : return NULL;
2230 : /* Rename columns of the rel_parse relation */
2231 1 : set_processed(rel);
2232 1 : if (is_mset(rel->op) || is_simple_project(rel->op) || (is_groupby(rel->op) && !list_empty(rel->r))) {
2233 : /* it's unsafe to set the projection names because of possible dependent sorting/grouping columns */
2234 1 : rel = rel_project(sql->sa, rel, rel_projections(sql, rel, NULL, 0, 0));
2235 1 : set_processed(rel);
2236 : }
2237 10 : for (node *n = ol_first_node(t->columns), *m = rel->exps->h; n && m; n = n->next, m = m->next) {
2238 9 : sql_column *c = n->data;
2239 9 : sql_exp *e = m->data;
2240 :
2241 9 : m->data = e = exp_check_type(sql, &c->type, NULL, e, type_equal);
2242 9 : exp_setname(sql, e, tname, c->base.name);
2243 9 : set_basecol(e);
2244 : }
2245 1 : list_hash_clear(rel->exps);
2246 1 : if (rel && !allowed && t->query && (rel = rel_reduce_on_column_privileges(sql, rel, t)) == NULL)
2247 0 : return sql_error(sql, 02, SQLSTATE(42000) "SELECT: access denied for %s to view '%s.%s'", get_string_global_var(sql, "current_user"), t->s->base.name, tname);
2248 1 : rel = rel_project(sql->sa, rel, NULL);
2249 : } else {
2250 200 : rel = rel_basetable(sql, t, tname);
2251 200 : if (!allowed) {
2252 0 : rel_base_disallow(rel);
2253 0 : if (rel_base_has_column_privileges(sql, rel) == 0)
2254 0 : return sql_error(sql, -1, SQLSTATE(42000) "Access denied for %s to table '%s.%s'\n",
2255 : get_string_global_var(sql, "current_user"), s->base.name, tname);
2256 : }
2257 200 : rel_base_use_all(sql, rel);
2258 200 : rel = rewrite_basetable(sql, rel);
2259 : }
2260 :
2261 201 : if (!r[*pos])
2262 : return rel;
2263 :
2264 : /* scan aliases */
2265 201 : if (!(exps = read_exps(sql, rel, NULL, NULL, r, pos, '[', 0, 1)))
2266 : return NULL;
2267 199 : rel->exps = exps;
2268 : }
2269 : } else { /* top N */
2270 3 : *pos += (int) strlen("top N");
2271 3 : skipWS(r, pos);
2272 3 : if (r[*pos] != '(')
2273 0 : return sql_error(sql, -1, SQLSTATE(42000) "Top N: missing '('\n");
2274 3 : (*pos)++;
2275 3 : skipWS(r, pos);
2276 3 : if (!(nrel = rel_read(sql, r, pos, refs)))
2277 : return NULL;
2278 3 : if (r[*pos] != ')')
2279 0 : return sql_error(sql, -1, SQLSTATE(42000) "Top N: missing ')'\n");
2280 3 : (*pos)++;
2281 3 : skipWS(r, pos);
2282 3 : if (!(exps = read_exps(sql, nrel, NULL, NULL, r, pos, '[', 0, 1)))
2283 : return NULL;
2284 3 : rel = rel_topn(sql->sa, nrel, exps);
2285 3 : set_processed(rel);
2286 : }
2287 : break;
2288 286 : case 'p':
2289 286 : *pos += (int) strlen("project");
2290 286 : skipWS(r, pos);
2291 :
2292 286 : if (r[*pos] != '(')
2293 0 : return sql_error(sql, -1, SQLSTATE(42000) "Project: missing '('\n");
2294 286 : (*pos)++;
2295 286 : skipWS(r, pos);
2296 286 : if (!(nrel = rel_read(sql, r, pos, refs)))
2297 : return NULL;
2298 282 : skipWS(r, pos);
2299 282 : if (r[*pos] != ')')
2300 0 : return sql_error(sql, -1, SQLSTATE(42000) "Project: missing ')'\n");
2301 282 : (*pos)++;
2302 282 : skipWS(r, pos);
2303 :
2304 282 : bool is_modify = (is_insert(nrel->op) || is_update(nrel->op) || is_delete(nrel->op));
2305 282 : if (!(exps = read_exps(sql, is_modify?nrel->l : nrel, NULL, NULL, r, pos, '[', 0, 1)))
2306 : return NULL;
2307 281 : rel = rel_project(sql->sa, nrel, exps);
2308 281 : set_processed(rel);
2309 : /* order by ? */
2310 : /* first projected expressions, then left relation projections */
2311 281 : if (r[*pos] == '[' && !(rel->r = read_exps(sql, rel, nrel, NULL, r, pos, '[', 0, 1)))
2312 : return NULL;
2313 : break;
2314 65 : case 's':
2315 : case 'a':
2316 65 : if (r[*pos+1] == 'a') {
2317 0 : *pos += (int) strlen("sample");
2318 0 : skipWS(r, pos);
2319 0 : if (r[*pos] != '(')
2320 0 : return sql_error(sql, -1, SQLSTATE(42000) "Sample: missing '('\n");
2321 0 : (*pos)++;
2322 0 : skipWS(r, pos);
2323 0 : if (!(nrel = rel_read(sql, r, pos, refs)))
2324 : return NULL;
2325 0 : if (r[*pos] != ')')
2326 0 : return sql_error(sql, -1, SQLSTATE(42000) "Sample: missing ')'\n");
2327 0 : (*pos)++;
2328 0 : skipWS(r, pos);
2329 0 : if (!(exps = read_exps(sql, nrel, NULL, NULL, r, pos, '[', 0, 1)))
2330 : return NULL;
2331 0 : rel = rel_sample(sql->sa, nrel, exps);
2332 0 : set_processed(rel);
2333 65 : } else if (r[*pos+2] == 'l') {
2334 65 : *pos += (int) strlen("select");
2335 65 : skipWS(r, pos);
2336 65 : if (r[*pos] != '(')
2337 0 : return sql_error(sql, -1, SQLSTATE(42000) "Select: missing '('\n");
2338 65 : (*pos)++;
2339 65 : skipWS(r, pos);
2340 65 : if (!(nrel = rel_read(sql, r, pos, refs)))
2341 : return NULL;
2342 65 : skipWS(r, pos);
2343 65 : if (r[*pos] != ')')
2344 0 : return sql_error(sql, -1, SQLSTATE(42000) "Select: missing ')'\n");
2345 65 : (*pos)++;
2346 65 : skipWS(r, pos);
2347 :
2348 65 : if (!(exps = read_exps(sql, nrel, NULL, NULL, r, pos, '[', 0, 1)))
2349 : return NULL;
2350 65 : rel = rel_select_copy(sql->sa, nrel, exps);
2351 65 : set_processed(rel);
2352 : /* semijoin or antijoin */
2353 0 : } else if (r[*pos+1] == 'e' || r[*pos+1] == 'n') {
2354 0 : if (r[*pos+1] == 'n') {
2355 0 : j = op_anti;
2356 0 : *pos += (int) strlen("antijoin");
2357 : } else {
2358 0 : j = op_semi;
2359 0 : *pos += (int) strlen("semijoin");
2360 : }
2361 :
2362 0 : skipWS(r, pos);
2363 0 : if (r[*pos] != '(')
2364 0 : return sql_error(sql, -1, SQLSTATE(42000) "%s: missing '('\n", (j == op_semi)?"Semijoin":"Antijoin");
2365 0 : (*pos)++;
2366 0 : skipWS(r, pos);
2367 0 : if (!(lrel = rel_read(sql, r, pos, refs)))
2368 : return NULL;
2369 0 : skipWS(r, pos);
2370 :
2371 0 : if (r[*pos] != ',')
2372 0 : return sql_error(sql, -1, SQLSTATE(42000) "%s: missing ','\n", (j == op_semi)?"Semijoin":"Antijoin");
2373 0 : (*pos)++;
2374 0 : skipWS(r, pos);
2375 0 : if (!(rrel = rel_read(sql, r, pos, refs)))
2376 : return NULL;
2377 :
2378 0 : skipWS(r, pos);
2379 0 : if (r[*pos] != ')')
2380 0 : return sql_error(sql, -1, SQLSTATE(42000) "%s: missing ')'\n", (j == op_semi)?"Semijoin":"Antijoin");
2381 0 : (*pos)++;
2382 0 : skipWS(r, pos);
2383 :
2384 0 : if (!(exps = read_exps(sql, lrel, rrel, NULL, r, pos, '[', 0, 1)))
2385 : return NULL;
2386 0 : rel = rel_crossproduct(sql->sa, lrel, rrel, j);
2387 0 : rel->exps = exps;
2388 0 : set_processed(rel);
2389 : }
2390 : break;
2391 56 : case 'g':
2392 56 : *pos += (int) strlen("group");
2393 56 : skipWS(r, pos);
2394 :
2395 56 : if (r[*pos] == 'b') {
2396 56 : *pos += (int) strlen("by");
2397 56 : skipWS(r, pos);
2398 :
2399 56 : if (r[*pos] != '(')
2400 0 : return sql_error(sql, -1, SQLSTATE(42000) "Group by: missing '('\n");
2401 56 : (*pos)++;
2402 56 : skipWS(r, pos);
2403 56 : if (!(nrel = rel_read(sql, r, pos, refs)))
2404 : return NULL;
2405 56 : skipWS(r, pos);
2406 55 : if (r[*pos] != ')')
2407 0 : return sql_error(sql, -1, SQLSTATE(42000) "Group by: missing ')'\n");
2408 55 : (*pos)++;
2409 55 : skipWS(r, pos);
2410 :
2411 56 : if (!(gexps = read_exps(sql, nrel, NULL, NULL, r, pos, '[', 0, 1)))
2412 : return NULL;
2413 56 : skipWS(r, pos);
2414 56 : rel = rel_groupby(sql, nrel, gexps);
2415 55 : rel->exps = new_exp_list(sql->sa); /* empty projection list for now */
2416 55 : set_processed(rel); /* don't search beyond the group by */
2417 : /* first group projected expressions, then group by columns, then left relation projections */
2418 55 : if (is_insert(nrel->op) || is_update(nrel->op) || is_delete(nrel->op))
2419 3 : nrel = nrel->l;
2420 55 : if (!(exps = read_exps(sql, rel, nrel, NULL, r, pos, '[', 1, 1)))
2421 : return NULL;
2422 56 : rel->exps = exps;
2423 56 : rel->nrcols = list_length(exps);
2424 56 : break;
2425 : } else {
2426 : groupjoin = true;
2427 : }
2428 : /* fall through */
2429 1 : case 'l':
2430 1 : if (strncmp(r+*pos, "left outer join", strlen("left outer join")) == 0) {
2431 0 : *pos += (int) strlen("left outer join");
2432 : } else {
2433 1 : groupjoin = true;
2434 1 : *pos += (int) strlen("left outer group join");
2435 : }
2436 : j = op_left;
2437 : /* fall through */
2438 : case 'r':
2439 0 : if (j == op_basetable) {
2440 0 : *pos += (int) strlen("right outer join");
2441 0 : j = op_right;
2442 : }
2443 : /* fall through */
2444 : case 'f':
2445 0 : if (j == op_basetable) {
2446 0 : *pos += (int) strlen("full outer join");
2447 0 : j = op_full;
2448 : }
2449 : /* fall through */
2450 : case 'c':
2451 0 : if (j == op_basetable) {
2452 7 : *pos += (int) strlen("crossproduct");
2453 7 : j = op_join;
2454 : }
2455 : /* fall through */
2456 : case 'j':
2457 7 : if (j == op_basetable) {
2458 1 : *pos += (int) strlen("join");
2459 1 : j = op_join;
2460 : }
2461 9 : skipWS(r, pos);
2462 :
2463 9 : if (r[*pos] != '(')
2464 0 : return sql_error(sql, -1, SQLSTATE(42000) "Join: missing '('\n");
2465 9 : (*pos)++;
2466 9 : skipWS(r, pos);
2467 9 : if (!(lrel = rel_read(sql, r, pos, refs)))
2468 : return NULL;
2469 9 : skipWS(r, pos);
2470 :
2471 9 : if (r[*pos] != ',')
2472 0 : return sql_error(sql, -1, SQLSTATE(42000) "Join: missing ','\n");
2473 9 : (*pos)++;
2474 9 : skipWS(r, pos);
2475 9 : if (!(rrel = rel_read(sql, r, pos, refs)))
2476 : return NULL;
2477 :
2478 9 : skipWS(r, pos);
2479 9 : if (r[*pos] != ')')
2480 0 : return sql_error(sql, -1, SQLSTATE(42000) "Join: missing ')'\n");
2481 9 : (*pos)++;
2482 9 : skipWS(r, pos);
2483 :
2484 9 : if (!(exps = read_exps(sql, lrel, rrel, NULL, r, pos, '[', 0, 1)))
2485 : return NULL;
2486 9 : rel = rel_crossproduct(sql->sa, lrel, rrel, j);
2487 9 : rel->exps = exps;
2488 9 : if (groupjoin) {
2489 1 : list *attr = NULL;
2490 1 : if (!(attr = read_exps(sql, lrel, rrel, NULL, r, pos, '[', 0, 1)))
2491 : return NULL;
2492 1 : rel->attr = attr;
2493 : }
2494 9 : set_processed(rel);
2495 9 : break;
2496 0 : case 'm':
2497 0 : if (strcmp(r+*pos, "munion") == 0 && j == op_basetable) {
2498 0 : *pos += (int) strlen("munion");
2499 0 : j = op_munion;
2500 : }
2501 : /* fall through */
2502 : case 'u':
2503 0 : if (j == op_basetable) {
2504 0 : *pos += (int) strlen("union");
2505 0 : j = op_union;
2506 : }
2507 : /* fall through */
2508 : case 'i':
2509 0 : if (j == op_basetable) {
2510 1 : *pos += (int) strlen("intersect");
2511 1 : j = op_inter;
2512 : }
2513 : /* fall through */
2514 : case 'e':
2515 1 : if (j == op_basetable) {
2516 1 : *pos += (int) strlen("except");
2517 1 : j = op_except;
2518 : }
2519 2 : skipWS(r, pos);
2520 :
2521 2 : if (r[*pos] != '(')
2522 0 : return sql_error(sql, -1, SQLSTATE(42000) "Setop: missing '('\n");
2523 2 : (*pos)++;
2524 2 : skipWS(r, pos);
2525 2 : if (!(lrel = rel_read(sql, r, pos, refs)))
2526 : return NULL;
2527 2 : skipWS(r, pos);
2528 :
2529 2 : if (j == op_munion) {
2530 0 : rels = sa_list(sql->sa);
2531 0 : append(rels, lrel);
2532 :
2533 0 : while (r[*pos] == ',') {
2534 0 : (*pos)++;
2535 0 : skipWS(r, pos);
2536 0 : if (!(rrel = rel_read(sql, r, pos, refs)))
2537 : return NULL;
2538 0 : skipWS(r, pos);
2539 0 : append(rels, rrel);
2540 : }
2541 : } else {
2542 2 : if (r[*pos] != ',')
2543 0 : return sql_error(sql, -1, SQLSTATE(42000) "Setop: missing ','\n");
2544 2 : (*pos)++;
2545 2 : skipWS(r, pos);
2546 2 : if (!(rrel = rel_read(sql, r, pos, refs)))
2547 : return NULL;
2548 2 : skipWS(r, pos);
2549 : }
2550 :
2551 2 : if (r[*pos] != ')')
2552 0 : return sql_error(sql, -1, SQLSTATE(42000) "Setop: missing ')'\n");
2553 2 : (*pos)++;
2554 2 : skipWS(r, pos);
2555 :
2556 2 : if (!(exps = read_exps(sql, lrel, rrel, NULL, r, pos, '[', 0, 1)))
2557 : return NULL;
2558 2 : if (j == op_munion) {
2559 0 : rel = rel_setop_n_ary(sql->sa, rels, j);
2560 0 : rel_setop_n_ary_set_exps(sql, rel, exps, false);
2561 0 : if (rel_set_types_n_ary(sql, rel) < 0)
2562 0 : return sql_error(sql, -1, SQLSTATE(42000) "Setop: number of expressions don't match\n");
2563 : } else {
2564 2 : rel = rel_setop(sql->sa, lrel, rrel, j);
2565 2 : rel_setop_set_exps(sql, rel, exps, false);
2566 2 : if (rel_set_types(sql, rel) < 0)
2567 0 : return sql_error(sql, -1, SQLSTATE(42000) "Setop: number of expressions don't match\n");
2568 : }
2569 2 : set_processed(rel);
2570 2 : break;
2571 3 : case '[': /* projection of list of values */
2572 3 : if (!(exps = read_exps(sql, NULL, NULL, NULL, r, pos, '[', 0, 1)))
2573 : return NULL;
2574 3 : rel = rel_project(sql->sa, NULL, exps);
2575 : /* order by ? */
2576 3 : if (r[*pos] == '[' && !(rel->r = read_exps(sql, NULL, rel, NULL, r, pos, '[', 0, 1)))
2577 : return NULL;
2578 3 : set_processed(rel);
2579 3 : break;
2580 0 : case 'd':
2581 : /* 'ddl' not supported */
2582 : default:
2583 0 : return sql_error(sql, -1, SQLSTATE(42000) "Could not determine the input relation\n");
2584 : }
2585 :
2586 342 : if (!rel)
2587 0 : return sql_error(sql, -1, SQLSTATE(42000) "Could not determine the input relation\n");
2588 623 : if (distinct)
2589 0 : set_distinct(rel);
2590 623 : if (single)
2591 0 : set_single(rel);
2592 623 : if (dependent)
2593 0 : set_dependent(rel);
2594 623 : if (recursive)
2595 0 : set_recursive(rel);
2596 :
2597 : /* sometimes, properties are sent */
2598 623 : if (!(rel = read_rel_properties(sql, rel, r, pos)))
2599 : return NULL;
2600 : return rel;
2601 : }
2602 :
2603 : static bool
2604 0 : is_infix(sql_func *f)
2605 : {
2606 0 : if (strlen(f->base.name) == 1) {
2607 : return true;
2608 0 : } else if (strlen(f->base.name) == 2) {
2609 0 : if (f->base.name[0] == '<' && f->base.name[1] == '>')
2610 : return true;
2611 0 : if (f->base.name[0] == '<' && f->base.name[1] == '=')
2612 : return true;
2613 0 : if (f->base.name[0] == '>' && f->base.name[1] == '=')
2614 : return true;
2615 0 : if (f->base.name[0] == '|' && f->base.name[1] == '|')
2616 : return true;
2617 0 : if (f->base.name[0] == 'o' && f->base.name[1] == 'r')
2618 0 : return true;
2619 0 : } else if (strlen(f->base.name) == 3) {
2620 0 : if (f->base.name[0] == 'a' && f->base.name[1] == 'n' && f->base.name[2] == 'd')
2621 0 : return true;
2622 : }
2623 : return false;
2624 : }
2625 :
2626 : static void
2627 0 : exp2sql_dquoted(stream *fout, const char *pref, const char *val, const char *suff)
2628 : {
2629 0 : if (pref)
2630 0 : mnstr_printf(fout, "%s", pref);
2631 0 : mnstr_write(fout, "\"", 1, 1);
2632 0 : while (*val) {
2633 0 : const char *p = strchr(val, '"');
2634 0 : if (p) {
2635 0 : if (p > val)
2636 0 : mnstr_write(fout, val, 1, p - val);
2637 0 : mnstr_write(fout, "\"\"", 1, 2);
2638 0 : val = p + 1;
2639 : } else {
2640 0 : mnstr_printf(fout, "%s", val);
2641 0 : break;
2642 : }
2643 : }
2644 0 : mnstr_write(fout, "\"", 1, 1);
2645 0 : if (suff)
2646 0 : mnstr_printf(fout, "%s", suff);
2647 0 : }
2648 :
2649 : /* only simple expressions, ie recursive no psm */
2650 : static void
2651 0 : exp2sql_print(mvc *sql, stream *fout, sql_exp *e, int depth)
2652 : {
2653 0 : switch (e->type) {
2654 0 : case e_func: {
2655 0 : sql_subfunc *sf = e->f;
2656 0 : list *args = e->l;
2657 0 : if (list_length(args) == 2 && is_infix(sf->func)) {
2658 0 : if (depth)
2659 0 : mnstr_printf(fout, "( " );
2660 0 : exp2sql_print(sql, fout, args->h->data, depth+1);
2661 0 : mnstr_printf(fout, " %s ", sf->func->base.name);
2662 0 : exp2sql_print(sql, fout, args->h->next->data, depth+1);
2663 0 : if (depth)
2664 0 : mnstr_printf(fout, " )" );
2665 : } else {
2666 0 : exp2sql_dquoted(fout, NULL, sf->func->base.name, "(");
2667 0 : if (args)
2668 0 : for (node *n = args->h; n; n = n->next) {
2669 0 : exp2sql_print(sql, fout, n->data, depth+1);
2670 0 : if (n->next)
2671 0 : mnstr_printf(fout, ", ");
2672 : }
2673 0 : mnstr_printf(fout, ")");
2674 : }
2675 : } break;
2676 0 : case e_column:
2677 0 : exp2sql_dquoted(fout, NULL, exp_name(e), NULL);
2678 0 : break;
2679 0 : case e_convert:
2680 0 : mnstr_printf(fout, "CAST (" );
2681 0 : exp2sql_print(sql, fout, e->l, depth+1);
2682 0 : mnstr_printf(fout, "AS %s)", sql_subtype_string(sql->sa, exp_subtype(e)));
2683 0 : break;
2684 0 : case e_atom:
2685 0 : mnstr_printf(fout, "%s", atom2sql(sql->sa, e->l, 0));
2686 0 : break;
2687 : case e_aggr: /* fall-through */
2688 : case e_cmp: /* fall-through */
2689 : case e_psm:
2690 0 : assert(0);
2691 : break;
2692 : }
2693 0 : }
2694 :
2695 : char *
2696 0 : exp2sql( mvc *sql, sql_exp *exp)
2697 : {
2698 0 : buffer *b = NULL;
2699 0 : stream *s = NULL;
2700 0 : char *res = NULL;
2701 :
2702 0 : b = buffer_create(1024);
2703 0 : if(b == NULL)
2704 0 : goto cleanup;
2705 0 : s = buffer_wastream(b, "exp_dump");
2706 0 : if(s == NULL)
2707 0 : goto cleanup;
2708 :
2709 0 : exp2sql_print(sql, s, exp, 0);
2710 :
2711 0 : res = buffer_get_buf(b);
2712 :
2713 0 : cleanup:
2714 0 : if(b)
2715 0 : buffer_destroy(b);
2716 0 : if(s)
2717 0 : close_stream(s);
2718 :
2719 0 : char* fres = SA_STRDUP(sql->sa, res);
2720 0 : free (res);
2721 0 : return fres;
2722 : }
|