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

Generated by: LCOV version 1.14