LCOV - code coverage report
Current view: top level - sql/server - rel_dump.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1328 1823 72.8 %
Date: 2024-11-13 22:44:48 Functions: 30 36 83.3 %

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

Generated by: LCOV version 1.14