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