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