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