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

Generated by: LCOV version 1.14