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