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