LCOV - code coverage report
Current view: top level - sql/server - rel_dump.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1366 1874 72.9 %
Date: 2025-03-24 21:28:01 Functions: 30 36 83.3 %

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

Generated by: LCOV version 1.14