LCOV - code coverage report
Current view: top level - sql/server - rel_dump.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1356 1858 73.0 %
Date: 2025-03-25 20:06:35 Functions: 30 36 83.3 %

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

Generated by: LCOV version 1.14