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