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