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