LCOV - code coverage report
Current view: top level - sql/server - rel_dump.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1119 1631 68.6 %
Date: 2024-04-26 00:35:57 Functions: 25 29 86.2 %

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

Generated by: LCOV version 1.14