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