LCOV - code coverage report
Current view: top level - sql/backends/monet5 - sql_scenario.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 705 1047 67.3 %
Date: 2024-12-20 21:24:02 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 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       25078 : sql_module_compare(const void *a, const void *b)
      67             : {
      68       25078 :         const struct sql_module *l = a, *r = b;
      69       25078 :         return strcmp(l->name, r->name);
      70             : }
      71             : 
      72             : void
      73       13332 : sql_register(const char *name, const unsigned char *code)
      74             : {
      75       13332 :         assert (sql_modules < MAX_SQL_MODULES);
      76       13332 :         sql_module[sql_modules].name = name;
      77       13332 :         sql_module[sql_modules].code = code;
      78       13332 :         sql_modules++;
      79       13332 : }
      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         116 : CLIENTprintinfo(void)
      94             : {
      95         116 :         int nrun = 0, nfinish = 0, nblock = 0;
      96         116 :         char mmbuf[64];
      97         116 :         char tmbuf[64];
      98         116 :         char trbuf[64];
      99         116 :         char chbuf[64];
     100         116 :         char cabuf[64];
     101         116 :         char clbuf[64];
     102         116 :         char crbuf[64];
     103         116 :         char cpbuf[64];
     104         116 :         struct tm tm;
     105             : 
     106         116 :         if (!MT_lock_trytime(&mal_contextLock, 1000)) {
     107           0 :                 printf("Clients are currently locked, so no client information\n");
     108           0 :                 return;
     109             :         }
     110         116 :         printf("Clients:\n");
     111        9464 :         for (Client c = mal_clients; c < mal_clients + MAL_MAXCLIENTS; c++) {
     112        9348 :                 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         116 :         MT_lock_unset(&mal_contextLock);
     167         116 :         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         116 : SQLprintinfo(void)
     173             : {
     174         116 :         CLIENTprintinfo();
     175         116 :         store_printinfo(SQLstore);
     176         116 : }
     177             : 
     178             : str
     179             : //SQLprelude(void *ret)
     180         352 : SQLprelude(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
     181             : {
     182         352 :         str tmp;
     183         352 :         Scenario ms, s = getFreeScenario();
     184             :         /* HACK ALERT: temporarily use sqlcontext to pass the initial
     185             :          * password to the prelude function */
     186         352 :         const char *initpasswd = cntxt->sqlcontext;
     187         352 :         cntxt->sqlcontext = NULL;
     188             :         /* HACK ALERT: use mb (MalBlkPtr) to pass revision string in order
     189             :          * to check that in the callee */
     190         352 :         if (mb) {
     191         352 :                 const char *caller_revision = (const char *) (void *) mb;
     192         352 :                 const char *p = mercurial_revision();
     193         352 :                 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         352 :         (void) stk;
     199         352 :         (void) pci;
     200         352 :         if (!s)
     201           0 :                 throw(MAL, "sql.start", SQLSTATE(42000) "out of scenario slots");
     202         352 :         sqlinit = GDKgetenv("sqlinit");
     203         352 :         *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         352 :         ms = getFreeScenario();
     214         352 :         if (!ms)
     215           0 :                 throw(MAL, "sql.start", SQLSTATE(42000) "out of scenario slots");
     216             : 
     217         352 :         *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         352 :         tmp = SQLinit(cntxt, initpasswd);
     229         352 :         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         351 :         if (!GDKembedded()) {
     242         339 :                 fprintf(stdout, "# MonetDB/SQL module loaded\n");
     243         339 :                 fflush(stdout);         /* make merovingian see this *now* */
     244             :         }
     245         351 :         GDKprintinforegister(SQLprintinfo);
     246         351 :         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         339 :         s->name = "sql";
     253         339 :         tmp = msab_marchScenario(s->name);
     254         339 :         if (tmp != NULL) {
     255           0 :                 char *err = createException(MAL, "sql.start", "%s", tmp);
     256           0 :                 free(tmp);
     257           0 :                 return err;
     258             :         }
     259         339 :         ms->name = "msql";
     260         339 :         tmp = msab_marchScenario(ms->name);
     261         339 :         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         350 : SQLexit(Client c)
     271             : {
     272         350 :         (void) c;               /* not used */
     273         350 :         MT_lock_set(&sql_contextLock);
     274         350 :         if (SQLstore) {
     275         350 :                 mvc_exit(SQLstore);
     276         350 :                 SQLstore = NULL;
     277             :         }
     278         350 :         MT_lock_unset(&sql_contextLock);
     279         350 :         return MAL_SUCCEED;
     280             : }
     281             : 
     282             : str
     283         350 : SQLepilogue(void *ret)
     284             : {
     285         350 :         const char s[] = "sql", m[] = "msql";
     286         350 :         char *msg;
     287             : 
     288         350 :         (void) ret;
     289         350 :         msg = SQLexit(NULL);
     290         350 :         freeException(msg);
     291             :         /* this function is never called, but for the style of it, we clean
     292             :          * up our own mess */
     293         350 :         if (!GDKinmemory(0) && !GDKembedded()) {
     294         339 :                 str res = msab_retreatScenario(m);
     295         339 :                 if (!res)
     296         339 :                         res = msab_retreatScenario(s);
     297         339 :                 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         350 :         Scenario sc = findScenario(s);
     305         350 :         if (sc)
     306         350 :                 sc->name = NULL;
     307         350 :         sc = findScenario(m);
     308         350 :         if (sc)
     309         350 :                 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       37201 : userCheckCredentials( mvc *m, Client c, const char *pwhash, const char *challenge, const char *algo)
     391             : {
     392       37201 :         oid uid = getUserOIDByName(m, c->username);
     393             : 
     394       37194 :         if (strNil(pwhash))
     395           0 :                 throw(INVCRED, "checkCredentials", INVCRED_INVALID_USER " '%s'", c->username);
     396       37194 :         str passValue = getUserPassword(m, uid);
     397       37200 :         if (strNil(passValue))
     398           6 :                 throw(INVCRED, "checkCredentials", INVCRED_INVALID_USER " '%s'", c->username);
     399             :             /* find the corresponding password to the user */
     400             : 
     401       37194 :         str pwd = NULL;
     402       37194 :         str msg = AUTHdecypherValue(&pwd, passValue);
     403       37194 :         GDKfree(passValue);
     404       37195 :         if (msg)
     405             :                 return msg;
     406             : 
     407             :         /* generate the hash as the client should have done */
     408       37193 :         str hash = mcrypt_hashPassword(algo, pwd, challenge);
     409       37190 :         GDKfree(pwd);
     410       37191 :         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       37191 :         if (strcmp(pwhash, hash) == 0) {
     415       37188 :                 free(hash);
     416       37188 :                 c->user = uid;
     417       37188 :                 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       37584 : SQLprepareClient(Client c, const char *pwhash, const char *challenge, const char *algo)
     446             : {
     447       37584 :         mvc *m = NULL;
     448       37584 :         backend *be = NULL;
     449       37584 :         str msg = MAL_SUCCEED;
     450             : 
     451       37584 :         if (c->sqlcontext == 0) {
     452       37584 :                 allocator *sa = sa_create(NULL);
     453       37584 :                 if (sa == NULL) {
     454           0 :                         msg = createException(SQL,"sql.initClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     455           0 :                         goto bailout2;
     456             :                 }
     457       37584 :                 m = mvc_create(SQLstore, sa, c->idx, SQLdebug, c->fdin, c->fdout);
     458       37584 :                 if (m == NULL) {
     459           0 :                         msg = createException(SQL,"sql.initClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     460           0 :                         goto bailout2;
     461             :                 }
     462       37584 :                 if (c->scenario && strcmp(c->scenario, "msql") == 0)
     463         186 :                         m->reply_size = -1;
     464       37584 :                 be = (void *) backend_create(m, c);
     465       37584 :                 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       37584 :         MT_lock_unset(&sql_contextLock);
     474       37584 :         if (c->username && pwhash) {
     475             : 
     476       37201 :                 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       37201 :                 msg = userCheckCredentials( m, c, pwhash, challenge, algo);
     483       37199 :                 if (msg)
     484           9 :                         goto bailout1;
     485       37190 :                 if (!GDKinmemory(0) && !GDKembedded()) {
     486       37185 :                         sabdb *stats = NULL;
     487       37185 :                         bool locked = false;
     488       37185 :                         char *err = msab_getMyStatus(&stats);
     489       37185 :                         if (err || stats == NULL)
     490           0 :                                 free(err);
     491             :                         else
     492       37185 :                                 locked = stats->locked;
     493       37185 :                         msab_freeStatus(&stats);
     494       37178 :                         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       37176 :                 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       37190 :                                 break;
     520             :                 }
     521       37190 :                 if (monet5_user_get_limits(m, m->user_id, &c->maxmem, &c->maxworkers) == 0) {
     522       37191 :                         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       37191 :                 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       37191 :                 mvc_rollback(m, 0, NULL, false);
     531             :         }
     532             : 
     533       37575 :         if (c->handshake_options) {
     534       37191 :                 char *strtok_state = NULL;
     535       37191 :                 char *tok = strtok_r(c->handshake_options, ",", &strtok_state);
     536      187347 :                 while (tok != NULL) {
     537      150160 :                         int value;
     538      150160 :                         if (sscanf(tok, "auto_commit=%d", &value) == 1) {
     539       37189 :                                 bool auto_commit= value != 0;
     540       37189 :                                 m->session->auto_commit = auto_commit;
     541       37189 :                                 m->session->ac_on_commit = auto_commit;
     542      112971 :                         } else if (sscanf(tok, "reply_size=%d", &value) == 1) {
     543       37188 :                                 if (value < -1) {
     544           0 :                                         msg = createException(SQL, "SQLprepareClient", SQLSTATE(42000) "Reply_size cannot be negative");
     545           0 :                                         goto bailout1;
     546             :                                 }
     547       37188 :                                 m->reply_size = value;
     548       75783 :                         } else if (sscanf(tok, "size_header=%d", &value) == 1) {
     549       37189 :                                 be->sizeheader = value != 0;
     550       38594 :                         } else if (sscanf(tok, "columnar_protocol=%d", &value) == 1) {
     551        2814 :                                 c->protocol = (value != 0) ? PROTOCOL_COLUMNAR : PROTOCOL_9;
     552       37187 :                         } else if (sscanf(tok, "time_zone=%d", &value) == 1) {
     553       37187 :                                 sql_schema *s = mvc_bind_schema(m, "sys");
     554       37188 :                                 sql_var *var = find_global_var(m, s, "current_timezone");
     555       37190 :                                 ValRecord val;
     556       37190 :                                 VALinit(&val, TYPE_lng, &(lng){1000 * value});
     557       37183 :                                 if ((msg = sql_update_var(m, s, "current_timezone", &val)))
     558           0 :                                         goto bailout1;
     559       37182 :                                 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      150159 :                         tok = strtok_r(NULL, ",", &strtok_state);
     566             :                 }
     567             :         }
     568             : 
     569             : 
     570         384 : bailout1:
     571       37579 :         if (m->session->tr->active)
     572           9 :                 mvc_rollback(m, 0, NULL, false);
     573       37579 :         MT_lock_set(&sql_contextLock);
     574       37584 : bailout2:
     575             :         /* expect SQL text first */
     576       37584 :         if (be)
     577       37584 :                 be->language = 'S';
     578             :         /* Set state, this indicates an initialized client scenario */
     579       37584 :         c->sqlcontext = be;
     580       37584 :         if (msg)
     581           9 :                 c->mode = FINISHCLIENT;
     582             :         return msg;
     583             : }
     584             : 
     585             : str
     586       37584 : SQLresetClient(Client c)
     587             : {
     588       37584 :         str msg = MAL_SUCCEED, other = MAL_SUCCEED;
     589             : 
     590       37584 :         if (c->sqlcontext == NULL)
     591           0 :                 throw(SQL, "SQLexitClient", SQLSTATE(42000) "MVC catalogue not available");
     592       37584 :         if (c->sqlcontext) {
     593       37584 :                 allocator *pa = NULL;
     594       37584 :                 backend *be = c->sqlcontext;
     595       37584 :                 mvc *m = be->mvc;
     596             : 
     597       37584 :                 assert(m->session);
     598       37584 :                 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       37584 :                 if (m->session->tr->active)
     603        1028 :                         other = mvc_rollback(m, 0, NULL, false);
     604             : 
     605       37584 :                 res_tables_destroy(be->results);
     606       37584 :                 be->results = NULL;
     607             : 
     608       37584 :                 pa = m->pa;
     609       37584 :                 mvc_destroy(m);
     610       37584 :                 backend_destroy(be);
     611       37584 :                 c->sqlcontext = NULL;
     612       37584 :                 c->query = NULL;
     613       37584 :                 sa_destroy(pa);
     614             :         }
     615       37584 :         if (other && !msg)
     616             :                 msg = other;
     617       37584 :         else if (other && msg)
     618           0 :                 freeException(other);
     619             :         return msg;
     620             : }
     621             : 
     622             : MT_Id sqllogthread;
     623             : 
     624             : static str
     625         352 : SQLinit(Client c, const char *initpasswd)
     626             : {
     627         352 :         const char *debug_str = GDKgetenv("sql_debug");
     628         352 :         char *msg = MAL_SUCCEED, *other = MAL_SUCCEED;
     629         352 :         bool readonly = GDKgetenv_isyes("gdk_readonly");
     630         352 :         bool single_user = GDKgetenv_isyes("gdk_single_user");
     631         352 :         static int maybeupgrade = 1;
     632         352 :         backend *be = NULL;
     633         352 :         mvc *m = NULL;
     634         352 :         const char *opt_pipe;
     635             : 
     636         352 :         master_password = NULL;
     637         352 :         if (!GDKembedded() && !GDKinmemory(0)) {
     638         340 :                 msg = msab_pickSecret(&master_password);
     639         340 :                 if (msg)
     640             :                         return msg;
     641             :         }
     642             : 
     643         352 :         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         352 :         MT_lock_set(&sql_contextLock);
     647             : 
     648         352 :         if (SQLstore) {
     649           0 :                 MT_lock_unset(&sql_contextLock);
     650           0 :                 return MAL_SUCCEED;
     651             :         }
     652             : 
     653         352 :         be_funcs.fcode = &monet5_freecode,
     654         352 :         be_funcs.fresolve_function = &monet5_resolve_function,
     655         352 :         be_funcs.fhas_module_function = &monet5_has_module,
     656         352 :         monet5_user_init(&be_funcs);
     657             : 
     658         352 :         if (debug_str)
     659         352 :                 SQLdebug = strtol(debug_str, NULL, 10);
     660         352 :         if (SQLdebug & 1)
     661           0 :                 GDKtracer_set_component_level("wal", "debug");
     662         352 :         if (single_user)
     663           0 :                 SQLdebug |= 64;
     664         352 :         if (readonly)
     665           4 :                 SQLdebug |= 32;
     666             : 
     667         703 :         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         351 :         sqlinit = GDKgetenv("sqlinit");
     672         351 :         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         351 :         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         351 :         be = c->sqlcontext;
     719         351 :         m = be->mvc;
     720             :         /* initialize the database with predefined SQL functions */
     721         351 :         sqlstore *store = SQLstore;
     722         351 :         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         125 :                 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         125 :                         if (!mvc_bind_schema(m, "json"))
     730           0 :                                 store->first = 1;
     731         125 :                         msg = mvc_rollback(m, 0, NULL, false);
     732             :                 }
     733         125 :                 if (msg) {
     734           0 :                         freeException(msg);
     735           0 :                         msg = MAL_SUCCEED;
     736             :                 }
     737             :         }
     738         351 :         if (store->first > 0) {
     739         226 :                 store->first = 0;
     740         226 :                 maybeupgrade = 0;
     741             : 
     742         226 :                 qsort(sql_module, sql_modules, sizeof(sql_module[0]), sql_module_compare);
     743        9034 :                 for (int i = 0; i < sql_modules && !msg; i++) {
     744        8808 :                         const char *createdb_inline = (const char*)sql_module[i].code;
     745             : 
     746        8808 :                         msg = SQLstatementIntern(c, createdb_inline, "sql.init", TRUE, FALSE, NULL);
     747        8808 :                         if (m->sa)
     748           0 :                                 sa_destroy(m->sa);
     749        8808 :                         m->sa = NULL;
     750             :                 }
     751             :                 /* 99_system.sql */
     752         226 :                 if (!msg) {
     753         226 :                         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         226 :                         msg = SQLstatementIntern(c, createdb_inline, "sql.init", TRUE, FALSE, NULL);
     769         226 :                         if (m->sa)
     770           0 :                                 sa_destroy(m->sa);
     771         226 :                         m->sa = NULL;
     772             :                 }
     773             :                 /* Commit after all the startup scripts have been processed */
     774         226 :                 assert(m->session->tr->active);
     775         226 :                 if (mvc_status(m) < 0 || msg)
     776           0 :                         other = mvc_rollback(m, 0, NULL, false);
     777             :                 else
     778         226 :                         other = mvc_commit(m, 0, NULL, false);
     779             : 
     780         226 :                 if (other && !msg) /* 'msg' variable might be set or not, as well as 'other'. Throw the earliest one */
     781             :                         msg = other;
     782         226 :                 else if (other)
     783           0 :                         freeException(other);
     784         226 :                 if (msg)
     785           0 :                         TRC_INFO(SQL_PARSER, "%s\n", msg);
     786             :         } else {                /* handle upgrades */
     787         125 :                 if (!m->sa)
     788         125 :                         m->sa = sa_create(m->pa);
     789         125 :                 if (!m->sa) {
     790           0 :                         msg = createException(MAL, "createdb", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     791         125 :                 } else if (maybeupgrade) {
     792         115 :                         if ((msg = SQLtrans(m)) == MAL_SUCCEED) {
     793         115 :                                 int res = SQLupgrades(c, m);
     794             :                                 /* Commit at the end of the upgrade */
     795         115 :                                 assert(m->session->tr->active);
     796         115 :                                 if (mvc_status(m) < 0 || res)
     797           0 :                                         msg = mvc_rollback(m, 0, NULL, false);
     798             :                                 else
     799         115 :                                         msg = mvc_commit(m, 0, NULL, false);
     800             :                         }
     801             :                 }
     802         125 :                 maybeupgrade = 0;
     803             :         }
     804         351 :         fflush(stdout);
     805         351 :         fflush(stderr);
     806             : 
     807             :         /* send error from create scripts back to the first client */
     808         351 :         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         351 :         other = SQLresetClient(c);
     815         351 :         if (other && !msg) /* 'msg' variable might be set or not, as well as 'other'. Throw the earliest one */
     816             :                 msg = other;
     817         351 :         else if (other)
     818           0 :                 freeException(other);
     819         351 :         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         351 :         if (GDKinmemory(0)) {
     827           1 :                 MT_lock_unset(&sql_contextLock);
     828           1 :                 return msg;
     829             :         }
     830             : 
     831         350 :         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         350 :         MT_lock_unset(&sql_contextLock);
     839         350 :         return MAL_SUCCEED;
     840             : }
     841             : 
     842             : #define TRANS_ABORTED SQLSTATE(25005) "Current transaction is aborted (please ROLLBACK)\n"
     843             : 
     844             : str
     845       18673 : handle_error(mvc *m, int pstatus, str msg)
     846             : {
     847       18673 :         str new = NULL, newmsg = MAL_SUCCEED;
     848             : 
     849             :         /* transaction already broken */
     850       18673 :         if (m->type != Q_TRANS && pstatus < 0) {
     851       16340 :                 freeException(msg);
     852       16323 :                 return createException(SQL,"sql.execute",TRANS_ABORTED);
     853        2333 :         } else if ( GDKerrbuf && GDKerrbuf[0]){
     854           0 :                 new = GDKstrdup(GDKerrbuf);
     855           0 :                 GDKerrbuf[0] = 0;
     856        2340 :         } else if ( *m->errstr){
     857           8 :                 new = GDKstrdup(m->errstr);
     858           8 :                 m->errstr[0] = 0;
     859             :         }
     860        2340 :         if ( new && msg){
     861           0 :                 newmsg = concatErrors(msg, new);
     862           0 :                 GDKfree(new);
     863        2340 :         } 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      305121 : SQLautocommit(mvc *m)
     876             : {
     877      305121 :         str msg = MAL_SUCCEED;
     878             : 
     879      305121 :         if (m->session->auto_commit && m->session->tr->active) {
     880      178000 :                 if (mvc_status(m) < 0) {
     881       20462 :                         msg = mvc_rollback(m, 0, NULL, false);
     882             :                 } else {
     883      157536 :                         msg = mvc_commit(m, 0, NULL, false);
     884             :                 }
     885             :         }
     886      305125 :         return msg;
     887             : }
     888             : 
     889             : str
     890      420550 : SQLtrans(mvc *m)
     891             : {
     892      420550 :         if (!m->session->tr->active) {
     893      181008 :                 sql_session *s;
     894             : 
     895      181008 :                 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      181072 :                                 break;
     902             :                 }
     903      181072 :                 s = m->session;
     904      181072 :                 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       37223 : SQLinitClient(Client c, const char *passwd, const char *challenge, const char *algo)
     930             : {
     931       37223 :         str msg = MAL_SUCCEED;
     932             : 
     933       37223 :         MT_lock_set(&sql_contextLock);
     934       37233 :         if (!SQLstore) {
     935           0 :                 MT_lock_unset(&sql_contextLock);
     936           0 :                 throw(SQL, "SQLinitClient", SQLSTATE(42000) "Catalogue not available");
     937             :         }
     938       37233 :         if ((msg = SQLprepareClient(c, passwd, challenge, algo)) == MAL_SUCCEED) {
     939       37224 :                 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       37233 :         MT_lock_unset(&sql_contextLock);
     945       37233 :         return msg;
     946             : }
     947             : 
     948             : str
     949         186 : SQLinitClientFromMAL(Client c, const char *passwd, const char *challenge, const char *algo)
     950             : {
     951         186 :         str msg = MAL_SUCCEED;
     952             : 
     953         186 :         if ((msg = SQLinitClient(c, passwd, challenge, algo)) != MAL_SUCCEED) {
     954           0 :                 c->mode = FINISHCLIENT;
     955           0 :                 return msg;
     956             :         }
     957             : 
     958         186 :         mvc* m = ((backend*) c->sqlcontext)->mvc;
     959         186 :         if (c->glb)
     960         186 :                 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         186 :         if ((msg = SQLtrans(m)) != MAL_SUCCEED) {
     967           0 :                 c->mode = FINISHCLIENT;
     968           0 :                 return msg;
     969             :         }
     970             :         return msg;
     971             : }
     972             : 
     973             : str
     974       37217 : SQLexitClient(Client c)
     975             : {
     976       37217 :         str err;
     977             : 
     978       37217 :         MT_lock_set(&sql_contextLock);
     979       37224 :         if (!SQLstore) {
     980           0 :                 MT_lock_unset(&sql_contextLock);
     981           0 :                 throw(SQL, "SQLexitClient", SQLSTATE(42000) "Catalogue not available");
     982             :         }
     983       37224 :         err = SQLresetClient(c);
     984       37224 :         MT_lock_unset(&sql_contextLock);
     985       37224 :         if (err != MAL_SUCCEED)
     986             :                 return err;
     987       37224 :         err = MALexitClient(c);
     988       37224 :         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      489190 : SQLreader(Client c, backend *be)
    1076             : {
    1077      489190 :         bool go = true;
    1078      489190 :         str msg = MAL_SUCCEED;
    1079      489190 :         bool more = true;
    1080      489190 :         bool commit_done = false;
    1081      489190 :         bstream *in = c->fdin;
    1082      489190 :         int language = -1;
    1083      489190 :         mvc *m = NULL;
    1084      489190 :         bool blocked = isa_block_stream(in->s);
    1085             : 
    1086      489458 :         if (!SQLstore || c->mode <= FINISHCLIENT) {
    1087           0 :                 c->mode = FINISHCLIENT;
    1088           0 :                 return MAL_SUCCEED;
    1089             :         }
    1090      489458 :         language = be->language;     /* 'S', 's' or 'X' */
    1091      489458 :         m = be->mvc;
    1092      489458 :         m->errstr[0] = 0;
    1093             :         /*
    1094             :          * Continue processing any left-over input from the previous round.
    1095             :          */
    1096             : 
    1097      990208 :         while (more) {
    1098      538245 :                 more = false;
    1099             : 
    1100             :                 /* Different kinds of supported statements sequences
    1101             :                    A;   -- single line                  s
    1102             :                    A \n B;      -- multi line                   S
    1103             :                    A; B;   -- compound single block     s
    1104             :                    A;   -- many multi line
    1105             :                    B \n C; -- statements in one block   S
    1106             :                  */
    1107             :                 /* auto_commit on end of statement */
    1108      538245 :                 if (m->scanner.mode == LINE_N && !commit_done) {
    1109       79810 :                         msg = SQLautocommit(m);
    1110       79805 :                         if (msg)
    1111             :                                 break;
    1112             :                         commit_done = true;
    1113             :                 }
    1114      538240 :                 if (m->session->tr && m->session->tr->active) {
    1115      404847 :                         MT_lock_set(&mal_contextLock);
    1116      405232 :                         c->idle = 0;
    1117      405232 :                         MT_lock_unset(&mal_contextLock);
    1118             :                 }
    1119             : 
    1120      538799 :                 if (go && in->pos >= in->len) {
    1121      353465 :                         ssize_t rd;
    1122             : 
    1123      353465 :                         if (c->bak) {
    1124           0 :                                 in = c->fdin;
    1125           0 :                                 blocked = isa_block_stream(in->s);
    1126           0 :                                 m->scanner.rs = c->fdin;
    1127           0 :                                 c->fdin->pos += c->yycur;
    1128           0 :                                 c->yycur = 0;
    1129             :                         }
    1130      353465 :                         if (in->eof || !blocked) {
    1131      305007 :                                 language = 0;
    1132             : 
    1133             :                                 /* The rules of auto_commit require us to finish
    1134             :                                    and start a transaction on the start of a new statement (s A;B; case) */
    1135      305007 :                                 if (!commit_done) {
    1136      225073 :                                         msg = SQLautocommit(m);
    1137      225073 :                                         if (msg)
    1138             :                                                 break;
    1139      224071 :                                         commit_done = true;
    1140      224071 :                                         MT_lock_set(&mal_contextLock);
    1141      224071 :                                         if (c->idle == 0 && (m->session->tr == NULL || !m->session->tr->active)) {
    1142             :                                                 /* now the session is idle */
    1143      173372 :                                                 c->idle = time(0);
    1144             :                                         }
    1145      224071 :                                         MT_lock_unset(&mal_contextLock);
    1146             :                                 }
    1147             : 
    1148      304005 :                                 if (go && ((!blocked && mnstr_write(c->fdout, c->prompt, c->promptlength, 1) != 1) || mnstr_flush(c->fdout, MNSTR_FLUSH_DATA))) {
    1149             :                                         go = false;
    1150             :                                         break;
    1151             :                                 }
    1152      303682 :                                 in->eof = false;
    1153             :                         }
    1154      352140 :                         while (bstream_getoob(in) > 0)
    1155             :                                 ;
    1156      352252 :                         m->scanner.aborted = false;
    1157      352252 :                         if (in->buf == NULL) {
    1158             :                                 more = false;
    1159             :                                 go = false;
    1160      352252 :                         } else if (go && (rd = bstream_next(in)) <= 0) {
    1161       85604 :                                 if (rd == 0 && in->eof && !mnstr_eof(in->s)) {
    1162             :                                         /* we hadn't seen the EOF before, so just try again
    1163             :                                            (this time with prompt) */
    1164       48618 :                                         more = true;
    1165       48618 :                                         continue;
    1166             :                                 }
    1167             :                                 go = false;
    1168             :                                 break;
    1169      266755 :                         } else if (go && language == 0) {
    1170      266743 :                                 if (in->buf[in->pos] == 's' && !in->eof) {
    1171      218341 :                                         while ((rd = bstream_next(in)) > 0)
    1172             :                                                 ;
    1173             :                                 }
    1174      266786 :                                 be->language = in->buf[in->pos++];
    1175      266786 :                                 if (be->language == 's') {
    1176      218252 :                                         be->language = 'S';
    1177      218252 :                                         m->scanner.mode = LINE_1;
    1178       48534 :                                 } else if (be->language == 'S') {
    1179        5519 :                                         m->scanner.mode = LINE_N;
    1180             :                                 }
    1181             :                         }
    1182             :                 }
    1183             :         }
    1184      489955 :         if ( (c->sessiontimeout && (GDKusec() - c->session) > c->sessiontimeout) || !go || (strncmp(CURRENT(c), "\\q", 2) == 0)) {
    1185       37002 :                 in->pos = in->len;        /* skip rest of the input */
    1186       37002 :                 c->mode = FINISHCLIENT;
    1187       37002 :                 return msg;
    1188             :         }
    1189             :         return msg;
    1190             : }
    1191             : 
    1192             : static str
    1193       43012 : SQLchannelcmd(Client c, backend *be)
    1194             : {
    1195       43012 :         assert(be->language == 'X');
    1196             : 
    1197       43012 :         bstream *in = c->fdin;
    1198       43012 :         stream *out = c->fdout;
    1199       43012 :         mvc *m = be->mvc;
    1200       43012 :         str msg = MAL_SUCCEED;
    1201       43012 :         int n = 0, v, off, len, ok;
    1202             : 
    1203       43012 :         if (strncmp(in->buf + in->pos, "export ", 7) == 0)
    1204          77 :                 n = sscanf(in->buf + in->pos + 7, "%d %d %d", &v, &off, &len);
    1205             : 
    1206       43012 :         if (n == 2 || n == 3) {
    1207          77 :                 if (n == 2)
    1208          27 :                         len = m->reply_size;
    1209          77 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1210          77 :                 if ((ok = mvc_export_chunk(be, out, v, off, len < 0 ? BUN_NONE : (BUN) len)) < 0) {
    1211           0 :                         sqlcleanup(be, 0);
    1212           0 :                         return createException(SQL, "SQLparser", SQLSTATE(45000) "Result set construction failed: %s", mvc_export_error(be, out, ok));
    1213             :                 }
    1214             :                 return MAL_SUCCEED;
    1215             :         }
    1216       42935 :         if (strncmp(in->buf + in->pos, "exportbin ", 10) == 0) {
    1217         600 :                 n = sscanf(in->buf + in->pos + 10, "%d %d %d", &v, &off, &len);
    1218         600 :                 if (n == 3) {
    1219         600 :                         if ((ok = mvc_export_bin_chunk(be, out, v, off, len < 0 ? BUN_NONE: (BUN) len)) < 0) {
    1220           0 :                                 msg = createException(SQL, "SQLparser", SQLSTATE(45000) "Result set construction failed: %s", mvc_export_error(be, out, ok));
    1221           0 :                                 in->pos = in->len;        /* HACK: should use parsed length */
    1222           0 :                                 sqlcleanup(be, 0);
    1223           0 :                                 return msg;
    1224             :                         }
    1225         600 :                         in->pos = in->len;        /* HACK: should use parsed length */
    1226         600 :                         return MAL_SUCCEED;
    1227             :                 }
    1228             :         }
    1229       42335 :         if (strncmp(in->buf + in->pos, "close ", 6) == 0) {
    1230        4484 :                 res_table *t;
    1231             : 
    1232        4484 :                 v = (int) strtol(in->buf + in->pos + 6, NULL, 0);
    1233        4484 :                 t = res_tables_find(be->results, v);
    1234        4484 :                 if (t)
    1235         493 :                         be->results = res_tables_remove(be->results, t);
    1236        4484 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1237        4484 :                 return MAL_SUCCEED;
    1238             :         }
    1239       37851 :         if (strncmp(in->buf + in->pos, "release ", 8) == 0) {
    1240         211 :                 cq *q = NULL;
    1241             : 
    1242         211 :                 v = (int) strtol(in->buf + in->pos + 8, NULL, 0);
    1243         211 :                 if ((q = qc_find(m->qc, v)) != NULL)
    1244         204 :                          qc_delete(m->qc, q);
    1245         211 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1246         211 :                 return MAL_SUCCEED;
    1247             :         }
    1248       37640 :         if (strncmp(in->buf + in->pos, "auto_commit ", 12) == 0) {
    1249         173 :                 int commit;
    1250         173 :                 v = (int) strtol(in->buf + in->pos + 12, NULL, 10);
    1251         173 :                 commit = (!m->session->auto_commit && v);
    1252         173 :                 m->session->auto_commit = (v) != 0;
    1253         173 :                 m->session->ac_on_commit = m->session->auto_commit;
    1254         173 :                 if (m->session->tr->active) {
    1255           1 :                         if (commit) {
    1256           1 :                                 msg = mvc_commit(m, 0, NULL, true);
    1257             :                         } else {
    1258           0 :                                 msg = mvc_rollback(m, 0, NULL, true);
    1259             :                         }
    1260             :                 }
    1261         173 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1262         173 :                 if (msg != NULL)
    1263           0 :                         sqlcleanup(be, 0);
    1264         173 :                 return msg;
    1265             :         }
    1266       37467 :         static const char columnar_protocol[] = "columnar_protocol ";
    1267       37467 :         if (strncmp(in->buf + in->pos, columnar_protocol, strlen(columnar_protocol)) == 0) {
    1268           0 :                 v = (int) strtol(in->buf + in->pos + strlen(columnar_protocol), NULL, 10);
    1269             : 
    1270           0 :                 c->protocol = v?PROTOCOL_COLUMNAR:PROTOCOL_9;
    1271             : 
    1272           0 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1273           0 :                 return MAL_SUCCEED;
    1274             :         }
    1275       37467 :         if (strncmp(in->buf + in->pos, "reply_size ", 11) == 0) {
    1276         472 :                 v = (int) strtol(in->buf + in->pos + 11, NULL, 10);
    1277         472 :                 if (v < -1) {
    1278           0 :                         sqlcleanup(be, 0);
    1279           0 :                         return createException(SQL, "SQLparser", SQLSTATE(42000) "Reply_size cannot be negative");
    1280             :                 }
    1281         472 :                 m->reply_size = v;
    1282         472 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1283         472 :                 return MAL_SUCCEED;
    1284             :         }
    1285       36995 :         if (strncmp(in->buf + in->pos, "sizeheader", 10) == 0) { // no underscore
    1286           0 :                 v = (int) strtol(in->buf + in->pos + 10, NULL, 10);
    1287           0 :                 be->sizeheader = v != 0;
    1288           0 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1289           0 :                 return MAL_SUCCEED;
    1290             :         }
    1291       36995 :         if (strncmp(in->buf + in->pos, "clientinfo ", 11) == 0) {
    1292       36995 :                 in->pos += 11;
    1293       36995 :                 char *end = in->buf + in->len;
    1294       36995 :                 char *key = in->buf + in->pos;
    1295      220740 :                 while (key < end) {
    1296      183743 :                         char *p = memchr(key, '\n', end - key);
    1297      183743 :                         if (!p)
    1298           0 :                                 return createException(SQL, "SQLparser", SQLSTATE(42000) "no trailing newline in clientinfo");
    1299      183743 :                         *p = '\0';
    1300      183743 :                         char *q = memchr(key, '=', p - key);
    1301      183743 :                         if (!q)
    1302           0 :                                 return createException(SQL, "SQLparser", SQLSTATE(42000) "found no = in clientinfo");
    1303      183743 :                         *q = '\0';
    1304      183743 :                         char *value = q + 1;
    1305      219526 :                         MCsetClientInfo(c, key, *value ? value : NULL);
    1306      183745 :                         key = p + 1;
    1307             :                 }
    1308       36997 :                 in->pos = in->len;
    1309       36997 :                 return MAL_SUCCEED;
    1310             :         }
    1311           0 :         if (strncmp(in->buf + in->pos, "quit", 4) == 0) {
    1312           0 :                 c->mode = FINISHCLIENT;
    1313           0 :                 in->pos = in->len;        /* HACK: should use parsed length */
    1314           0 :                 return MAL_SUCCEED;
    1315             :         }
    1316           0 :         in->pos = in->len;        /* HACK: should use parsed length */
    1317           0 :         msg = createException(SQL, "SQLparser", SQLSTATE(42000) "Unrecognized X command: %s\n", in->buf + in->pos);
    1318           0 :         sqlcleanup(be, 0);
    1319           0 :         return msg;
    1320             : }
    1321             : 
    1322             : /*
    1323             :  * The SQL block is stored in the client input buffer, from which it
    1324             :  * can be parsed by the SQL parser. The client structure contains
    1325             :  * a small table of bounded tables. This should be reset before we
    1326             :  * parse a new statement sequence.
    1327             :  * Before we parse the sql statement, we look for any variable settings
    1328             :  * for specific commands.
    1329             :  * The most important one is to prepare code to be handled by the debugger.
    1330             :  * The current analysis is simple and fulfills our short-term needs.
    1331             :  * A future version may analyze the parameter settings in more detail.
    1332             :  */
    1333             : 
    1334             : #define MAX_QUERY       (64*1024*1024)
    1335             : 
    1336             : static str
    1337      408863 : SQLparser_body(Client c, backend *be)
    1338             : {
    1339      408863 :         str msg = MAL_SUCCEED;
    1340      408863 :         mvc *m = be->mvc;
    1341      408863 :         lng Tbegin = 0, Tend = 0;
    1342             : 
    1343      408863 :         int pstatus = m->session->status;
    1344             : 
    1345      408863 :         int err = 0;
    1346      408863 :         m->type = Q_PARSE;
    1347      408863 :         m->emode = m_normal;
    1348      408863 :         m->emod = mod_none;
    1349      408863 :         c->query = NULL;
    1350      408863 :         c->qryctx.starttime = Tbegin = Tend = GDKusec();
    1351      408881 :         c->qryctx.endtime = c->querytimeout ? c->qryctx.starttime + c->querytimeout : 0;
    1352             : 
    1353      408881 :         if ((err = sqlparse(m)) ||
    1354      407662 :                 m->scanner.aborted ||
    1355      815265 :                 ((m->scanner.aborted |= bstream_getoob(m->scanner.rs) != 0) != false) ||
    1356             :             /* Only forget old errors on transaction boundaries */
    1357      815500 :             (mvc_status(m) && m->type != Q_TRANS) || !m->sym) {
    1358      149257 :                 if (!err && m->scanner.started)      /* repeat old errors, with a parsed query */
    1359       17085 :                         err = mvc_status(m);
    1360      149562 :                 if (m->scanner.aborted) {
    1361           0 :                         msg = createException(PARSE, "SQLparser", "Query aborted");
    1362           0 :                         *m->errstr = 0;
    1363      149562 :                 } else if (err && *m->errstr) {
    1364        1072 :                         if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
    1365        1071 :                                 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
    1366             :                         else
    1367           1 :                                 msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
    1368        1072 :                         *m->errstr = 0;
    1369             :                 }
    1370      149562 :                 if (m->sym)
    1371       16373 :                         msg = handle_error(m, pstatus, msg);
    1372      149580 :                 if (!m->sym) /* skip empty input */
    1373      133126 :                         m->emode = m_deallocate;
    1374      149580 :                 sqlcleanup(be, err);
    1375      149873 :                 goto finalize;
    1376             :         }
    1377             :         /*
    1378             :          * We have dealt with the first parsing step and advanced the input reader
    1379             :          * to the next statement (if any).
    1380             :          * Now is the time to also perform the semantic analysis, optimize and
    1381             :          * produce code.
    1382             :          */
    1383      258441 :         c->query = query_cleaned(m->sa, QUERY(m->scanner));
    1384             : 
    1385      258500 :         if (profilerStatus > 0) {
    1386           0 :                 profilerEvent(NULL,
    1387             :                                           &(struct NonMalEvent)
    1388           0 :                                           {TEXT_TO_SQL, c, Tend, &m->session->tr->ts, NULL, c->query?0:1, Tend-Tbegin});
    1389             :         }
    1390             : 
    1391      258500 :         if (c->query == NULL) {
    1392           0 :                 err = 1;
    1393           0 :                 msg = createException(PARSE, "SQLparser", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1394      258500 :         } else if (m->emode == m_deallocate) {
    1395           8 :                 AtomNode *an = (AtomNode *) m->sym;
    1396           8 :                 assert(m->sym->type == type_symbol && an->a->data.vtype == TYPE_int);
    1397           8 :                 int preparedid = an->a->data.val.ival;
    1398             : 
    1399           8 :                 if (preparedid > -1) { /* The -1 case represents the deallocate the entire query cache */
    1400           4 :                         be->q = qc_find(m->qc, preparedid);
    1401           4 :                         if (!be->q) {
    1402           2 :                                 msg = createException(SQL, "DEALLOC", SQLSTATE(07003) "No prepared statement with id: %d\n", preparedid);
    1403           2 :                                 *m->errstr = 0;
    1404           2 :                                 msg = handle_error(m, pstatus, msg);
    1405           2 :                                 sqlcleanup(be, -1);
    1406           2 :                                 return msg;
    1407             :                         } else {
    1408           2 :                                 qc_delete(m->qc, be->q);
    1409           2 :                                 be->q = NULL;
    1410             :                         }
    1411             :                 } else {
    1412           4 :                         qc_clean(m->qc);
    1413             :                 }
    1414             : 
    1415           6 :                 m->type = Q_SCHEMA; /* TODO DEALLOCATE statements don't fit for Q_SCHEMA */
    1416           6 :                 mvc_query_processed(m);
    1417             : 
    1418             :                 /* For deallocate statements just export a simple output */
    1419           6 :                 if (!GDKembedded() && (err = mvc_export_operation(be, c->fdout, "", c->qryctx.starttime, c->curprg->def->optimize)) < 0)
    1420           0 :                         msg = createException(PARSE, "SQLparser", SQLSTATE(45000) "Export operation failed: %s", mvc_export_error(be, c->fdout, err));
    1421           6 :                 sqlcleanup(be, 0);
    1422           6 :                 return msg;
    1423             :         } else {
    1424      258492 :                 sql_rel *r = sql_symbol2relation(be, m->sym);
    1425             : 
    1426      257761 :                 if (!r || (err = mvc_status(m) && m->type != Q_TRANS && *m->errstr)) {
    1427        2327 :                         if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
    1428        2325 :                                 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
    1429             :                         else
    1430           2 :                                 msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
    1431        2327 :                         *m->errstr = 0;
    1432        2327 :                         msg = handle_error(m, pstatus, msg);
    1433        2327 :                         sqlcleanup(be, err);
    1434        2327 :                         goto finalize;
    1435             :                 }
    1436             : 
    1437      255382 :                 int oldvtop = c->curprg->def->vtop;
    1438      255382 :                 int oldstop = c->curprg->def->stop;
    1439      255382 :                 be->vtop = oldvtop;
    1440      255382 :                 (void)runtimeProfileSetTag(c); /* generate and set the tag in the mal block of the clients current program. */
    1441      256470 :                 if (m->emode != m_prepare || (m->emode == m_prepare && (m->emod & mod_exec) && is_ddl(r->op)) /* direct execution prepare */) {
    1442      256134 :                         mvc_query_processed(m);
    1443             : 
    1444      256122 :                         err = 0;
    1445      256122 :                         setVarType(c->curprg->def, 0, 0);
    1446      256122 :                         if (m->emode != m_prepare && be->subbackend && be->subbackend->check(be->subbackend, r)) {
    1447           0 :                                 res_table *rt = NULL;
    1448           0 :                                 if (be->subbackend->exec(be->subbackend, r, be->result_id++, &rt) == NULL) { /* on error fall back */
    1449           0 :                                         be->subbackend->reset(be->subbackend);
    1450           0 :                                         if (rt) {
    1451           0 :                                                 rt->next = be->results;
    1452           0 :                                                 be->results = rt;
    1453             :                                         }
    1454           0 :                                         return NULL;
    1455             :                                 }
    1456           0 :                                 be->subbackend->reset(be->subbackend);
    1457             :                         }
    1458             : 
    1459      256122 :                         Tbegin = GDKusec();
    1460             : 
    1461      256096 :                         int opt = 0;
    1462      256096 :                         if (m->emode == m_prepare && (m->emod & mod_exec)) {
    1463             :                                 /* generated the named parameters for the placeholders */
    1464           3 :                                 if (backend_dumpstmt(be, c->curprg->def, r->r, !(m->emod & mod_exec), 0, c->query) < 0) {
    1465           0 :                                         msg = handle_error(m, 0, msg);
    1466           0 :                                         err = 1;
    1467           0 :                                         MSresetInstructions(c->curprg->def, oldstop);
    1468           0 :                                         freeVariables(c, c->curprg->def, NULL, oldvtop);
    1469             :                                 }
    1470           3 :                                 r = r->l;
    1471           3 :                                 m->emode = m_normal;
    1472           3 :                                 m->emod &= ~mod_exec;
    1473             :                         }
    1474      256096 :                         if (!err && backend_dumpstmt(be, c->curprg->def, r, !(m->emod & mod_exec), 0, c->query) < 0) {
    1475           7 :                                 msg = handle_error(m, 0, msg);
    1476           7 :                                 err = 1;
    1477           7 :                                 MSresetInstructions(c->curprg->def, oldstop);
    1478           7 :                                 freeVariables(c, c->curprg->def, NULL, oldvtop);
    1479           7 :                                 freeException(c->curprg->def->errors);
    1480           7 :                                 c->curprg->def->errors = NULL;
    1481             :                         } else
    1482      255957 :                                 opt = ((m->emod & mod_exec) == 0); /* no need to optimize prepare - execute */
    1483             : 
    1484      255964 :                         Tend = GDKusec();
    1485      255780 :                         if(profilerStatus > 0)
    1486           0 :                                 profilerEvent(NULL,
    1487             :                                                           &(struct NonMalEvent)
    1488           0 :                                                           {REL_TO_MAL, c, Tend, NULL, NULL, c->query?0:1, Tend-Tbegin});
    1489      255780 :                         if (err)
    1490           7 :                                 m->session->status = -10;
    1491           7 :                         if (err == 0) {
    1492             :                                 /* no parsing error encountered, finalize the code of the query wrapper */
    1493      255773 :                                 pushEndInstruction(c->curprg->def);
    1494             : 
    1495             :                                 /* check the query wrapper for errors */
    1496      255812 :                                 if (msg == MAL_SUCCEED)
    1497      255683 :                                         msg = chkTypes(c->usermodule, c->curprg->def, TRUE);
    1498             : 
    1499      255709 :                                 if (msg == MAL_SUCCEED && opt) {
    1500      249811 :                                         Tbegin = Tend;
    1501      249811 :                                         msg = SQLoptimizeQuery(c, c->curprg->def);
    1502      249985 :                                         Tend = GDKusec();
    1503      249969 :                                         if (profilerStatus > 0)
    1504           0 :                                                 profilerEvent(NULL,
    1505             :                                                           &(struct NonMalEvent)
    1506           0 :                                                           {MAL_OPT, c, Tend, NULL, NULL, msg==MAL_SUCCEED?0:1, Tend-Tbegin});
    1507      249969 :                                         if (msg != MAL_SUCCEED) {
    1508           1 :                                                 str other = c->curprg->def->errors;
    1509           1 :                                                 c->curprg->def->errors = 0;
    1510           1 :                                                 MSresetInstructions(c->curprg->def, oldstop);
    1511           1 :                                                 freeVariables(c, c->curprg->def, NULL, oldvtop);
    1512           1 :                                                 if (other != msg)
    1513           1 :                                                         freeException(other);
    1514           1 :                                                 goto finalize;
    1515             :                                         }
    1516             :                                 }
    1517             : 
    1518             :                                 /* we know more in this case than chkProgram(c->fdout, c->usermodule, c->curprg->def); */
    1519      255866 :                                 if (msg == MAL_SUCCEED && c->curprg->def->errors) {
    1520           0 :                                         msg = c->curprg->def->errors;
    1521           0 :                                         c->curprg->def->errors = 0;
    1522             :                                         /* restore the state */
    1523           0 :                                         MSresetInstructions(c->curprg->def, oldstop);
    1524           0 :                                         freeVariables(c, c->curprg->def, NULL, oldvtop);
    1525           0 :                                         if (msg == NULL && *m->errstr){
    1526             :                                                 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
    1527             :                                                         msg = createException(PARSE, "SQLparser", "%s", m->errstr);
    1528             :                                                 else
    1529             :                                                         msg = createException(PARSE, "SQLparser", SQLSTATE(M0M27) "Semantic errors %s", m->errstr);
    1530             :                                                 *m->errstr = 0;
    1531             :                                         } else if (msg) {
    1532           0 :                                                 str newmsg = createException(PARSE, "SQLparser", SQLSTATE(M0M27) "Semantic errors %s", msg);
    1533           0 :                                                 freeException(msg);
    1534           0 :                                                 msg = newmsg;
    1535             :                                         }
    1536             :                                 }
    1537             :                         }
    1538             :                 } else {
    1539         336 :                         char *q_copy = sa_strdup(m->sa, c->query);
    1540             : 
    1541         336 :                         be->q = NULL;
    1542         336 :                         if (!q_copy) {
    1543           0 :                                 msg = createException(PARSE, "SQLparser", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1544           0 :                                 err = 1;
    1545             :                         } else {
    1546         672 :                                 be->q = qc_insert(m->qc, m->sa,        /* the allocator */
    1547             :                                                   r,    /* keep relational query */
    1548         336 :                                                   m->sym,    /* the sql symbol tree */
    1549             :                                                   m->params, /* the argument list */
    1550             :                                                   m->type,   /* the type of the statement */
    1551             :                                                   q_copy,
    1552         336 :                                                   be->no_mitosis);
    1553         336 :                                 if (!be->q) {
    1554           0 :                                         msg = createException(PARSE, "SQLparser", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1555           0 :                                         err = 1;
    1556             :                                 }
    1557             :                         }
    1558         336 :                         mvc_query_processed(m);
    1559         336 :                         if (be->q && backend_dumpproc(be, c, be->q, r) < 0) {
    1560           1 :                                 msg = handle_error(m, 0, msg);
    1561           1 :                                 err = 1;
    1562             :                         }
    1563             : 
    1564             :                         /* passed over to query cache, used during dumpproc */
    1565         336 :                         m->sa = NULL;
    1566         336 :                         m->sym = NULL;
    1567         336 :                         m->runs = NULL;
    1568         336 :                         m->params = NULL;
    1569             : 
    1570         336 :                         if (be->q) {
    1571         336 :                                 int res = 0;
    1572         336 :                                 if (!err && (res = mvc_export_prepare(be, c->fdout)) < 0) {
    1573           0 :                                         msg = createException(PARSE, "SQLparser", SQLSTATE(45000) "Export operation failed: %s", mvc_export_error(be, c->fdout, res));
    1574           0 :                                         err = 1;
    1575             :                                 }
    1576           0 :                                 if (err) {
    1577           1 :                                         be->q->name = NULL; /* later remove cleanup from mal from qc code */
    1578           1 :                                         qc_delete(m->qc, be->q);
    1579             :                                 }
    1580         336 :                                 be->result_id = be->q->id;
    1581         336 :                                 be->q = NULL;
    1582             :                         }
    1583         336 :                         if (err)
    1584           1 :                                 m->session->status = -10;
    1585         336 :                         sqlcleanup(be, 0);
    1586         336 :                         c->query = NULL;
    1587         336 :                         return msg;
    1588             :                 }
    1589             :         }
    1590      408074 : finalize:
    1591      408074 :         if (m->sa)
    1592      408074 :                 eb_init(&m->sa->eb); /* exiting the scope where the exception buffer can be used */
    1593      407572 :         if (msg) {
    1594       19797 :                 sqlcleanup(be, 0);
    1595       19799 :                 c->query = NULL;
    1596             :         }
    1597             :         return msg;
    1598             : }
    1599             : 
    1600             : static str
    1601      408906 : SQLparser(Client c, backend *be)
    1602             : {
    1603      408906 :         mvc *m = be->mvc;
    1604      408906 :         char *msg;
    1605             : 
    1606      408906 :         assert (be->language != 'X');
    1607             : 
    1608      408906 :         if ((msg = SQLtrans(m)) != MAL_SUCCEED) {
    1609           1 :                 c->mode = FINISHCLIENT;
    1610           1 :                 return msg;
    1611             :         }
    1612             : 
    1613             :         /* sqlparse needs sql allocator to be available.  It can be NULL at
    1614             :          * this point if this is a recursive call. */
    1615      408917 :         if (m->sa == NULL)
    1616       37129 :                 m->sa = sa_create(m->pa);
    1617      408918 :         if (m->sa == NULL) {
    1618           0 :                 c->mode = FINISHCLIENT;
    1619           0 :                 throw(SQL, "SQLparser", SQLSTATE(HY013) MAL_MALLOC_FAIL " for SQL allocator");
    1620             :         }
    1621      408868 :         if (eb_savepoint(&m->sa->eb)) {
    1622           0 :                 msg = createException(SQL, "SQLparser", "%s", m->sa->eb.msg);
    1623           0 :                 eb_init(&m->sa->eb);
    1624           0 :                 sa_reset(m->sa);
    1625           0 :                 if (c && c->curprg && c->curprg->def && c->curprg->def->errors) {
    1626           0 :                         freeException(c->curprg->def->errors);
    1627           0 :                         c->curprg->def->errors = NULL;
    1628             :                 }
    1629           0 :                 sqlcleanup(be, 0);
    1630           0 :                 c->query = NULL;
    1631           0 :                 return msg;
    1632             :         }
    1633      408868 :         return SQLparser_body(c, be);
    1634             : }
    1635             : 
    1636             : str
    1637      489198 : SQLengine_(Client c)
    1638             : {
    1639      489198 :         backend *be = (backend *) c->sqlcontext;
    1640             : 
    1641      489198 :         if (be == 0) {
    1642             :                 /* leave a message in the log */
    1643           0 :                 TRC_ERROR(SQL_PARSER, "SQL state description is missing, cannot handle client!\n");
    1644             :                 /* stop here, instead of printing the exception below to the
    1645             :                  * client in an endless loop */
    1646           0 :                 c->mode = FINISHCLIENT;
    1647           0 :                 throw(SQL, "SQLparser", SQLSTATE(42000) "State descriptor missing, aborting");
    1648             :         }
    1649             : 
    1650      489198 :         str msg = SQLreader(c, be);
    1651      489936 :         if (msg || c->mode <= FINISHCLIENT)
    1652             :                 return msg;
    1653             : 
    1654      451974 :         if (be->language == 'X') {
    1655       43015 :                 return SQLchannelcmd(c, be);
    1656      408959 :         } else if (be->language !='S') {
    1657           0 :                 msg = createException(SQL, "SQLparser", SQLSTATE(42000) "Unrecognized language prefix: %ci\n", be->language);
    1658           0 :                 c->mode = FINISHCLIENT; /* and disconnect, as client doesn't respect the mapi protocol */
    1659           0 :                 sqlcleanup(be, 0);
    1660           0 :                 c->query = NULL;
    1661             :         } else {
    1662      408959 :                 msg = SQLparser(c, be);
    1663      407731 :                 if (msg == MAL_SUCCEED && (be->mvc->emode == m_deallocate || be->mvc->emode == m_prepare))
    1664             :                         return msg;
    1665             :         }
    1666      256008 :         if (msg || c->mode <= FINISHCLIENT)
    1667       19257 :                 return msg;
    1668             : 
    1669      256008 :         if (c->curprg->def->stop == 1) {
    1670           0 :                 sqlcleanup(be, 0);
    1671           0 :                 return NULL;
    1672             :         }
    1673      256008 :         return SQLengineIntern(c, be);
    1674             : }
    1675             : 
    1676             : void
    1677      489237 : SQLengine(Client c)
    1678             : {
    1679      489237 :         char *msg = SQLengine_(c);
    1680      489097 :         if (msg) {
    1681             :                 /* remove exception decoration */
    1682       76638 :                 for (char *m = msg; m && *m; ) {
    1683       38275 :                         char *n = strchr(m, '\n');
    1684       38275 :                         char *s = getExceptionMessageAndState(m);
    1685       38613 :                         mnstr_printf(c->fdout, "!%.*s\n", (int) (n - s), s);
    1686       38592 :                         m = n;
    1687       38592 :                         if (n) {
    1688       38592 :                                 m++; /* include newline */
    1689             :                         }
    1690             :                 }
    1691       38363 :                 freeException(msg);
    1692             :         }
    1693      489448 : }
    1694             : 
    1695             : str
    1696           0 : SYSupdate_tables(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1697             : {
    1698           0 :         mvc *m = ((backend *) cntxt->sqlcontext)->mvc;
    1699             : 
    1700           0 :         (void) mb;
    1701           0 :         (void) stk;
    1702           0 :         (void) pci;
    1703             : 
    1704           0 :         sql_trans_update_tables(m->session->tr, mvc_bind_schema(m, "sys"));
    1705           0 :         return MAL_SUCCEED;
    1706             : }
    1707             : 
    1708             : str
    1709         226 : SYSupdate_schemas(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1710             : {
    1711         226 :         mvc *m = ((backend *) cntxt->sqlcontext)->mvc;
    1712             : 
    1713         226 :         (void) mb;
    1714         226 :         (void) stk;
    1715         226 :         (void) pci;
    1716             : 
    1717         226 :         if (sql_trans_update_schemas(m->session->tr) < 0)
    1718           0 :                 throw(MAL, "sql.update_schemas", MAL_MALLOC_FAIL);
    1719             :         return MAL_SUCCEED;
    1720             : }

Generated by: LCOV version 1.14