LCOV - code coverage report
Current view: top level - sql/backends/monet5 - sql_scenario.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 669 970 69.0 %
Date: 2024-04-26 00:35:57 Functions: 25 27 92.6 %

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

Generated by: LCOV version 1.14