LCOV - code coverage report
Current view: top level - sql/backends/monet5 - sql_scenario.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 722 1064 67.9 %
Date: 2025-03-25 20:06:35 Functions: 26 28 92.9 %

          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             : /*
      14             :  * (authors) N. Nes, M.L. Kersten
      15             :  * The SQL scenario implementation is a derivative of the MAL session scenario.
      16             :  *
      17             :  */
      18             : /*
      19             :  * Before we are can process SQL statements the global catalog
      20             :  * should be initialized. Thereafter, each time a client enters
      21             :  * we update its context descriptor to denote an SQL scenario.
      22             :  */
      23             : #include "monetdb_config.h"
      24             : #include "mal_backend.h"
      25             : #include "sql_scenario.h"
      26             : #include "sql_result.h"
      27             : #include "sql_gencode.h"
      28             : #include "sql_optimizer.h"
      29             : #include "sql_assert.h"
      30             : #include "sql_execute.h"
      31             : #include "sql_env.h"
      32             : #include "sql_mvc.h"
      33             : #include "sql_user.h"
      34             : #include "sql_datetime.h"
      35             : #include "sql_import.h"
      36             : #include "mal.h"
      37             : #include "mal_instruction.h"
      38             : #include "mal_interpreter.h"
      39             : #include "mal_parser.h"
      40             : #include "mal_builder.h"
      41             : #include "mal_namespace.h"
      42             : #include "mal_linker.h"
      43             : #include "mal_scenario.h"
      44             : #include "mal_authorize.h"
      45             : #include "mcrypt.h"
      46             : #include "mutils.h"
      47             : #include "bat5.h"
      48             : #include "msabaoth.h"
      49             : #include "gdk_time.h"
      50             : #include "optimizer.h"
      51             : #include "opt_pipes.h"
      52             : #include "opt_mitosis.h"
      53             : #include <unistd.h>
      54             : #include "sql_upgrades.h"
      55             : #include "rel_semantic.h"
      56             : #include "rel_rel.h"
      57             : 
      58             : #define MAX_SQL_MODULES 128
      59             : static int sql_modules = 0;
      60             : static struct sql_module {
      61             :         const char *name;
      62             :         const unsigned char *code;
      63             : } sql_module[MAX_SQL_MODULES];
      64             : 
      65             : static int
      66       25411 : sql_module_compare(const void *a, const void *b)
      67             : {
      68       25411 :         const struct sql_module *l = a, *r = b;
      69       25411 :         return strcmp(l->name, r->name);
      70             : }
      71             : 
      72             : void
      73       13527 : sql_register(const char *name, const unsigned char *code)
      74             : {
      75       13527 :         assert (sql_modules < MAX_SQL_MODULES);
      76       13527 :         sql_module[sql_modules].name = name;
      77       13527 :         sql_module[sql_modules].code = code;
      78       13527 :         sql_modules++;
      79       13527 : }
      80             : 
      81             : static sql_store SQLstore = NULL;
      82             : int SQLdebug = 0;
      83             : static const char *sqlinit = NULL;
      84             : static MT_Lock sql_contextLock = MT_LOCK_INITIALIZER(sql_contextLock);
      85             : 
      86             : static str SQLinit(Client c, const char *initpasswd);
      87             : static str master_password = NULL;
      88             : 
      89             : #include "mal.h"
      90             : #include "mal_client.h"
      91             : 
      92             : static void
      93         118 : CLIENTprintinfo(void)
      94             : {
      95         118 :         int nrun = 0, nfinish = 0, nblock = 0;
      96         118 :         char mmbuf[64];
      97         118 :         char tmbuf[64];
      98         118 :         char trbuf[64];
      99         118 :         char chbuf[64];
     100         118 :         char cabuf[64];
     101         118 :         char clbuf[64];
     102         118 :         char crbuf[64];
     103         118 :         char cpbuf[64];
     104         118 :         struct tm tm;
     105             : 
     106         118 :         if (!MT_lock_trytime(&mal_contextLock, 1000)) {
     107           0 :                 printf("Clients are currently locked, so no client information\n");
     108           0 :                 return;
     109             :         }
     110         118 :         printf("Clients:\n");
     111        9594 :         for (Client c = mal_clients; c < mal_clients + MAL_MAXCLIENTS; c++) {
     112        9476 :                 switch (c->mode) {
     113           0 :                 case RUNCLIENT:
     114             :                         /* running */
     115           0 :                         nrun++;
     116           0 :                         if (c->qryctx.maxmem)
     117           0 :                                 snprintf(mmbuf, sizeof(mmbuf), " (max %"PRIu64")", (uint64_t) c->qryctx.maxmem);
     118             :                         else
     119           0 :                                 mmbuf[0] = 0;
     120           0 :                         if (c->idle) {
     121           0 :                                 localtime_r(&c->idle, &tm);
     122           0 :                                 strftime(tmbuf, sizeof(tmbuf), ", idle since %F %H:%M:%S%z", &tm);
     123           0 :                         } else if (c->lastcmd) {
     124           0 :                                 localtime_r(&c->lastcmd, &tm);
     125           0 :                                 strftime(tmbuf, sizeof(tmbuf), ", busy since %F %H:%M:%S%z", &tm);
     126             :                         } else
     127           0 :                                 tmbuf[0] = 0;
     128           0 :                         if (c->sqlcontext && ((backend *) c->sqlcontext)->mvc && ((backend *) c->sqlcontext)->mvc->session && ((backend *) c->sqlcontext)->mvc->session->tr && ((backend *) c->sqlcontext)->mvc->session->tr->active)
     129           0 :                                 snprintf(trbuf, sizeof(trbuf), ", active transaction, ts: "ULLFMT, ((backend *) c->sqlcontext)->mvc->session->tr->ts);
     130             :                         else
     131           0 :                                 trbuf[0] = 0;
     132           0 :                         if (c->client_hostname)
     133           0 :                                 snprintf(chbuf, sizeof(chbuf), ", client host: %s", c->client_hostname);
     134             :                         else
     135           0 :                                 chbuf[0] = 0;
     136           0 :                         if (c->client_application)
     137           0 :                                 snprintf(cabuf, sizeof(cabuf), ", client app: %s", c->client_application);
     138             :                         else
     139           0 :                                 cabuf[0] = 0;
     140           0 :                         if (c->client_library)
     141           0 :                                 snprintf(clbuf, sizeof(clbuf), ", client lib: %s", c->client_library);
     142             :                         else
     143           0 :                                 clbuf[0] = 0;
     144           0 :                         if (c->client_remark)
     145           0 :                                 snprintf(crbuf, sizeof(crbuf), ", client remark: %s", c->client_remark);
     146             :                         else
     147           0 :                                 crbuf[0] = 0;
     148           0 :                         if (c->client_pid)
     149           0 :                                 snprintf(cpbuf, sizeof(cpbuf), ", client pid: %ld", c->client_pid);
     150             :                         else
     151           0 :                                 cpbuf[0] = 0;
     152           0 :                         printf("client %d, user %s, thread %s, using %"PRIu64" bytes of transient space%s%s%s%s%s%s%s%s\n", c->idx, c->username, c->mythread ? c->mythread : "?", (uint64_t) ATOMIC_GET(&c->qryctx.datasize), mmbuf, tmbuf, trbuf, chbuf, cabuf, clbuf, cpbuf, crbuf);
     153           0 :                         break;
     154           0 :                 case FINISHCLIENT:
     155             :                         /* finishing */
     156           0 :                         nfinish++;
     157           0 :                         break;
     158           0 :                 case BLOCKCLIENT:
     159             :                         /* blocked */
     160           0 :                         nblock++;
     161           0 :                         break;
     162             :                 case FREECLIENT:
     163             :                         break;
     164             :                 }
     165             :         }
     166         118 :         MT_lock_unset(&mal_contextLock);
     167         118 :         printf("%d active clients, %d finishing clients, %d blocked clients; max: %d\n",
     168             :                    nrun, nfinish, nblock, MAL_MAXCLIENTS);
     169             : }
     170             : 
     171             : static void
     172         118 : SQLprintinfo(void)
     173             : {
     174         118 :         CLIENTprintinfo();
     175         118 :         store_printinfo(SQLstore);
     176         118 : }
     177             : 
     178             : str
     179             : //SQLprelude(void *ret)
     180         357 : SQLprelude(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
     181             : {
     182         357 :         str tmp;
     183         357 :         Scenario ms, s = getFreeScenario();
     184             :         /* HACK ALERT: temporarily use sqlcontext to pass the initial
     185             :          * password to the prelude function */
     186         357 :         const char *initpasswd = cntxt->sqlcontext;
     187         357 :         cntxt->sqlcontext = NULL;
     188             :         /* HACK ALERT: use mb (MalBlkPtr) to pass revision string in order
     189             :          * to check that in the callee */
     190         357 :         if (mb) {
     191         357 :                 const char *caller_revision = (const char *) (void *) mb;
     192         357 :                 const char *p = mercurial_revision();
     193         357 :                 if (p && strcmp(p, caller_revision) != 0) {
     194           0 :                         throw(MAL, "sql.start", "incompatible versions: caller is %s, GDK is %s\n", caller_revision, p);
     195             :                 }
     196             :         }
     197             : 
     198         357 :         (void) stk;
     199         357 :         (void) pci;
     200         357 :         if (!s)
     201           0 :                 throw(MAL, "sql.start", SQLSTATE(42000) "out of scenario slots");
     202         357 :         sqlinit = GDKgetenv("sqlinit");
     203         357 :         *s = (struct SCENARIO) {
     204             :                 .name = "S_Q_L",
     205             :                 .language = "sql",
     206             :                 .initClient = "SQLinitClient",
     207             :                 .initClientCmd = SQLinitClient,
     208             :                 .exitClient = "SQLexitClient",
     209             :                 .exitClientCmd = SQLexitClient,
     210             :                 .engine = "SQLengine",
     211             :                 .engineCmd = SQLengine,
     212             :         };
     213         357 :         ms = getFreeScenario();
     214         357 :         if (!ms)
     215           0 :                 throw(MAL, "sql.start", SQLSTATE(42000) "out of scenario slots");
     216             : 
     217         357 :         *ms = (struct SCENARIO) {
     218             :                 .name = "M_S_Q_L",
     219             :                 .language = "msql",
     220             :                 .initClient = "SQLinitClientFromMAL",
     221             :                 .initClientCmd = SQLinitClientFromMAL,
     222             :                 .exitClient = "SQLexitClient",
     223             :                 .exitClientCmd = SQLexitClient,
     224             :                 .engine = "MALengine",
     225             :                 .engineCmd = MALengine,
     226             :         };
     227             : 
     228         357 :         tmp = SQLinit(cntxt, initpasswd);
     229         357 :         if (tmp != MAL_SUCCEED) {
     230           1 :                 TRC_CRITICAL(SQL_PARSER, "Fatal error during initialization: %s\n", tmp);
     231           1 :                 if (!GDKembedded()) {
     232           1 :                         freeException(tmp);
     233           1 :                         if ((tmp = GDKerrbuf) && *tmp)
     234           0 :                                 TRC_CRITICAL(SQL_PARSER, SQLSTATE(42000) "GDK reported: %s\n", tmp);
     235           1 :                         fflush(stderr);
     236           1 :                         exit(1);
     237             :                 } else {
     238             :                         return tmp;
     239             :                 }
     240             :         }
     241         356 :         if (!GDKembedded()) {
     242         344 :                 fprintf(stdout, "# MonetDB/SQL module loaded\n");
     243         344 :                 fflush(stdout);         /* make merovingian see this *now* */
     244             :         }
     245         356 :         GDKprintinforegister(SQLprintinfo);
     246         356 :         if (GDKinmemory(0) || GDKembedded()) {
     247          12 :                 s->name = "sql";
     248          12 :                 ms->name = "msql";
     249          12 :                 return MAL_SUCCEED;
     250             :         }
     251             :         /* only register availability of scenarios AFTER we are inited! */
     252         344 :         s->name = "sql";
     253         344 :         tmp = msab_marchScenario(s->name);
     254         344 :         if (tmp != NULL) {
     255           0 :                 char *err = createException(MAL, "sql.start", "%s", tmp);
     256           0 :                 free(tmp);
     257           0 :                 return err;
     258             :         }
     259         344 :         ms->name = "msql";
     260         344 :         tmp = msab_marchScenario(ms->name);
     261         344 :         if (tmp != NULL) {
     262           0 :                 char *err = createException(MAL, "sql.start", "%s", tmp);
     263           0 :                 free(tmp);
     264           0 :                 return err;
     265             :         }
     266             :         return MAL_SUCCEED;
     267             : }
     268             : 
     269             : static str
     270         355 : SQLexit(Client c)
     271             : {
     272         355 :         (void) c;               /* not used */
     273         355 :         MT_lock_set(&sql_contextLock);
     274         355 :         if (SQLstore) {
     275         355 :                 mvc_exit(SQLstore);
     276         355 :                 SQLstore = NULL;
     277             :         }
     278         355 :         MT_lock_unset(&sql_contextLock);
     279         355 :         return MAL_SUCCEED;
     280             : }
     281             : 
     282             : str
     283         355 : SQLepilogue(void *ret)
     284             : {
     285         355 :         const char s[] = "sql", m[] = "msql";
     286         355 :         char *msg;
     287             : 
     288         355 :         (void) ret;
     289         355 :         msg = SQLexit(NULL);
     290         355 :         freeException(msg);
     291             :         /* this function is never called, but for the style of it, we clean
     292             :          * up our own mess */
     293         355 :         if (!GDKinmemory(0) && !GDKembedded()) {
     294         344 :                 str res = msab_retreatScenario(m);
     295         344 :                 if (!res)
     296         344 :                         res = msab_retreatScenario(s);
     297         344 :                 if (res != NULL) {
     298           0 :                         char *err = createException(MAL, "sql.epilogue", "%s", res);
     299           0 :                         free(res);
     300           0 :                         return err;
     301             :                 }
     302             :         }
     303             :         /* return scenarios */
     304         355 :         Scenario sc = findScenario(s);
     305         355 :         if (sc)
     306         355 :                 sc->name = NULL;
     307         355 :         sc = findScenario(m);
     308         355 :         if (sc)
     309         355 :                 sc->name = NULL;
     310             :         return MAL_SUCCEED;
     311             : }
     312             : 
     313             : 
     314             : static str
     315         179 : SQLexecPostLoginTriggers(Client c)
     316             : {
     317         179 :         str msg = MAL_SUCCEED;
     318         179 :         backend *be = (backend *) c->sqlcontext;
     319         179 :         if (be) {
     320         179 :                 mvc *m = be->mvc;
     321         179 :                 sql_trans *tr = m->session->tr;
     322         179 :                 int active = tr->active;
     323         179 :                 if (active || mvc_trans(m) == 0) {
     324         179 :                         sql_schema *sys = find_sql_schema(tr, "sys");
     325         179 :                         struct os_iter oi;
     326             :                         // triggers not related to table should have been loaded in sys
     327         179 :                         os_iterator(&oi, sys->triggers, tr, NULL);
     328         350 :                         for (sql_base *b = oi_next(&oi); b && msg == MAL_SUCCEED; b = oi_next(&oi)) {
     329         171 :                                 sql_trigger *t = (sql_trigger*) b;
     330         171 :                                 if (t->event == LOGIN_EVENT) {
     331           1 :                                         const char *stmt = t->statement;
     332           1 :                                         sql_rel *r = NULL;
     333             :                                         // cache state
     334           1 :                                         int oldvtop = c->curprg->def->vtop;
     335           1 :                                         int oldstop = c->curprg->def->stop;
     336           1 :                                         Symbol curprg = c->curprg;
     337           1 :                                         allocator *sa = m->sa;
     338             : 
     339           1 :                                         if (!(m->sa = sa_create(m->pa))) {
     340           0 :                                                 m->sa = sa;
     341           0 :                                                 throw(SQL, "sql.SQLexecPostLoginTriggers", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     342             :                                         }
     343           1 :                                         r = rel_parse(m, sys, stmt, m_deps);
     344           1 :                                         if (r)
     345           1 :                                                 r = sql_processrelation(m, r, 0, 0, 0, 0);
     346           1 :                                         if (!r) {
     347           0 :                                                 sa_destroy(m->sa);
     348           0 :                                                 m->sa = sa;
     349           0 :                                                 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
     350           0 :                                                         throw(SQL, "sql.SQLexecPostLoginTriggers", "%s", m->errstr);
     351             :                                                 else
     352           0 :                                                         throw(SQL, "sql.SQLexecPostLoginTriggers", SQLSTATE(42000) "%s", m->errstr);
     353             :                                         }
     354             : 
     355           1 :                                         setVarType(c->curprg->def, 0, 0);
     356           1 :                                         if (backend_dumpstmt(be, c->curprg->def, r, 1, 1, NULL) < 0) {
     357           0 :                                                 freeVariables(c, c->curprg->def, NULL, oldvtop);
     358           0 :                                                 c->curprg = curprg;
     359           0 :                                                 sa_destroy(m->sa);
     360           0 :                                                 m->sa = sa;
     361           0 :                                                 throw(SQL, "sql.SQLexecPostLoginTriggers", SQLSTATE(4200) "%s", "generating MAL failed");
     362             :                                         }
     363             : 
     364           1 :                                         msg = SQLoptimizeQuery(c, c->curprg->def);
     365             : 
     366           1 :                                         stream *out = be->out;
     367           1 :                                         be->out = NULL;      /* no output stream */
     368           1 :                                         if (!msg)
     369           1 :                                                 msg = SQLrun(c, m);
     370             : 
     371             :                                         // restore previous state
     372           1 :                                         be->out = out;
     373           1 :                                         MSresetInstructions(c->curprg->def, oldstop);
     374           1 :                                         freeVariables(c, c->curprg->def, NULL, oldvtop);
     375           1 :                                         sqlcleanup(be, 0);
     376           1 :                                         c->curprg = curprg;
     377           1 :                                         sa_destroy(m->sa);
     378           1 :                                         m->sa = sa;
     379             :                                 }
     380             :                         }
     381             : 
     382         179 :                         if (!active)
     383         179 :                                 sql_trans_end(m->session, SQL_OK);
     384             :                 }
     385             :         }
     386             :         return msg;
     387             : }
     388             : 
     389             : static str
     390       37249 : userCheckCredentials( mvc *m, Client c, const char *pwhash, const char *challenge, const char *algo)
     391             : {
     392       37249 :         oid uid = getUserOIDByName(m, c->username);
     393             : 
     394       37249 :         if (strNil(pwhash))
     395           0 :                 throw(INVCRED, "checkCredentials", INVCRED_INVALID_USER " '%s'", c->username);
     396       37249 :         str passValue = getUserPassword(m, uid);
     397       37249 :         if (strNil(passValue))
     398           6 :                 throw(INVCRED, "checkCredentials", INVCRED_INVALID_USER " '%s'", c->username);
     399             :             /* find the corresponding password to the user */
     400             : 
     401       37243 :         str pwd = NULL;
     402       37243 :         str msg = AUTHdecypherValue(&pwd, passValue);
     403       37243 :         GDKfree(passValue);
     404       37243 :         if (msg)
     405             :                 return msg;
     406             : 
     407             :         /* generate the hash as the client should have done */
     408       37243 :         str hash = mcrypt_hashPassword(algo, pwd, challenge);
     409       37243 :         GDKfree(pwd);
     410       37243 :         if(!hash)
     411           0 :                 throw(MAL, "checkCredentials", "hash '%s' backend not found", algo);
     412             : 
     413             :         /* and now we have it, compare it to what was given to us */
     414       37243 :         if (strcmp(pwhash, hash) == 0) {
     415       37240 :                 free(hash);
     416       37240 :                 c->user = uid;
     417       37240 :                 return MAL_SUCCEED;
     418             :         }
     419           3 :         free(hash);
     420             : 
     421             :         /* special case: users whose name starts with '.' can authenticate using
     422             :          * the temporary master password.
     423             :          */
     424           3 :         if (c->username[0] == '.' && master_password != NULL && master_password[0] != '\0') {
     425             :                 // first encrypt the master password as if we've just found it
     426             :                 // in the password store
     427           0 :                 str encrypted = mcrypt_BackendSum(master_password, strlen(master_password));
     428           0 :                 if (encrypted == NULL)
     429           0 :                         throw(MAL, "checkCredentials", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     430           0 :                 hash = mcrypt_hashPassword(algo, encrypted, challenge);
     431           0 :                 free(encrypted);
     432           0 :                 if (hash && strcmp(pwhash, hash) == 0) {
     433           0 :                         free(hash);
     434           0 :                         c->user = uid;
     435           0 :                         return(MAL_SUCCEED);
     436             :                 }
     437           0 :                 free(hash);
     438             :         }
     439             : 
     440             :         /* of course we DO NOT print the password here */
     441           3 :         throw(INVCRED, "checkCredentials", INVCRED_INVALID_USER " '%s'", c->username);
     442             : }
     443             : 
     444             : static char*
     445       37637 : SQLprepareClient(Client c, const char *pwhash, const char *challenge, const char *algo)
     446             : {
     447       37637 :         mvc *m = NULL;
     448       37637 :         backend *be = NULL;
     449       37637 :         str msg = MAL_SUCCEED;
     450             : 
     451       37637 :         if (c->sqlcontext == 0) {
     452       37637 :                 allocator *sa = sa_create(NULL);
     453       37637 :                 if (sa == NULL) {
     454           0 :                         msg = createException(SQL,"sql.initClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     455           0 :                         goto bailout2;
     456             :                 }
     457       37637 :                 m = mvc_create(SQLstore, sa, c->idx, SQLdebug, c->fdin, c->fdout);
     458       37637 :                 if (m == NULL) {
     459           0 :                         msg = createException(SQL,"sql.initClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     460           0 :                         goto bailout2;
     461             :                 }
     462       37637 :                 if (c->scenario && strcmp(c->scenario, "msql") == 0)
     463         188 :                         m->reply_size = -1;
     464       37637 :                 be = (void *) backend_create(m, c);
     465       37637 :                 if ( be == NULL) {
     466           0 :                         mvc_destroy(m);
     467           0 :                         msg = createException(SQL,"sql.initClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     468           0 :                         goto bailout2;
     469             :                 }
     470             :         } else {
     471           0 :                 assert(0);
     472             :         }
     473       37637 :         MT_lock_unset(&sql_contextLock);
     474       37637 :         if (c->username && pwhash) {
     475             : 
     476       37249 :                 if (mvc_trans(m) < 0) {
     477             :                         // we have -1 here
     478           0 :                         MT_lock_set(&sql_contextLock);
     479           0 :                         throw(INVCRED, "checkCredentials", INVCRED_INVALID_USER " '%s'", c->username);
     480             :                 }
     481             : 
     482       37249 :                 msg = userCheckCredentials( m, c, pwhash, challenge, algo);
     483       37249 :                 if (msg)
     484           9 :                         goto bailout1;
     485       37240 :                 if (!GDKinmemory(0) && !GDKembedded()) {
     486       37240 :                         sabdb *stats = NULL;
     487       37240 :                         bool locked = false;
     488       37240 :                         char *err = msab_getMyStatus(&stats);
     489       37240 :                         if (err || stats == NULL)
     490           0 :                                 free(err);
     491             :                         else
     492       37240 :                                 locked = stats->locked;
     493       37240 :                         msab_freeStatus(&stats);
     494       37240 :                         if (locked) {
     495           0 :                                 if (c->user == 0) {
     496           0 :                                         mnstr_printf(c->fdout, "#server is running in "
     497             :                                                                  "maintenance mode\n");
     498             :                                 } else {
     499           0 :                                         msg = createException(SQL,"sql.initClient", SQLSTATE(42000) "server is running in maintenance mode, please try again later\n");
     500           0 :                                         goto bailout1;
     501             :                                 }
     502             :                         }
     503             :                 }
     504             : 
     505       37240 :                 switch (monet5_user_set_def_schema(m, c->user, c->username)) {
     506           0 :                         case -1:
     507           0 :                                 msg = createException(SQL,"sql.initClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     508           0 :                                 goto bailout1;
     509           0 :                         case -2:
     510           0 :                                 msg = createException(SQL,"sql.initClient", SQLSTATE(42000) "The user was not found in the database, this session is going to terminate");
     511           0 :                                 goto bailout1;
     512           0 :                         case -3:
     513           0 :                                 msg = createException(SQL,"sql.initClient", SQLSTATE(42000) "The user's default schema was not found, this session is going to terminate");
     514           0 :                                 goto bailout1;
     515           0 :                         case -4:
     516           0 :                                 msg = createException(SQL,"sql.initClient", SQLSTATE(42000) "The user's default role was not found, this session is going to terminate");
     517           0 :                                 goto bailout1;
     518             :                         default:
     519       37240 :                                 break;
     520             :                 }
     521       37240 :                 if (monet5_user_get_limits(m, m->user_id, &c->maxmem, &c->maxworkers) == 0) {
     522       37240 :                         c->qryctx.maxmem = (ATOMIC_BASE_TYPE) (c->maxmem > 0 ? c->maxmem : 0);
     523             :                 } else {
     524           0 :                         c->maxmem = 0;
     525           0 :                         c->qryctx.maxmem = 0;
     526           0 :                         c->maxworkers = 0;
     527             :                 }
     528       37240 :                 if (c->memorylimit > 0 && c->qryctx.maxmem > ((ATOMIC_BASE_TYPE) c->memorylimit << 20))
     529           0 :                         c->qryctx.maxmem = (ATOMIC_BASE_TYPE) c->memorylimit << 20;
     530       37240 :                 mvc_rollback(m, 0, NULL, false);
     531             :         }
     532             : 
     533       37628 :         if (c->handshake_options) {
     534       37239 :                 char *strtok_state = NULL;
     535       37239 :                 char *tok = strtok_r(c->handshake_options, ",", &strtok_state);
     536      187634 :                 while (tok != NULL) {
     537      150395 :                         int value;
     538      150395 :                         if (sscanf(tok, "auto_commit=%d", &value) == 1) {
     539       37238 :                                 bool auto_commit= value != 0;
     540       37238 :                                 m->session->auto_commit = auto_commit;
     541       37238 :                                 m->session->ac_on_commit = auto_commit;
     542      113157 :                         } else if (sscanf(tok, "reply_size=%d", &value) == 1) {
     543       37238 :                                 if (value < -1) {
     544           0 :                                         msg = createException(SQL, "SQLprepareClient", SQLSTATE(42000) "Reply_size cannot be negative");
     545           0 :                                         goto bailout1;
     546             :                                 }
     547       37238 :                                 m->reply_size = value;
     548       75919 :                         } else if (sscanf(tok, "size_header=%d", &value) == 1) {
     549       37238 :                                 be->sizeheader = value != 0;
     550       38681 :                         } else if (sscanf(tok, "columnar_protocol=%d", &value) == 1) {
     551        2886 :                                 c->protocol = (value != 0) ? PROTOCOL_COLUMNAR : PROTOCOL_9;
     552       37238 :                         } else if (sscanf(tok, "time_zone=%d", &value) == 1) {
     553       37238 :                                 sql_schema *s = mvc_bind_schema(m, "sys");
     554       37238 :                                 sql_var *var = find_global_var(m, s, "current_timezone");
     555       37238 :                                 ValRecord val;
     556       37238 :                                 VALinit(&val, TYPE_lng, &(lng){1000 * value});
     557       37238 :                                 if ((msg = sql_update_var(m, s, "current_timezone", &val)))
     558           0 :                                         goto bailout1;
     559       37238 :                                 sqlvar_set(var, &val);
     560             :                         } else {
     561           0 :                                 msg = createException(SQL, "SQLprepareClient", SQLSTATE(42000) "unexpected handshake option: %s", tok);
     562           0 :                                 goto bailout1;
     563             :                         }
     564             : 
     565      150395 :                         tok = strtok_r(NULL, ",", &strtok_state);
     566             :                 }
     567             :         }
     568             : 
     569             : 
     570         389 : bailout1:
     571       37637 :         if (m->session->tr->active)
     572           9 :                 mvc_rollback(m, 0, NULL, false);
     573       37637 :         MT_lock_set(&sql_contextLock);
     574       37637 : bailout2:
     575             :         /* expect SQL text first */
     576       37637 :         if (be)
     577       37637 :                 be->language = 'S';
     578             :         /* Set state, this indicates an initialized client scenario */
     579       37637 :         c->sqlcontext = be;
     580       37637 :         if (msg)
     581           9 :                 c->mode = FINISHCLIENT;
     582             :         return msg;
     583             : }
     584             : 
     585             : str
     586       37637 : SQLresetClient(Client c)
     587             : {
     588       37637 :         str msg = MAL_SUCCEED, other = MAL_SUCCEED;
     589             : 
     590       37637 :         if (c->sqlcontext == NULL)
     591           0 :                 throw(SQL, "SQLexitClient", SQLSTATE(42000) "MVC catalogue not available");
     592       37637 :         if (c->sqlcontext) {
     593       37637 :                 allocator *pa = NULL;
     594       37637 :                 backend *be = c->sqlcontext;
     595       37637 :                 mvc *m = be->mvc;
     596             : 
     597       37637 :                 assert(m->session);
     598       37637 :                 if (m->session->auto_commit && m->session->tr->active) {
     599          11 :                         if (mvc_status(m) >= 0)
     600          11 :                                 msg = mvc_commit(m, 0, NULL, false);
     601             :                 }
     602       37637 :                 if (m->session->tr->active)
     603        1028 :                         other = mvc_rollback(m, 0, NULL, false);
     604             : 
     605       37637 :                 res_tables_destroy(be->results);
     606       37637 :                 be->results = NULL;
     607             : 
     608       37637 :                 pa = m->pa;
     609       37637 :                 mvc_destroy(m);
     610       37637 :                 backend_destroy(be);
     611       37637 :                 c->sqlcontext = NULL;
     612       37637 :                 c->query = NULL;
     613       37637 :                 sa_destroy(pa);
     614             :         }
     615       37637 :         if (other && !msg)
     616             :                 msg = other;
     617       37637 :         else if (other && msg)
     618           0 :                 freeException(other);
     619             :         return msg;
     620             : }
     621             : 
     622             : MT_Id sqllogthread;
     623             : 
     624             : static str
     625         357 : SQLinit(Client c, const char *initpasswd)
     626             : {
     627         357 :         const char *debug_str = GDKgetenv("sql_debug");
     628         357 :         char *msg = MAL_SUCCEED, *other = MAL_SUCCEED;
     629         357 :         bool readonly = GDKgetenv_isyes("gdk_readonly");
     630         357 :         bool single_user = GDKgetenv_isyes("gdk_single_user");
     631         357 :         static int maybeupgrade = 1;
     632         357 :         backend *be = NULL;
     633         357 :         mvc *m = NULL;
     634         357 :         const char *opt_pipe;
     635             : 
     636         357 :         master_password = NULL;
     637         357 :         if (!GDKembedded() && !GDKinmemory(0)) {
     638         345 :                 msg = msab_pickSecret(&master_password);
     639         345 :                 if (msg)
     640             :                         return msg;
     641             :         }
     642             : 
     643         357 :         if ((opt_pipe = GDKgetenv("sql_optimizer")) && !isOptimizerPipe(opt_pipe))
     644           0 :                 throw(SQL, "sql.init", SQLSTATE(42000) "invalid sql optimizer pipeline %s", opt_pipe);
     645             : 
     646         357 :         MT_lock_set(&sql_contextLock);
     647             : 
     648         357 :         if (SQLstore) {
     649           0 :                 MT_lock_unset(&sql_contextLock);
     650           0 :                 return MAL_SUCCEED;
     651             :         }
     652             : 
     653         357 :         be_funcs.fcode = &monet5_freecode,
     654         357 :         be_funcs.fresolve_function = &monet5_resolve_function,
     655         357 :         be_funcs.fhas_module_function = &monet5_has_module,
     656         357 :         monet5_user_init(&be_funcs);
     657             : 
     658         357 :         if (debug_str)
     659         357 :                 SQLdebug = strtol(debug_str, NULL, 10);
     660         357 :         if (SQLdebug & 1)
     661           0 :                 GDKtracer_set_component_level("wal", "debug");
     662         357 :         if (single_user)
     663           0 :                 SQLdebug |= 64;
     664         357 :         if (readonly)
     665           4 :                 SQLdebug |= 32;
     666             : 
     667         713 :         if ((SQLstore = mvc_init(SQLdebug, GDKinmemory(0) ? store_mem : store_bat, readonly, single_user, initpasswd)) == NULL) {
     668           1 :                 MT_lock_unset(&sql_contextLock);
     669           1 :                 throw(SQL, "SQLinit", SQLSTATE(42000) "Catalogue initialization failed");
     670             :         }
     671         356 :         sqlinit = GDKgetenv("sqlinit");
     672         356 :         if (sqlinit) {          /* add sqlinit to the fdin stack */
     673           0 :                 buffer *b = (buffer *) GDKmalloc(sizeof(buffer));
     674           0 :                 size_t len = strlen(sqlinit);
     675           0 :                 char* cbuf = _STRDUP(sqlinit);
     676           0 :                 stream *buf;
     677           0 :                 bstream *fdin;
     678             : 
     679           0 :                 if ( b == NULL || cbuf == NULL) {
     680           0 :                         mvc_exit(SQLstore);
     681           0 :                         SQLstore = NULL;
     682           0 :                         MT_lock_unset(&sql_contextLock);
     683           0 :                         GDKfree(b);
     684           0 :                         GDKfree(cbuf);
     685           0 :                         throw(SQL,"sql.init",SQLSTATE(HY013) MAL_MALLOC_FAIL);
     686             :                 }
     687             : 
     688           0 :                 buffer_init(b, cbuf, len);
     689           0 :                 buf = buffer_rastream(b, "si");
     690           0 :                 if ( buf == NULL) {
     691           0 :                         mvc_exit(SQLstore);
     692           0 :                         SQLstore = NULL;
     693           0 :                         MT_lock_unset(&sql_contextLock);
     694           0 :                         buffer_destroy(b);
     695           0 :                         throw(SQL,"sql.init",SQLSTATE(HY013) MAL_MALLOC_FAIL);
     696             :                 }
     697             : 
     698           0 :                 fdin = bstream_create(buf, b->len);
     699           0 :                 if ( fdin == NULL) {
     700           0 :                         mvc_exit(SQLstore);
     701           0 :                         SQLstore = NULL;
     702           0 :                         MT_lock_unset(&sql_contextLock);
     703           0 :                         buffer_destroy(b);
     704           0 :                         throw(SQL,"sql.init",SQLSTATE(HY013) MAL_MALLOC_FAIL);
     705             :                 }
     706             : 
     707           0 :                 bstream_next(fdin);
     708           0 :                 if ( MCpushClientInput(c, fdin, 0, "") < 0)
     709           0 :                         TRC_ERROR(SQL_PARSER, "Could not switch client input stream\n");
     710             :         }
     711         356 :         if ((msg = SQLprepareClient(c, NULL, NULL, NULL)) != NULL) {
     712           0 :                 mvc_exit(SQLstore);
     713           0 :                 SQLstore = NULL;
     714           0 :                 MT_lock_unset(&sql_contextLock);
     715           0 :                 TRC_INFO(SQL_PARSER, "%s\n", msg);
     716           0 :                 return msg;
     717             :         }
     718         356 :         be = c->sqlcontext;
     719         356 :         m = be->mvc;
     720             :         /* initialize the database with predefined SQL functions */
     721         356 :         sqlstore *store = SQLstore;
     722         356 :         if (store->first == 0) {
     723             :                 /* check whether last created object trigger sys.system_update_tables (from 99_system.sql) exists.
     724             :                  * if it doesn't, this is probably a restart of the
     725             :                  * server after an incomplete initialization */
     726         127 :                 if ((msg = SQLtrans(m)) == MAL_SUCCEED) {
     727             :                         /* TODO there's a going issue with loading triggers due to system tables,
     728             :                            so at the moment check for existence of 'json' schema from 40_json.sql */
     729         127 :                         if (!mvc_bind_schema(m, "json"))
     730           0 :                                 store->first = 1;
     731         127 :                         msg = mvc_rollback(m, 0, NULL, false);
     732             :                 }
     733         127 :                 if (msg) {
     734           0 :                         freeException(msg);
     735           0 :                         msg = MAL_SUCCEED;
     736             :                 }
     737             :         }
     738         356 :         if (store->first > 0) {
     739         229 :                 store->first = 0;
     740         229 :                 maybeupgrade = 0;
     741             : 
     742         229 :                 qsort(sql_module, sql_modules, sizeof(sql_module[0]), sql_module_compare);
     743        9154 :                 for (int i = 0; i < sql_modules && !msg; i++) {
     744        8925 :                         const char *createdb_inline = (const char*)sql_module[i].code;
     745             : 
     746        8925 :                         msg = SQLstatementIntern(c, createdb_inline, "sql.init", TRUE, FALSE, NULL);
     747        8925 :                         if (m->sa)
     748           0 :                                 sa_destroy(m->sa);
     749        8925 :                         m->sa = NULL;
     750             :                 }
     751             :                 /* 99_system.sql */
     752         229 :                 if (!msg) {
     753         229 :                         const char *createdb_inline =
     754             :                                 "create trigger system_update_schemas after update on sys.schemas for each statement call sys_update_schemas();\n"
     755             :                                 //"create trigger system_update_tables after update on sys._tables for each statement call sys_update_tables();\n"
     756             :                                 /* set "system" attribute for all system schemas; be
     757             :                                  * explicit about which ones they are (id 2000 is sys,
     758             :                                  * 2114 is tmp; these values are immutable) */
     759             :                                 "update sys.schemas set system = true where id in (2000, 2114) or name in ('json', 'profiler', 'logging', 'information_schema');\n"
     760             :                                 /* correct invalid FK schema ids, set them to schema id 2000
     761             :                                  * (the "sys" schema) */
     762             :                                 "update sys.types set schema_id = 2000 where schema_id = 0;\n"
     763             :                                 "update sys.functions set schema_id = 2000 where schema_id = 0;\n"
     764             :                                 /* set system attribute for all system tables and
     765             :                                  * functions (i.e. ones in system schemas) */
     766             :                                 "update sys.functions set system = true where schema_id in (select id from sys.schemas s where s.system);\n"
     767             :                                 "update sys._tables set system = true where schema_id in (select id from sys.schemas s where s.system);\n";
     768         229 :                         msg = SQLstatementIntern(c, createdb_inline, "sql.init", TRUE, FALSE, NULL);
     769         229 :                         if (m->sa)
     770           0 :                                 sa_destroy(m->sa);
     771         229 :                         m->sa = NULL;
     772             :                 }
     773             :                 /* Commit after all the startup scripts have been processed */
     774         229 :                 assert(m->session->tr->active);
     775         229 :                 if (mvc_status(m) < 0 || msg)
     776           0 :                         other = mvc_rollback(m, 0, NULL, false);
     777             :                 else
     778         229 :                         other = mvc_commit(m, 0, NULL, false);
     779             : 
     780         229 :                 if (other && !msg) /* 'msg' variable might be set or not, as well as 'other'. Throw the earliest one */
     781             :                         msg = other;
     782         229 :                 else if (other)
     783           0 :                         freeException(other);
     784         229 :                 if (msg)
     785           0 :                         TRC_INFO(SQL_PARSER, "%s\n", msg);
     786             :         } else {                /* handle upgrades */
     787         127 :                 if (!m->sa)
     788         127 :                         m->sa = sa_create(m->pa);
     789         127 :                 if (!m->sa) {
     790           0 :                         msg = createException(MAL, "createdb", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     791         127 :                 } else if (maybeupgrade) {
     792         117 :                         if ((msg = SQLtrans(m)) == MAL_SUCCEED) {
     793         117 :                                 int res = SQLupgrades(c, m);
     794             :                                 /* Commit at the end of the upgrade */
     795         117 :                                 assert(m->session->tr->active);
     796         117 :                                 if (mvc_status(m) < 0 || res)
     797           0 :                                         msg = mvc_rollback(m, 0, NULL, false);
     798             :                                 else
     799         117 :                                         msg = mvc_commit(m, 0, NULL, false);
     800             :                         }
     801             :                 }
     802         127 :                 maybeupgrade = 0;
     803             :         }
     804         356 :         fflush(stdout);
     805         356 :         fflush(stderr);
     806             : 
     807             :         /* send error from create scripts back to the first client */
     808         356 :         if (msg) {
     809           0 :                 msg = handle_error(m, 0, msg);
     810           0 :                 *m->errstr = 0;
     811           0 :                 sqlcleanup(be, mvc_status(m));
     812             :         }
     813             : 
     814         356 :         other = SQLresetClient(c);
     815         356 :         if (other && !msg) /* 'msg' variable might be set or not, as well as 'other'. Throw the earliest one */
     816             :                 msg = other;
     817         356 :         else if (other)
     818           0 :                 freeException(other);
     819         356 :         if (msg != MAL_SUCCEED) {
     820           0 :                 mvc_exit(SQLstore);
     821           0 :                 SQLstore = NULL;
     822           0 :                 MT_lock_unset(&sql_contextLock);
     823           0 :                 return msg;
     824             :         }
     825             : 
     826         356 :         if (GDKinmemory(0)) {
     827           1 :                 MT_lock_unset(&sql_contextLock);
     828           1 :                 return msg;
     829             :         }
     830             : 
     831         355 :         if (MT_create_thread(&sqllogthread, mvc_logmanager, SQLstore, MT_THR_DETACHED, "logmanager") < 0) {
     832           0 :                 mvc_exit(SQLstore);
     833           0 :                 SQLstore = NULL;
     834           0 :                 MT_lock_unset(&sql_contextLock);
     835           0 :                 throw(SQL, "SQLinit", SQLSTATE(42000) "Starting log manager failed");
     836             :         }
     837             : 
     838         355 :         MT_lock_unset(&sql_contextLock);
     839         355 :         return MAL_SUCCEED;
     840             : }
     841             : 
     842             : #define TRANS_ABORTED SQLSTATE(25005) "Current transaction is aborted (please ROLLBACK)\n"
     843             : 
     844             : str
     845       18818 : handle_error(mvc *m, int pstatus, str msg)
     846             : {
     847       18818 :         str new = NULL, newmsg = MAL_SUCCEED;
     848             : 
     849             :         /* transaction already broken */
     850       18818 :         if (m->type != Q_TRANS && pstatus < 0) {
     851       16457 :                 freeException(msg);
     852       16457 :                 return createException(SQL,"sql.execute",TRANS_ABORTED);
     853        2361 :         } else if ( GDKerrbuf && GDKerrbuf[0]){
     854           0 :                 new = GDKstrdup(GDKerrbuf);
     855           0 :                 GDKerrbuf[0] = 0;
     856        2361 :         } else if ( *m->errstr){
     857           8 :                 new = GDKstrdup(m->errstr);
     858           8 :                 m->errstr[0] = 0;
     859             :         }
     860        2361 :         if ( new && msg){
     861           0 :                 newmsg = concatErrors(msg, new);
     862           0 :                 GDKfree(new);
     863        2361 :         } else if (msg)
     864             :                 newmsg = msg;
     865           8 :         else if (new) {
     866           8 :                 newmsg = createException(SQL, "sql.execute", "%s", new);
     867           8 :                 GDKfree(new);
     868             :         } else {
     869           0 :                 newmsg = createException(SQL, "sql.execute", MAL_MALLOC_FAIL);
     870             :         }
     871             :         return newmsg;
     872             : }
     873             : 
     874             : str
     875      338260 : SQLautocommit(mvc *m)
     876             : {
     877      338260 :         str msg = MAL_SUCCEED;
     878             : 
     879      338260 :         if (m->session->auto_commit && m->session->tr->active) {
     880      210956 :                 if (mvc_status(m) < 0) {
     881       20619 :                         msg = mvc_rollback(m, 0, NULL, false);
     882             :                 } else {
     883      190337 :                         msg = mvc_commit(m, 0, NULL, false);
     884             :                 }
     885             :         }
     886      338260 :         return msg;
     887             : }
     888             : 
     889             : str
     890      453783 : SQLtrans(mvc *m)
     891             : {
     892      453783 :         if (!m->session->tr->active) {
     893      214031 :                 sql_session *s;
     894             : 
     895      214031 :                 switch (mvc_trans(m)) {
     896           0 :                         case -1:
     897           0 :                                 throw(SQL, "sql.trans", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     898           1 :                         case -3:
     899           1 :                                 throw(SQL, "sql.trans", SQLSTATE(42000) "The session's schema was not found, this transaction won't start");
     900             :                         default:
     901      214033 :                                 break;
     902             :                 }
     903      214033 :                 s = m->session;
     904      214033 :                 if (!s->schema) {
     905           0 :                         switch (monet5_user_get_def_schema(m, m->user_id, &s->def_schema_name)) {
     906           0 :                                 case -1:
     907           0 :                                         mvc_cancel_session(m);
     908           0 :                                         throw(SQL, "sql.trans", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     909           0 :                                 case -2:
     910           0 :                                         mvc_cancel_session(m);
     911           0 :                                         throw(SQL, "sql.trans", SQLSTATE(42000) "The user was not found in the database, this session is going to terminate");
     912           0 :                                 case -3:
     913           0 :                                         mvc_cancel_session(m);
     914           0 :                                         throw(SQL, "sql.trans", SQLSTATE(42000) "The user's default schema was not found, this session is going to terminate");
     915             :                                 default:
     916           0 :                                         break;
     917             :                         }
     918           0 :                         s->schema_name = s->def_schema_name;
     919           0 :                         if (!(s->schema = find_sql_schema(s->tr, s->schema_name))) {
     920           0 :                                 mvc_cancel_session(m);
     921           0 :                                 throw(SQL, "sql.trans", SQLSTATE(42000) "The session's schema was not found, this session is going to terminate");
     922             :                         }
     923             :                 }
     924             :         }
     925             :         return MAL_SUCCEED;
     926             : }
     927             : 
     928             : str
     929       37281 : SQLinitClient(Client c, const char *passwd, const char *challenge, const char *algo)
     930             : {
     931       37281 :         str msg = MAL_SUCCEED;
     932             : 
     933       37281 :         MT_lock_set(&sql_contextLock);
     934       37281 :         if (!SQLstore) {
     935           0 :                 MT_lock_unset(&sql_contextLock);
     936           0 :                 throw(SQL, "SQLinitClient", SQLSTATE(42000) "Catalogue not available");
     937             :         }
     938       37281 :         if ((msg = SQLprepareClient(c, passwd, challenge, algo)) == MAL_SUCCEED) {
     939       37272 :                 if (c->usermodule && (c->user != MAL_ADMIN) && (SQLexecPostLoginTriggers(c) != MAL_SUCCEED)) {
     940           0 :                         MT_lock_unset(&sql_contextLock);
     941           0 :                         throw(SQL, "SQLinitClient", SQLSTATE(42000) "Failed to execute post login triggers");
     942             :                 }
     943             :         }
     944       37281 :         MT_lock_unset(&sql_contextLock);
     945       37281 :         return msg;
     946             : }
     947             : 
     948             : str
     949         188 : SQLinitClientFromMAL(Client c, const char *passwd, const char *challenge, const char *algo)
     950             : {
     951         188 :         str msg = MAL_SUCCEED;
     952             : 
     953         188 :         if ((msg = SQLinitClient(c, passwd, challenge, algo)) != MAL_SUCCEED) {
     954           0 :                 c->mode = FINISHCLIENT;
     955           0 :                 return msg;
     956             :         }
     957             : 
     958         188 :         mvc* m = ((backend*) c->sqlcontext)->mvc;
     959         188 :         if (c->glb)
     960         188 :                 c->glb->keepTmps = true;
     961             : 
     962             :         /* Crucial step:
     963             :          * MAL scripts that interact with the sql module
     964             :          * must have a properly initialized transaction.
     965             :          */
     966         188 :         if ((msg = SQLtrans(m)) != MAL_SUCCEED) {
     967           0 :                 c->mode = FINISHCLIENT;
     968           0 :                 return msg;
     969             :         }
     970             :         return msg;
     971             : }
     972             : 
     973             : str
     974       37272 : SQLexitClient(Client c)
     975             : {
     976       37272 :         str err;
     977             : 
     978       37272 :         MT_lock_set(&sql_contextLock);
     979       37272 :         if (!SQLstore) {
     980           0 :                 MT_lock_unset(&sql_contextLock);
     981           0 :                 throw(SQL, "SQLexitClient", SQLSTATE(42000) "Catalogue not available");
     982             :         }
     983       37272 :         err = SQLresetClient(c);
     984       37272 :         MT_lock_unset(&sql_contextLock);
     985       37272 :         if (err != MAL_SUCCEED)
     986             :                 return err;
     987       37272 :         err = MALexitClient(c);
     988       37272 :         if (err != MAL_SUCCEED)
     989             :                 return err;
     990             :         return MAL_SUCCEED;
     991             : }
     992             : 
     993             : str
     994          12 : SQLstatement(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
     995             : {
     996          12 :         const char *expr = *getArgReference_str(stk, pci, 1);
     997             : 
     998          12 :         (void) mb;
     999             : 
    1000          12 :         protocol_version backup = cntxt->protocol;
    1001             : 
    1002          12 :         if (pci->argc == 3 && *getArgReference_bit(stk, pci, 2))
    1003           1 :                 cntxt->protocol = PROTOCOL_COLUMNAR;
    1004             : 
    1005          12 :         str msg = SQLstatementIntern(cntxt, expr, "SQLstatement", TRUE, TRUE, NULL);
    1006             : 
    1007          12 :         cntxt->protocol = backup;
    1008             : 
    1009          12 :         return msg;
    1010             : }
    1011             : 
    1012             : /*
    1013             :  * Locate a file with SQL commands and execute it. For the time being a 1MB
    1014             :  * file limit is implicitly imposed. If the file can not be located in the
    1015             :  * script library, we assume it is sufficiently self descriptive.
    1016             :  * (Respecting the file system context where the call is executed )
    1017             :  */
    1018             : str
    1019           0 : SQLinclude(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1020             : {
    1021           0 :         stream *fd;
    1022           0 :         bstream *bfd;
    1023           0 :         str *name = getArgReference_str(stk, pci, 1);
    1024           0 :         str msg = MAL_SUCCEED, fullname;
    1025           0 :         mvc *m;
    1026           0 :         size_t sz;
    1027             : 
    1028           0 :         fullname = MSP_locate_sqlscript(*name, 0);
    1029           0 :         if (fullname == NULL)
    1030           0 :                 fullname = *name;
    1031           0 :         fd = open_rastream(fullname);
    1032           0 :         if (mnstr_errnr(fd) == MNSTR_OPEN_ERROR) {
    1033           0 :                 close_stream(fd);
    1034           0 :                 throw(MAL, "sql.include", SQLSTATE(42000) "%s\n", mnstr_peek_error(NULL));
    1035             :         }
    1036           0 :         sz = getFileSize(fd);
    1037           0 :         if (sz > (size_t) 1 << 29) {
    1038           0 :                 close_stream(fd);
    1039           0 :                 throw(MAL, "sql.include", SQLSTATE(42000) "file %s too large to process", fullname);
    1040             :         }
    1041           0 :         if ((bfd = bstream_create(fd, sz == 0 ? (size_t) (128 * BLOCK) : sz)) == NULL) {
    1042           0 :                 close_stream(fd);
    1043           0 :                 throw(MAL, "sql.include", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1044             :         }
    1045           0 :         if (bstream_next(bfd) < 0) {
    1046           0 :                 bstream_destroy(bfd);
    1047           0 :                 throw(MAL, "sql.include", SQLSTATE(42000) "could not read %s\n", *name);
    1048             :         }
    1049             : 
    1050           0 :         msg = SQLstatementIntern(cntxt, bfd->buf, "sql.include", TRUE, FALSE, NULL);
    1051           0 :         bstream_destroy(bfd);
    1052           0 :         m = ((backend *) cntxt->sqlcontext)->mvc;
    1053           0 :         if (m->sa)
    1054           0 :                 sa_destroy(m->sa);
    1055           0 :         m->sa = NULL;
    1056           0 :         (void) mb;
    1057           0 :         return msg;
    1058             : }
    1059             : 
    1060             : /*
    1061             :  * The SQL reader collects a (sequence) of statements from the input
    1062             :  * stream, but only when no unresolved 'nxt' character is visible.
    1063             :  * In combination with SQLparser this ensures that all statements
    1064             :  * are handled one by one.
    1065             :  *
    1066             :  * The SQLreader is called from two places: the SQL parser and
    1067             :  * the MAL debugger.
    1068             :  * The former only occurs during the parsing phase and the
    1069             :  * second only during execution.
    1070             :  * This means we can safely change the language setting for
    1071             :  * the duration of these calls.
    1072             :  */
    1073             : 
    1074             : static str
    1075      523145 : SQLreader(Client c, backend *be)
    1076             : {
    1077      523145 :         bool go = true;
    1078      523145 :         str msg = MAL_SUCCEED;
    1079      523145 :         bool more = true;
    1080      523145 :         bool commit_done = false;
    1081      523145 :         bstream *in = c->fdin;
    1082      523145 :         int language = -1;
    1083      523145 :         mvc *m = NULL;
    1084      523145 :         bool blocked = isa_block_stream(in->s);
    1085             : 
    1086      523153 :         MT_lock_set(&mal_contextLock);
    1087      523160 :         if (!SQLstore || c->mode <= FINISHCLIENT) {
    1088           0 :                 c->mode = FINISHCLIENT;
    1089           0 :                 MT_lock_unset(&mal_contextLock);
    1090           0 :                 return MAL_SUCCEED;
    1091             :         }
    1092      523160 :         MT_lock_unset(&mal_contextLock);
    1093      523160 :         language = be->language;     /* 'S', 's' or 'X' */
    1094      523160 :         m = be->mvc;
    1095      523160 :         m->errstr[0] = 0;
    1096             :         /*
    1097             :          * Continue processing any left-over input from the previous round.
    1098             :          */
    1099             : 
    1100     1056528 :         while (more) {
    1101      571383 :                 more = false;
    1102             : 
    1103             :                 /* Different kinds of supported statements sequences
    1104             :                    A;   -- single line                  s
    1105             :                    A \n B;      -- multi line                   S
    1106             :                    A; B;   -- compound single block     s
    1107             :                    A;   -- many multi line
    1108             :                    B \n C; -- statements in one block   S
    1109             :                  */
    1110             :                 /* auto_commit on end of statement */
    1111      571383 :                 if (m->scanner.mode == LINE_N && !commit_done) {
    1112       79271 :                         msg = SQLautocommit(m);
    1113       79268 :                         if (msg)
    1114             :                                 break;
    1115             :                         commit_done = true;
    1116             :                 }
    1117      571380 :                 if (m->session->tr && m->session->tr->active) {
    1118      438441 :                         MT_lock_set(&mal_contextLock);
    1119      438441 :                         c->idle = 0;
    1120      438441 :                         MT_lock_unset(&mal_contextLock);
    1121             :                 }
    1122             : 
    1123      571346 :                 if (go && in->pos >= in->len) {
    1124      386252 :                         ssize_t rd;
    1125             : 
    1126      386252 :                         if (c->bak) {
    1127           0 :                                 in = c->fdin;
    1128           0 :                                 blocked = isa_block_stream(in->s);
    1129           0 :                                 m->scanner.rs = c->fdin;
    1130           0 :                                 c->fdin->pos += c->yycur;
    1131           0 :                                 c->yycur = 0;
    1132             :                         }
    1133      386252 :                         if (in->eof || !blocked) {
    1134      338189 :                                 language = 0;
    1135             : 
    1136             :                                 /* The rules of auto_commit require us to finish
    1137             :                                    and start a transaction on the start of a new statement (s A;B; case) */
    1138      338189 :                                 if (!commit_done) {
    1139      258749 :                                         msg = SQLautocommit(m);
    1140      258749 :                                         if (msg)
    1141             :                                                 break;
    1142      257819 :                                         commit_done = true;
    1143      257819 :                                         MT_lock_set(&mal_contextLock);
    1144      257819 :                                         if (c->idle == 0 && (m->session->tr == NULL || !m->session->tr->active)) {
    1145             :                                                 /* now the session is idle */
    1146      206757 :                                                 c->idle = time(0);
    1147             :                                         }
    1148      257819 :                                         MT_lock_unset(&mal_contextLock);
    1149             :                                 }
    1150             : 
    1151      337259 :                                 if (go && ((!blocked && mnstr_write(c->fdout, c->prompt, c->promptlength, 1) != 1) || mnstr_flush(c->fdout, MNSTR_FLUSH_DATA))) {
    1152             :                                         go = false;
    1153             :                                         break;
    1154             :                                 }
    1155      337264 :                                 in->eof = false;
    1156             :                         }
    1157      385327 :                         while (bstream_getoob(in) > 0)
    1158             :                                 ;
    1159      385329 :                         m->scanner.aborted = false;
    1160      385329 :                         if (in->buf == NULL) {
    1161             :                                 more = false;
    1162             :                                 go = false;
    1163      385329 :                         } else if (go && (rd = bstream_next(in)) <= 0) {
    1164       85292 :                                 if (rd == 0 && in->eof && !mnstr_eof(in->s)) {
    1165             :                                         /* we hadn't seen the EOF before, so just try again
    1166             :                                            (this time with prompt) */
    1167       48243 :                                         more = true;
    1168       48243 :                                         continue;
    1169             :                                 }
    1170             :                                 go = false;
    1171             :                                 break;
    1172      300032 :                         } else if (go && language == 0) {
    1173      300031 :                                 if (in->buf[in->pos] == 's' && !in->eof) {
    1174      252012 :                                         while ((rd = bstream_next(in)) > 0)
    1175             :                                                 ;
    1176             :                                 }
    1177      300030 :                                 be->language = in->buf[in->pos++];
    1178      300030 :                                 if (be->language == 's') {
    1179      251893 :                                         be->language = 'S';
    1180      251893 :                                         m->scanner.mode = LINE_1;
    1181       48137 :                                 } else if (be->language == 'S') {
    1182        4915 :                                         m->scanner.mode = LINE_N;
    1183             :                                 }
    1184             :                         }
    1185             :                 }
    1186             :         }
    1187      523125 :         if ( (c->sessiontimeout && (GDKusec() - c->session) > c->sessiontimeout) || !go || (strncmp(CURRENT(c), "\\q", 2) == 0)) {
    1188       37048 :                 in->pos = in->len;        /* skip rest of the input */
    1189       37048 :                 MT_lock_set(&mal_contextLock);
    1190       37050 :                 c->mode = FINISHCLIENT;
    1191       37050 :                 MT_lock_unset(&mal_contextLock);
    1192       37050 :                 return msg;
    1193             :         }
    1194             :         return msg;
    1195             : }
    1196             : 
    1197             : static str
    1198       43222 : SQLchannelcmd(Client c, backend *be)
    1199             : {
    1200       43222 :         assert(be->language == 'X');
    1201             : 
    1202       43222 :         bstream *in = c->fdin;
    1203       43222 :         stream *out = c->fdout;
    1204       43222 :         mvc *m = be->mvc;
    1205       43222 :         str msg = MAL_SUCCEED;
    1206       43222 :         int n = 0, v, off, len, ok;
    1207             : 
    1208       43222 :         if (strncmp(in->buf + in->pos, "export ", 7) == 0)
    1209          77 :                 n = sscanf(in->buf + in->pos + 7, "%d %d %d", &v, &off, &len);
    1210             : 
    1211       43222 :         if (n == 2 || n == 3) {
    1212          77 :                 if (n == 2)
    1213          27 :                         len = m->reply_size;
    1214          77 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1215          77 :                 if ((ok = mvc_export_chunk(be, out, v, off, len < 0 ? BUN_NONE : (BUN) len)) < 0) {
    1216           0 :                         sqlcleanup(be, 0);
    1217           0 :                         return createException(SQL, "SQLparser", SQLSTATE(45000) "Result set construction failed: %s", mvc_export_error(be, out, ok));
    1218             :                 }
    1219             :                 return MAL_SUCCEED;
    1220             :         }
    1221       43145 :         if (strncmp(in->buf + in->pos, "exportbin ", 10) == 0) {
    1222         606 :                 n = sscanf(in->buf + in->pos + 10, "%d %d %d", &v, &off, &len);
    1223         606 :                 if (n == 3) {
    1224         606 :                         if ((ok = mvc_export_bin_chunk(be, out, v, off, len < 0 ? BUN_NONE: (BUN) len)) < 0) {
    1225           0 :                                 msg = createException(SQL, "SQLparser", SQLSTATE(45000) "Result set construction failed: %s", mvc_export_error(be, out, ok));
    1226           0 :                                 in->pos = in->len;        /* HACK: should use parsed length */
    1227           0 :                                 sqlcleanup(be, 0);
    1228           0 :                                 return msg;
    1229             :                         }
    1230         606 :                         in->pos = in->len;        /* HACK: should use parsed length */
    1231         606 :                         return MAL_SUCCEED;
    1232             :                 }
    1233             :         }
    1234       42539 :         if (strncmp(in->buf + in->pos, "close ", 6) == 0) {
    1235        4560 :                 res_table *t;
    1236             : 
    1237        4560 :                 v = (int) strtol(in->buf + in->pos + 6, NULL, 0);
    1238        4560 :                 t = res_tables_find(be->results, v);
    1239        4560 :                 if (t)
    1240         495 :                         be->results = res_tables_remove(be->results, t);
    1241        4560 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1242        4560 :                 return MAL_SUCCEED;
    1243             :         }
    1244       37979 :         if (strncmp(in->buf + in->pos, "release ", 8) == 0) {
    1245         211 :                 cq *q = NULL;
    1246             : 
    1247         211 :                 v = (int) strtol(in->buf + in->pos + 8, NULL, 0);
    1248         211 :                 if ((q = qc_find(m->qc, v)) != NULL)
    1249         204 :                          qc_delete(m->qc, q);
    1250         211 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1251         211 :                 return MAL_SUCCEED;
    1252             :         }
    1253       37768 :         if (strncmp(in->buf + in->pos, "auto_commit ", 12) == 0) {
    1254         173 :                 int commit;
    1255         173 :                 v = (int) strtol(in->buf + in->pos + 12, NULL, 10);
    1256         173 :                 commit = (!m->session->auto_commit && v);
    1257         173 :                 m->session->auto_commit = (v) != 0;
    1258         173 :                 m->session->ac_on_commit = m->session->auto_commit;
    1259         173 :                 if (m->session->tr->active) {
    1260           2 :                         if (commit) {
    1261           1 :                                 msg = mvc_commit(m, 0, NULL, true);
    1262             :                         } else {
    1263           1 :                                 msg = mvc_rollback(m, 0, NULL, true);
    1264             :                         }
    1265             :                 }
    1266         173 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1267         173 :                 if (msg != NULL)
    1268           0 :                         sqlcleanup(be, 0);
    1269         173 :                 return msg;
    1270             :         }
    1271       37595 :         static const char columnar_protocol[] = "columnar_protocol ";
    1272       37595 :         if (strncmp(in->buf + in->pos, columnar_protocol, strlen(columnar_protocol)) == 0) {
    1273           0 :                 v = (int) strtol(in->buf + in->pos + strlen(columnar_protocol), NULL, 10);
    1274             : 
    1275           0 :                 c->protocol = v?PROTOCOL_COLUMNAR:PROTOCOL_9;
    1276             : 
    1277           0 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1278           0 :                 return MAL_SUCCEED;
    1279             :         }
    1280       37595 :         if (strncmp(in->buf + in->pos, "reply_size ", 11) == 0) {
    1281         545 :                 v = (int) strtol(in->buf + in->pos + 11, NULL, 10);
    1282         545 :                 if (v < -1) {
    1283           0 :                         sqlcleanup(be, 0);
    1284           0 :                         return createException(SQL, "SQLparser", SQLSTATE(42000) "Reply_size cannot be negative");
    1285             :                 }
    1286         545 :                 m->reply_size = v;
    1287         545 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1288         545 :                 return MAL_SUCCEED;
    1289             :         }
    1290       37050 :         if (strncmp(in->buf + in->pos, "sizeheader", 10) == 0) { // no underscore
    1291           0 :                 v = (int) strtol(in->buf + in->pos + 10, NULL, 10);
    1292           0 :                 be->sizeheader = v != 0;
    1293           0 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1294           0 :                 return MAL_SUCCEED;
    1295             :         }
    1296       37050 :         if (strncmp(in->buf + in->pos, "clientinfo ", 11) == 0) {
    1297       37050 :                 in->pos += 11;
    1298       37050 :                 char *end = in->buf + in->len;
    1299       37050 :                 char *key = in->buf + in->pos;
    1300      221044 :                 while (key < end) {
    1301      183994 :                         char *p = memchr(key, '\n', end - key);
    1302      183994 :                         if (!p)
    1303           0 :                                 return createException(SQL, "SQLparser", SQLSTATE(42000) "no trailing newline in clientinfo");
    1304      183994 :                         *p = '\0';
    1305      183994 :                         char *q = memchr(key, '=', p - key);
    1306      183994 :                         if (!q)
    1307           0 :                                 return createException(SQL, "SQLparser", SQLSTATE(42000) "found no = in clientinfo");
    1308      183994 :                         *q = '\0';
    1309      183994 :                         char *value = q + 1;
    1310      219787 :                         MCsetClientInfo(c, key, *value ? value : NULL);
    1311      183994 :                         key = p + 1;
    1312             :                 }
    1313       37050 :                 in->pos = in->len;
    1314       37050 :                 return MAL_SUCCEED;
    1315             :         }
    1316           0 :         if (strncmp(in->buf + in->pos, "quit", 4) == 0) {
    1317           0 :                 c->mode = FINISHCLIENT;
    1318           0 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1319           0 :                 return MAL_SUCCEED;
    1320             :         }
    1321           0 :         in->pos = in->len;        /* HACK: should use parsed length */
    1322           0 :         msg = createException(SQL, "SQLparser", SQLSTATE(42000) "Unrecognized X command: %s\n", in->buf + in->pos);
    1323           0 :         sqlcleanup(be, 0);
    1324           0 :         return msg;
    1325             : }
    1326             : 
    1327             : /*
    1328             :  * The SQL block is stored in the client input buffer, from which it
    1329             :  * can be parsed by the SQL parser. The client structure contains
    1330             :  * a small table of bounded tables. This should be reset before we
    1331             :  * parse a new statement sequence.
    1332             :  * Before we parse the sql statement, we look for any variable settings
    1333             :  * for specific commands.
    1334             :  * The most important one is to prepare code to be handled by the debugger.
    1335             :  * The current analysis is simple and fulfills our short-term needs.
    1336             :  * A future version may analyze the parameter settings in more detail.
    1337             :  */
    1338             : 
    1339             : #define MAX_QUERY       (64*1024*1024)
    1340             : 
    1341             : static str
    1342      441950 : SQLparser_body(Client c, backend *be)
    1343             : {
    1344      441950 :         str msg = MAL_SUCCEED;
    1345      441950 :         mvc *m = be->mvc;
    1346      441950 :         lng Tbegin = 0, Tend = 0;
    1347             : 
    1348      441950 :         int pstatus = m->session->status;
    1349             : 
    1350      441950 :         int err = 0;
    1351      441950 :         m->type = Q_PARSE;
    1352      441950 :         m->emode = m_normal;
    1353      441950 :         m->emod = mod_none;
    1354      441950 :         c->query = NULL;
    1355      441950 :         c->qryctx.starttime = Tbegin = Tend = GDKusec();
    1356      441944 :         c->qryctx.endtime = c->querytimeout ? c->qryctx.starttime + c->querytimeout : 0;
    1357             : 
    1358      441944 :         if ((err = sqlparse(m)) ||
    1359      441574 :                 m->scanner.aborted ||
    1360      883165 :                 ((m->scanner.aborted |= bstream_getoob(m->scanner.rs) != 0) != false) ||
    1361             :             /* Only forget old errors on transaction boundaries */
    1362      883181 :             (mvc_status(m) && m->type != Q_TRANS) || !m->sym) {
    1363      149694 :                 if (!err && m->scanner.started)      /* repeat old errors, with a parsed query */
    1364       17180 :                         err = mvc_status(m);
    1365      150059 :                 if (m->scanner.aborted) {
    1366           0 :                         msg = createException(PARSE, "SQLparser", "Query aborted");
    1367           0 :                         *m->errstr = 0;
    1368      150059 :                 } else if (err && *m->errstr) {
    1369        1071 :                         if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
    1370        1070 :                                 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
    1371             :                         else
    1372           1 :                                 msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
    1373        1071 :                         *m->errstr = 0;
    1374             :                 }
    1375      150059 :                 if (m->sym)
    1376       16457 :                         msg = handle_error(m, pstatus, msg);
    1377      150059 :                 if (!m->sym) /* skip empty input */
    1378      133604 :                         m->emode = m_deallocate;
    1379      150059 :                 sqlcleanup(be, err);
    1380      150054 :                 goto finalize;
    1381             :         }
    1382             :         /*
    1383             :          * We have dealt with the first parsing step and advanced the input reader
    1384             :          * to the next statement (if any).
    1385             :          * Now is the time to also perform the semantic analysis, optimize and
    1386             :          * produce code.
    1387             :          */
    1388      291895 :         c->query = query_cleaned(m->sa, QUERY(m->scanner));
    1389             : 
    1390      291882 :         if (profilerStatus > 0) {
    1391           0 :                 profilerEvent(NULL,
    1392             :                                           &(struct NonMalEvent)
    1393           0 :                                           {TEXT_TO_SQL, c, Tend, &m->session->tr->ts, NULL, c->query?0:1, Tend-Tbegin});
    1394             :         }
    1395             : 
    1396      291882 :         if (c->query == NULL) {
    1397           0 :                 err = 1;
    1398           0 :                 msg = createException(PARSE, "SQLparser", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1399      291882 :         } else if (m->emode == m_deallocate) {
    1400           8 :                 AtomNode *an = (AtomNode *) m->sym;
    1401           8 :                 assert(m->sym->type == type_symbol && an->a->data.vtype == TYPE_int);
    1402           8 :                 int preparedid = an->a->data.val.ival;
    1403             : 
    1404           8 :                 if (preparedid > -1) { /* The -1 case represents the deallocate the entire query cache */
    1405           4 :                         be->q = qc_find(m->qc, preparedid);
    1406           4 :                         if (!be->q) {
    1407           2 :                                 msg = createException(SQL, "DEALLOC", SQLSTATE(07003) "No prepared statement with id: %d\n", preparedid);
    1408           2 :                                 *m->errstr = 0;
    1409           2 :                                 msg = handle_error(m, pstatus, msg);
    1410           2 :                                 sqlcleanup(be, -1);
    1411           2 :                                 return msg;
    1412             :                         } else {
    1413           2 :                                 qc_delete(m->qc, be->q);
    1414           2 :                                 be->q = NULL;
    1415             :                         }
    1416             :                 } else {
    1417           4 :                         qc_clean(m->qc);
    1418             :                 }
    1419             : 
    1420           6 :                 m->type = Q_SCHEMA; /* TODO DEALLOCATE statements don't fit for Q_SCHEMA */
    1421           6 :                 mvc_query_processed(m);
    1422             : 
    1423             :                 /* For deallocate statements just export a simple output */
    1424           6 :                 if (!GDKembedded() && (err = mvc_export_operation(be, c->fdout, "", c->qryctx.starttime, c->curprg->def->optimize)) < 0)
    1425           0 :                         msg = createException(PARSE, "SQLparser", SQLSTATE(45000) "Export operation failed: %s", mvc_export_error(be, c->fdout, err));
    1426           6 :                 sqlcleanup(be, 0);
    1427           6 :                 return msg;
    1428             :         } else {
    1429      291874 :                 sql_rel *r = sql_symbol2relation(be, m->sym);
    1430             : 
    1431      291881 :                 if (!r || (err = mvc_status(m) && m->type != Q_TRANS && *m->errstr)) {
    1432        2348 :                         if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
    1433        2346 :                                 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
    1434             :                         else
    1435           2 :                                 msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
    1436        2348 :                         *m->errstr = 0;
    1437        2348 :                         msg = handle_error(m, pstatus, msg);
    1438        2348 :                         sqlcleanup(be, err);
    1439        2348 :                         goto finalize;
    1440             :                 }
    1441             : 
    1442      289534 :                 int oldvtop = c->curprg->def->vtop;
    1443      289534 :                 int oldstop = c->curprg->def->stop;
    1444      289534 :                 (void)runtimeProfileSetTag(c); /* generate and set the tag in the mal block of the clients current program. */
    1445      289540 :                 if (m->emode != m_prepare || (m->emode == m_prepare && (m->emod & mod_exec) && is_ddl(r->op)) /* direct execution prepare */) {
    1446      289206 :                         mvc_query_processed(m);
    1447             : 
    1448      289206 :                         err = 0;
    1449      289206 :                         setVarType(c->curprg->def, 0, 0);
    1450      289206 :                         if (m->emode != m_prepare && be->subbackend && be->subbackend->check(be->subbackend, r)) {
    1451           0 :                                 res_table *rt = NULL;
    1452           0 :                                 if (be->subbackend->exec(be->subbackend, r, be->result_id++, &rt) == NULL) { /* on error fall back */
    1453           0 :                                         be->subbackend->reset(be->subbackend);
    1454           0 :                                         if (rt) {
    1455           0 :                                                 rt->next = be->results;
    1456           0 :                                                 be->results = rt;
    1457             :                                         }
    1458           0 :                                         return NULL;
    1459             :                                 }
    1460           0 :                                 be->subbackend->reset(be->subbackend);
    1461             :                         }
    1462             : 
    1463      289206 :                         Tbegin = GDKusec();
    1464             : 
    1465      289206 :                         int opt = 0;
    1466      289206 :                         if (m->emode == m_prepare && (m->emod & mod_exec)) {
    1467             :                                 /* generated the named parameters for the placeholders */
    1468           3 :                                 if (backend_dumpstmt(be, c->curprg->def, r->r, !(m->emod & mod_exec), 0, c->query) < 0) {
    1469           0 :                                         msg = handle_error(m, 0, msg);
    1470           0 :                                         err = 1;
    1471           0 :                                         MSresetInstructions(c->curprg->def, oldstop);
    1472           0 :                                         freeVariables(c, c->curprg->def, NULL, oldvtop);
    1473             :                                 }
    1474           3 :                                 r = r->l;
    1475           3 :                                 m->emode = m_normal;
    1476           3 :                                 m->emod &= ~mod_exec;
    1477             :                         }
    1478      289206 :                         if (!err && backend_dumpstmt(be, c->curprg->def, r, !(m->emod & mod_exec), 0, c->query) < 0) {
    1479           7 :                                 msg = handle_error(m, 0, msg);
    1480           7 :                                 err = 1;
    1481           7 :                                 MSresetInstructions(c->curprg->def, oldstop);
    1482           7 :                                 freeVariables(c, c->curprg->def, NULL, oldvtop);
    1483           7 :                                 freeException(c->curprg->def->errors);
    1484           7 :                                 c->curprg->def->errors = NULL;
    1485             :                         } else
    1486      289194 :                                 opt = ((m->emod & mod_exec) == 0); /* no need to optimize prepare - execute */
    1487             : 
    1488      289201 :                         Tend = GDKusec();
    1489      289205 :                         if(profilerStatus > 0)
    1490           0 :                                 profilerEvent(NULL,
    1491             :                                                           &(struct NonMalEvent)
    1492           0 :                                                           {REL_TO_MAL, c, Tend, NULL, NULL, c->query?0:1, Tend-Tbegin});
    1493      289205 :                         if (err)
    1494           7 :                                 m->session->status = -10;
    1495           7 :                         if (err == 0) {
    1496             :                                 /* no parsing error encountered, finalize the code of the query wrapper */
    1497      289198 :                                 pushEndInstruction(c->curprg->def);
    1498             : 
    1499             :                                 /* check the query wrapper for errors */
    1500      289195 :                                 if (msg == MAL_SUCCEED)
    1501      289195 :                                         msg = chkTypes(c->usermodule, c->curprg->def, TRUE);
    1502             : 
    1503      289176 :                                 if (msg == MAL_SUCCEED && opt) {
    1504      283278 :                                         Tbegin = Tend;
    1505      283278 :                                         msg = SQLoptimizeQuery(c, c->curprg->def);
    1506      283297 :                                         Tend = GDKusec();
    1507      283297 :                                         if (profilerStatus > 0)
    1508           0 :                                                 profilerEvent(NULL,
    1509             :                                                           &(struct NonMalEvent)
    1510           0 :                                                           {MAL_OPT, c, Tend, NULL, NULL, msg==MAL_SUCCEED?0:1, Tend-Tbegin});
    1511      283297 :                                         if (msg != MAL_SUCCEED) {
    1512           1 :                                                 str other = c->curprg->def->errors;
    1513           1 :                                                 c->curprg->def->errors = 0;
    1514           1 :                                                 MSresetInstructions(c->curprg->def, oldstop);
    1515           1 :                                                 freeVariables(c, c->curprg->def, NULL, oldvtop);
    1516           1 :                                                 if (other != msg)
    1517           1 :                                                         freeException(other);
    1518           1 :                                                 goto finalize;
    1519             :                                         }
    1520             :                                 }
    1521             : 
    1522             :                                 /* we know more in this case than chkProgram(c->fdout, c->usermodule, c->curprg->def); */
    1523      289194 :                                 if (msg == MAL_SUCCEED && c->curprg->def->errors) {
    1524           0 :                                         msg = c->curprg->def->errors;
    1525           0 :                                         c->curprg->def->errors = 0;
    1526             :                                         /* restore the state */
    1527           0 :                                         MSresetInstructions(c->curprg->def, oldstop);
    1528           0 :                                         freeVariables(c, c->curprg->def, NULL, oldvtop);
    1529           0 :                                         if (msg == NULL && *m->errstr){
    1530             :                                                 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
    1531             :                                                         msg = createException(PARSE, "SQLparser", "%s", m->errstr);
    1532             :                                                 else
    1533             :                                                         msg = createException(PARSE, "SQLparser", SQLSTATE(M0M27) "Semantic errors %s", m->errstr);
    1534             :                                                 *m->errstr = 0;
    1535             :                                         } else if (msg) {
    1536           0 :                                                 str newmsg = createException(PARSE, "SQLparser", SQLSTATE(M0M27) "Semantic errors %s", msg);
    1537           0 :                                                 freeException(msg);
    1538           0 :                                                 msg = newmsg;
    1539             :                                         }
    1540             :                                 }
    1541             :                         }
    1542             :                 } else {
    1543         334 :                         char *q_copy = sa_strdup(m->sa, c->query);
    1544             : 
    1545         334 :                         be->q = NULL;
    1546         334 :                         if (!q_copy) {
    1547           0 :                                 msg = createException(PARSE, "SQLparser", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1548           0 :                                 err = 1;
    1549             :                         } else {
    1550         668 :                                 be->q = qc_insert(m->qc, m->sa,        /* the allocator */
    1551             :                                                   r,    /* keep relational query */
    1552         334 :                                                   m->sym,    /* the sql symbol tree */
    1553             :                                                   m->params, /* the argument list */
    1554             :                                                   m->type,   /* the type of the statement */
    1555             :                                                   q_copy,
    1556         334 :                                                   be->no_mitosis);
    1557         334 :                                 if (!be->q) {
    1558           0 :                                         msg = createException(PARSE, "SQLparser", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1559           0 :                                         err = 1;
    1560             :                                 }
    1561             :                         }
    1562         334 :                         mvc_query_processed(m);
    1563         334 :                         if (be->q && backend_dumpproc(be, c, be->q, r) < 0) {
    1564           1 :                                 msg = handle_error(m, 0, msg);
    1565           1 :                                 err = 1;
    1566             :                         }
    1567             : 
    1568             :                         /* passed over to query cache, used during dumpproc */
    1569         334 :                         m->sa = NULL;
    1570         334 :                         m->sym = NULL;
    1571         334 :                         m->runs = NULL;
    1572         334 :                         m->params = NULL;
    1573             : 
    1574         334 :                         if (be->q) {
    1575         334 :                                 int res = 0;
    1576         334 :                                 if (!err && (res = mvc_export_prepare(be, c->fdout)) < 0) {
    1577           0 :                                         msg = createException(PARSE, "SQLparser", SQLSTATE(45000) "Export operation failed: %s", mvc_export_error(be, c->fdout, res));
    1578           0 :                                         err = 1;
    1579             :                                 }
    1580           0 :                                 if (err) {
    1581           1 :                                         be->q->name = NULL; /* later remove cleanup from mal from qc code */
    1582           1 :                                         qc_delete(m->qc, be->q);
    1583             :                                 }
    1584         334 :                                 be->result_id = be->q->id;
    1585         334 :                                 be->q = NULL;
    1586             :                         }
    1587         334 :                         if (err)
    1588           1 :                                 m->session->status = -10;
    1589         334 :                         sqlcleanup(be, 0);
    1590         334 :                         c->query = NULL;
    1591         334 :                         return msg;
    1592             :                 }
    1593             :         }
    1594      441604 : finalize:
    1595      441604 :         if (m->sa)
    1596      441604 :                 eb_init(&m->sa->eb); /* exiting the scope where the exception buffer can be used */
    1597      441599 :         if (msg) {
    1598       19883 :                 sqlcleanup(be, 0);
    1599       19883 :                 c->query = NULL;
    1600             :         }
    1601             :         return msg;
    1602             : }
    1603             : 
    1604             : static str
    1605      441945 : SQLparser(Client c, backend *be)
    1606             : {
    1607      441945 :         mvc *m = be->mvc;
    1608      441945 :         char *msg;
    1609             : 
    1610      441945 :         assert (be->language != 'X');
    1611             : 
    1612      441945 :         if ((msg = SQLtrans(m)) != MAL_SUCCEED) {
    1613           1 :                 c->mode = FINISHCLIENT;
    1614           1 :                 return msg;
    1615             :         }
    1616             : 
    1617             :         /* sqlparse needs sql allocator to be available.  It can be NULL at
    1618             :          * this point if this is a recursive call. */
    1619      441926 :         if (m->sa == NULL)
    1620       37308 :                 m->sa = sa_create(m->pa);
    1621      441926 :         if (m->sa == NULL) {
    1622           0 :                 c->mode = FINISHCLIENT;
    1623           0 :                 throw(SQL, "SQLparser", SQLSTATE(HY013) MAL_MALLOC_FAIL " for SQL allocator");
    1624             :         }
    1625      441951 :         if (eb_savepoint(&m->sa->eb)) {
    1626           0 :                 msg = createException(SQL, "SQLparser", "%s", m->sa->eb.msg);
    1627           0 :                 eb_init(&m->sa->eb);
    1628           0 :                 sa_reset(m->sa);
    1629           0 :                 if (c && c->curprg && c->curprg->def && c->curprg->def->errors) {
    1630           0 :                         freeException(c->curprg->def->errors);
    1631           0 :                         c->curprg->def->errors = NULL;
    1632             :                 }
    1633           0 :                 sqlcleanup(be, 0);
    1634           0 :                 c->query = NULL;
    1635           0 :                 return msg;
    1636             :         }
    1637      441951 :         return SQLparser_body(c, be);
    1638             : }
    1639             : 
    1640             : str
    1641      523146 : SQLengine_(Client c)
    1642             : {
    1643      523146 :         backend *be = (backend *) c->sqlcontext;
    1644             : 
    1645      523146 :         if (be == 0) {
    1646             :                 /* leave a message in the log */
    1647           0 :                 TRC_ERROR(SQL_PARSER, "SQL state description is missing, cannot handle client!\n");
    1648             :                 /* stop here, instead of printing the exception below to the
    1649             :                  * client in an endless loop */
    1650           0 :                 c->mode = FINISHCLIENT;
    1651           0 :                 throw(SQL, "SQLparser", SQLSTATE(42000) "State descriptor missing, aborting");
    1652             :         }
    1653             : 
    1654      523146 :         str msg = SQLreader(c, be);
    1655      523140 :         if (msg || c->mode <= FINISHCLIENT)
    1656             :                 return msg;
    1657             : 
    1658      485158 :         int oldvtop = c->curprg?c->curprg->def->vtop:0;
    1659      485158 :         if (be->language == 'X') {
    1660       43222 :                 return SQLchannelcmd(c, be);
    1661      441936 :         } else if (be->language !='S') {
    1662           0 :                 msg = createException(SQL, "SQLparser", SQLSTATE(42000) "Unrecognized language prefix: %ci\n", be->language);
    1663           0 :                 c->mode = FINISHCLIENT; /* and disconnect, as client doesn't respect the mapi protocol */
    1664           0 :                 sqlcleanup(be, 0);
    1665           0 :                 c->query = NULL;
    1666             :         } else {
    1667      441936 :                 msg = SQLparser(c, be);
    1668      441947 :                 if (msg == MAL_SUCCEED && (be->mvc->emode == m_deallocate || be->mvc->emode == m_prepare))
    1669             :                         return msg;
    1670             :         }
    1671      289197 :         if (msg || c->mode <= FINISHCLIENT)
    1672       19878 :                 return msg;
    1673             : 
    1674      289197 :         if (c->curprg->def->stop == 1) {
    1675           0 :                 sqlcleanup(be, 0);
    1676           0 :                 return NULL;
    1677             :         }
    1678             : 
    1679      289197 :         mvc *m = be->mvc;
    1680             : 
    1681      289197 :         assert (m->emode != m_deallocate && m->emode != m_prepare);
    1682      289197 :         assert (c->curprg->def->stop > 2);
    1683             : 
    1684      289197 :         msg = SQLrun(c, m);
    1685             : 
    1686      289198 :         if (m->type == Q_SCHEMA && m->qc != NULL)
    1687       16763 :                 qc_clean(m->qc);
    1688      289198 :         be->q = NULL;
    1689      289198 :         if (msg)
    1690       17733 :                 m->session->status = -10;
    1691      289198 :         sqlcleanup(be, (!msg) ? 0 : -1);
    1692      289198 :         MSresetInstructions(c->curprg->def, 1);
    1693      289198 :         freeVariables(c, c->curprg->def, NULL, oldvtop);
    1694             :         /*
    1695             :          * Any error encountered during execution should block further processing
    1696             :          * unless auto_commit has been set.
    1697             :          */
    1698      289198 :         return msg;
    1699             : }
    1700             : 
    1701             : void
    1702      523148 : SQLengine(Client c)
    1703             : {
    1704      523148 :         char *msg = SQLengine_(c);
    1705      523157 :         if (msg) {
    1706             :                 /* remove exception decoration */
    1707       77327 :                 for (char *m = msg; m && *m; ) {
    1708       38781 :                         char *n = strchr(m, '\n');
    1709       38781 :                         char *s = getExceptionMessageAndState(m);
    1710       38780 :                         mnstr_printf(c->fdout, "!%.*s\n", (int) (n - s), s);
    1711       38776 :                         m = n;
    1712       38776 :                         if (n) {
    1713       38776 :                                 m++; /* include newline */
    1714             :                         }
    1715             :                 }
    1716       38546 :                 freeException(msg);
    1717             :         }
    1718      523157 : }
    1719             : 
    1720             : str
    1721           0 : SYSupdate_tables(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1722             : {
    1723           0 :         mvc *m = ((backend *) cntxt->sqlcontext)->mvc;
    1724             : 
    1725           0 :         (void) mb;
    1726           0 :         (void) stk;
    1727           0 :         (void) pci;
    1728             : 
    1729           0 :         sql_trans_update_tables(m->session->tr, mvc_bind_schema(m, "sys"));
    1730           0 :         return MAL_SUCCEED;
    1731             : }
    1732             : 
    1733             : str
    1734         229 : SYSupdate_schemas(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1735             : {
    1736         229 :         mvc *m = ((backend *) cntxt->sqlcontext)->mvc;
    1737             : 
    1738         229 :         (void) mb;
    1739         229 :         (void) stk;
    1740         229 :         (void) pci;
    1741             : 
    1742         229 :         if (sql_trans_update_schemas(m->session->tr) < 0)
    1743           0 :                 throw(MAL, "sql.update_schemas", MAL_MALLOC_FAIL);
    1744             :         return MAL_SUCCEED;
    1745             : }

Generated by: LCOV version 1.14