LCOV - code coverage report
Current view: top level - sql/server - rel_dump.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1253 1806 69.4 %
Date: 2024-10-03 20:03:20 Functions: 31 36 86.1 %

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

Generated by: LCOV version 1.14