1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/*
10 * (authors) N. Nes, M.L. Kersten
11 * The SQL scenario implementation is a derivative of the MAL session scenario.
12 *
13 */
14/*
15 * Before we are can process SQL statements the global catalog
16 * should be initialized. Thereafter, each time a client enters
17 * we update its context descriptor to denote an SQL scenario.
18 */
19#include "monetdb_config.h"
20#include "mal_backend.h"
21#include "sql_scenario.h"
22#include "sql_result.h"
23#include "sql_gencode.h"
24#include "sql_optimizer.h"
25#include "sql_assert.h"
26#include "sql_execute.h"
27#include "sql_env.h"
28#include "sql_mvc.h"
29#include "sql_user.h"
30#include "sql_datetime.h"
31#include "mal_io.h"
32#include "mal_parser.h"
33#include "mal_builder.h"
34#include "mal_namespace.h"
35#include "mal_debugger.h"
36#include "mal_linker.h"
37#include "bat5.h"
38#include "wlc.h"
39#include "wlr.h"
40#include "msabaoth.h"
41#include "mtime.h"
42#include "optimizer.h"
43#include "opt_prelude.h"
44#include "opt_pipes.h"
45#include "opt_mitosis.h"
46#include <unistd.h>
47#include "sql_upgrades.h"
48
49static int SQLinitialized = 0;
50static int SQLnewcatalog = 0;
51int SQLdebug = 0;
52static const char *sqlinit = NULL;
53static MT_Lock sql_contextLock = MT_LOCK_INITIALIZER("sql_contextLock");
54
55static void
56monet5_freestack(int clientid, backend_stack stk)
57{
58 MalStkPtr p = (ptr) stk;
59
60 (void) clientid;
61 if (p != NULL)
62 freeStack(p);
63#ifdef _SQL_SCENARIO_DEBUG
64 fprintf(stderr, "#monet5_freestack\n");
65#endif
66}
67
68static void
69monet5_freecode(int clientid, backend_code code, backend_stack stk, int nr, char *name)
70{
71 str msg;
72
73 (void) code;
74 (void) stk;
75 (void) nr;
76 (void) clientid;
77 msg = SQLCacheRemove(MCgetClient(clientid), name);
78 if (msg)
79 freeException(msg); /* do something with error? */
80
81#ifdef _SQL_SCENARIO_DEBUG
82 fprintf(stderr, "#monet5_free:%d\n", nr);
83#endif
84}
85
86static str SQLinit(Client c);
87
88str
89//SQLprelude(void *ret)
90SQLprelude(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
91{
92 str tmp;
93 Scenario ms, s = getFreeScenario();
94
95 (void) mb;
96 (void) stk;
97 (void) pci;
98 if (!s)
99 throw(MAL, "sql.start", SQLSTATE(42000) "out of scenario slots");
100 sqlinit = GDKgetenv("sqlinit");
101 *s = (struct SCENARIO) {
102 .name = "S_Q_L",
103 .language = "sql",
104 .exitSystem = "SQLexit",
105 .exitSystemCmd = SQLexit,
106 .initClient = "SQLinitClient",
107 .initClientCmd = SQLinitClient,
108 .exitClient = "SQLexitClient",
109 .exitClientCmd = SQLexitClient,
110 .reader = "SQLreader",
111 .readerCmd = SQLreader,
112 .parser = "SQLparser",
113 .parserCmd = SQLparser,
114 .engine = "SQLengine",
115 .engineCmd = SQLengine,
116 .callback = "SQLcallback",
117 .callbackCmd = SQLcallback,
118 };
119 ms = getFreeScenario();
120 if (!ms)
121 throw(MAL, "sql.start", SQLSTATE(42000) "out of scenario slots");
122
123 *ms = (struct SCENARIO) {
124 .name = "M_S_Q_L",
125 .language = "msql",
126 .exitSystem = "SQLexit",
127 .exitSystemCmd = SQLexit,
128 .initClient = "SQLinitClientFromMAL",
129 .initClientCmd = SQLinitClientFromMAL,
130 .exitClient = "SQLexitClient",
131 .exitClientCmd = SQLexitClient,
132 .reader = "MALreader",
133 .readerCmd = MALreader,
134 .parser = "MALparser",
135 .parserCmd = MALparser,
136 .optimizer = "MALoptimizer",
137 .optimizerCmd = MALoptimizer,
138 .engine = "MALengine",
139 .engineCmd = MALengine,
140 .callback = "MALcallback",
141 .callbackCmd = MALcallback,
142 };
143
144 tmp = SQLinit(cntxt);
145 if (tmp != MAL_SUCCEED) {
146 fprintf(stderr, "Fatal error during initialization:\n%s\n", tmp);
147 freeException(tmp);
148 if ((tmp = GDKerrbuf) && *tmp)
149 fprintf(stderr, SQLSTATE(42000) "GDK reported: %s\n", tmp);
150 fflush(stderr);
151 exit(1);
152 }
153#ifndef HAVE_EMBEDDED
154 fprintf(stdout, "# MonetDB/SQL module loaded\n");
155 fflush(stdout); /* make merovingian see this *now* */
156#endif
157 if (GDKinmemory()) {
158 s->name = "sql";
159 ms->name = "msql";
160 return MAL_SUCCEED;
161 }
162 /* only register availability of scenarios AFTER we are inited! */
163 s->name = "sql";
164 tmp = msab_marchScenario(s->name);
165 if (tmp != NULL) {
166 char *err = createException(MAL, "sql.start", "%s", tmp);
167 free(tmp);
168 return err;
169 }
170 ms->name = "msql";
171 tmp = msab_marchScenario(ms->name);
172 if (tmp != NULL) {
173 char *err = createException(MAL, "sql.start", "%s", tmp);
174 free(tmp);
175 return err;
176 }
177 return MAL_SUCCEED;
178}
179
180str
181SQLexit(Client c)
182{
183#ifdef _SQL_SCENARIO_DEBUG
184 fprintf(stderr, "#SQLexit\n");
185#endif
186 (void) c; /* not used */
187 MT_lock_set(&sql_contextLock);
188 if (SQLinitialized) {
189 mvc_exit();
190 SQLinitialized = FALSE;
191 }
192 MT_lock_unset(&sql_contextLock);
193 return MAL_SUCCEED;
194}
195
196str
197SQLepilogue(void *ret)
198{
199 char *s = "sql", *m = "msql";
200 str res;
201
202 (void) ret;
203 (void) SQLexit(NULL);
204 /* this function is never called, but for the style of it, we clean
205 * up our own mess */
206 if (!GDKinmemory()) {
207 res = msab_retreatScenario(m);
208 if (!res)
209 res = msab_retreatScenario(s);
210 if (res != NULL) {
211 char *err = createException(MAL, "sql.start", "%s", res);
212 free(res);
213 return err;
214 }
215 }
216 return MAL_SUCCEED;
217}
218
219#define SQLglobal(name, val, failure) \
220 if(!stack_push_var(sql, name, &ctype) || !stack_set_var(sql, name, VALset(&src, ctype.type->localtype, (char*)(val)))) \
221 failure--;
222
223#define NR_GLOBAL_VARS 9
224/* NR_GLOBAL_VAR should match exactly the number of variables created
225 in global_variables */
226/* initialize the global variable, ie make mvc point to these */
227static int
228global_variables(mvc *sql, const char *user, const char *schema)
229{
230 sql_subtype ctype;
231 const char *typename;
232 lng sec = 0;
233 ValRecord src;
234 const char *opt;
235 int failure = 0;
236
237 typename = "int";
238 sql_find_subtype(&ctype, typename, 0, 0);
239 SQLglobal("debug", &sql->debug, failure);
240 SQLglobal("cache", &sql->cache, failure);
241
242 typename = "varchar";
243 sql_find_subtype(&ctype, typename, 1024, 0);
244 SQLglobal("current_schema", schema, failure);
245 SQLglobal("current_user", user, failure);
246 SQLglobal("current_role", user, failure);
247
248 /* inherit the optimizer from the server */
249 opt = GDKgetenv("sql_optimizer");
250 if (!opt)
251 opt = "default_pipe";
252 SQLglobal("optimizer", opt, failure);
253
254 typename = "sec_interval";
255 sql_find_subtype(&ctype, typename, inttype2digits(ihour, isec), 0);
256 SQLglobal("current_timezone", &sec, failure);
257
258 typename = "bigint";
259 sql_find_subtype(&ctype, typename, 0, 0);
260 SQLglobal("last_id", &sql->last_id, failure);
261 SQLglobal("rowcnt", &sql->rowcnt, failure);
262 return failure;
263}
264
265static const char *
266SQLgetquery(Client c)
267{
268 if (c) {
269 backend *be = c->sqlcontext;
270 if (be) {
271 mvc *m = be->mvc;
272 if (m)
273 return m->query;
274 }
275 }
276 return NULL;
277}
278
279static char*
280SQLprepareClient(Client c, int login)
281{
282 mvc *m;
283 str schema;
284 backend *be;
285
286 c->getquery = SQLgetquery;
287 if (c->sqlcontext == 0) {
288 m = mvc_create(c->idx, 0, SQLdebug, c->fdin, c->fdout);
289 if( m == NULL) {
290 throw(SQL,"sql.initClient",SQLSTATE(HY001) MAL_MALLOC_FAIL);
291 }
292 if(global_variables(m, "monetdb", "sys") < 0) {
293 mvc_destroy(m);
294 throw(SQL,"sql.initClient",SQLSTATE(HY001) MAL_MALLOC_FAIL);
295 }
296 if (c->scenario && strcmp(c->scenario, "msql") == 0)
297 m->reply_size = -1;
298 be = (void *) backend_create(m, c);
299 if( be == NULL) {
300 mvc_destroy(m);
301 throw(SQL,"sql.initClient", SQLSTATE(HY001) MAL_MALLOC_FAIL);
302 }
303 } else {
304 be = c->sqlcontext;
305 m = be->mvc;
306 /* Only reset if there is no active transaction which
307 * can happen when we combine sql.init with msql.
308 */
309 if(m->session->tr->active) {
310 return NULL;
311 }
312 if(mvc_reset(m, c->fdin, c->fdout, SQLdebug, NR_GLOBAL_VARS) < 0) {
313 throw(SQL,"sql.initClient", SQLSTATE(HY001) MAL_MALLOC_FAIL);
314 }
315 backend_reset(be);
316 }
317 if (m->session->tr)
318 reset_functions(m->session->tr);
319 if (login) {
320 schema = monet5_user_set_def_schema(m, c->user);
321 if (!schema) {
322 _DELETE(schema);
323 throw(PERMD, "SQLinitClient", SQLSTATE(08004) "schema authorization error");
324 }
325 _DELETE(schema);
326 }
327
328 /*expect SQL text first */
329 be->language = 'S';
330 /* Set state, this indicates an initialized client scenario */
331 c->state[MAL_SCENARIO_READER] = c;
332 c->state[MAL_SCENARIO_PARSER] = c;
333 c->state[MAL_SCENARIO_OPTIMIZE] = c;
334 c->sqlcontext = be;
335 return NULL;
336}
337
338str
339SQLresetClient(Client c)
340{
341 str msg = MAL_SUCCEED, other = MAL_SUCCEED;
342
343 if (c->sqlcontext == NULL)
344 throw(SQL, "SQLexitClient", SQLSTATE(42000) "MVC catalogue not available");
345 if (c->sqlcontext) {
346 backend *be = c->sqlcontext;
347 mvc *m = be->mvc;
348
349 assert(m->session);
350 if (m->session->auto_commit && m->session->tr->active) {
351 if (mvc_status(m) >= 0)
352 msg = mvc_commit(m, 0, NULL, false);
353 }
354 if (m->session->tr->active)
355 other = mvc_rollback(m, 0, NULL, false);
356
357 res_tables_destroy(m->results);
358 m->results = NULL;
359
360 mvc_destroy(m);
361 backend_destroy(be);
362 c->state[MAL_SCENARIO_OPTIMIZE] = NULL;
363 c->state[MAL_SCENARIO_PARSER] = NULL;
364 c->sqlcontext = NULL;
365 }
366 c->state[MAL_SCENARIO_READER] = NULL;
367 if(other && !msg)
368 msg = other;
369 else if(other && msg)
370 freeException(other);
371 return msg;
372}
373
374MT_Id sqllogthread, idlethread;
375
376static str
377SQLinit(Client c)
378{
379 const char *debug_str = GDKgetenv("sql_debug");
380 char *msg = MAL_SUCCEED, *other = MAL_SUCCEED;
381 bool readonly = GDKgetenv_isyes("gdk_readonly");
382 bool single_user = GDKgetenv_isyes("gdk_single_user");
383 static int maybeupgrade = 1;
384 backend *be = NULL;
385 mvc *m = NULL;
386
387#ifdef _SQL_SCENARIO_DEBUG
388 fprintf(stderr, "#SQLinit Monet 5\n");
389#endif
390 MT_lock_set(&sql_contextLock);
391
392 if (SQLinitialized) {
393 MT_lock_unset(&sql_contextLock);
394 return MAL_SUCCEED;
395 }
396
397 be_funcs = (backend_functions) {
398 .fstack = &monet5_freestack,
399 .fcode = &monet5_freecode,
400 .fresolve_function = &monet5_resolve_function,
401 };
402 monet5_user_init(&be_funcs);
403
404 if (debug_str)
405 SQLdebug = strtol(debug_str, NULL, 10);
406 if (single_user)
407 SQLdebug |= 64;
408 if (readonly)
409 SQLdebug |= 32;
410 if ((SQLnewcatalog = mvc_init(SQLdebug, GDKinmemory() ? store_mem : store_bat, readonly, single_user, 0)) < 0) {
411 MT_lock_unset(&sql_contextLock);
412 throw(SQL, "SQLinit", SQLSTATE(42000) "Catalogue initialization failed");
413 }
414 SQLinitialized = TRUE;
415 sqlinit = GDKgetenv("sqlinit");
416 if (sqlinit) { /* add sqlinit to the fdin stack */
417 buffer *b = (buffer *) GDKmalloc(sizeof(buffer));
418 size_t len = strlen(sqlinit);
419 char* cbuf = _STRDUP(sqlinit);
420 stream *buf;
421 bstream *fdin;
422
423 if( b == NULL || cbuf == NULL) {
424 MT_lock_unset(&sql_contextLock);
425 GDKfree(b);
426 GDKfree(cbuf);
427 throw(SQL,"sql.init",SQLSTATE(HY001) MAL_MALLOC_FAIL);
428 }
429
430 buffer_init(b, cbuf, len);
431 buf = buffer_rastream(b, "si");
432 if( buf == NULL) {
433 MT_lock_unset(&sql_contextLock);
434 buffer_destroy(b);
435 throw(SQL,"sql.init",SQLSTATE(HY001) MAL_MALLOC_FAIL);
436 }
437
438 fdin = bstream_create(buf, b->len);
439 if( fdin == NULL) {
440 MT_lock_unset(&sql_contextLock);
441 buffer_destroy(b);
442 throw(SQL,"sql.init",SQLSTATE(HY001) MAL_MALLOC_FAIL);
443 }
444
445 bstream_next(fdin);
446 if( MCpushClientInput(c, fdin, 0, "") < 0)
447 fprintf(stderr, "SQLinit:Could not switch client input stream");
448 }
449 if ((msg = SQLprepareClient(c, 0)) != NULL) {
450 MT_lock_unset(&sql_contextLock);
451 fprintf(stderr, "%s\n", msg);
452 return msg;
453 }
454 be = c->sqlcontext;
455 m = be->mvc;
456 /* initialize the database with predefined SQL functions */
457 if (SQLnewcatalog == 0) {
458 /* check whether table sys.systemfunctions exists: if
459 * it doesn't, this is probably a restart of the
460 * server after an incomplete initialization */
461 sql_schema *s = mvc_bind_schema(m, "sys");
462 sql_table *t = s ? mvc_bind_table(m, s, "systemfunctions") : NULL;
463 if (t == NULL)
464 SQLnewcatalog = 1;
465 }
466 if (SQLnewcatalog > 0) {
467 SQLnewcatalog = 0;
468 maybeupgrade = 0;
469
470#ifdef HAVE_EMBEDDED
471 size_t createdb_len = strlen(createdb_inline);
472 buffer* createdb_buf;
473 stream* createdb_stream;
474 bstream* createdb_bstream;
475 if ((createdb_buf = GDKmalloc(sizeof(buffer))) == NULL) {
476 MT_lock_unset(&sql_contextLock);
477 throw(MAL, "createdb", SQLSTATE(HY001) MAL_MALLOC_FAIL);
478 }
479 buffer_init(createdb_buf, createdb_inline, createdb_len);
480 if ((createdb_stream = buffer_rastream(createdb_buf, "createdb.sql")) == NULL) {
481 MT_lock_unset(&sql_contextLock);
482 GDKfree(createdb_buf);
483 throw(MAL, "createdb", SQLSTATE(HY001) MAL_MALLOC_FAIL);
484 }
485 if ((createdb_bstream = bstream_create(createdb_stream, createdb_len)) == NULL) {
486 MT_lock_unset(&sql_contextLock);
487 close_stream(createdb_stream);
488 GDKfree(createdb_buf);
489 throw(MAL, "createdb", SQLSTATE(HY001) MAL_MALLOC_FAIL);
490 }
491 if (bstream_next(createdb_bstream) >= 0)
492 msg = SQLstatementIntern(c, &createdb_bstream->buf, "sql.init", TRUE, FALSE, NULL);
493 else
494 msg = createException(MAL, "createdb", SQLSTATE(42000) "Could not load inlined createdb script");
495
496 bstream_destroy(createdb_bstream);
497 GDKfree(createdb_buf);
498 if (m->sa)
499 sa_destroy(m->sa);
500 m->sa = NULL;
501
502#else
503 char path[FILENAME_MAX];
504 str fullname;
505
506 snprintf(path, FILENAME_MAX, "createdb");
507 slash_2_dir_sep(path);
508 fullname = MSP_locate_sqlscript(path, 1);
509 if (fullname) {
510 str filename = fullname, p, n;
511
512 fprintf(stdout, "# SQL catalog created, loading sql scripts once\n");
513 do {
514 stream *fd = NULL;
515
516 p = strchr(filename, PATH_SEP);
517 if (p)
518 *p = '\0';
519 if ((n = strrchr(filename, DIR_SEP)) == NULL) {
520 n = filename;
521 } else {
522 n++;
523 }
524 fprintf(stdout, "# loading sql script: %s\n", n);
525 fd = open_rastream(filename);
526 if (p)
527 filename = p + 1;
528
529 if (fd) {
530 size_t sz;
531 sz = getFileSize(fd);
532 if (sz > (size_t) 1 << 29) {
533 close_stream(fd);
534 msg = createException(MAL, "createdb", SQLSTATE(42000) "File %s too large to process", filename);
535 } else {
536 bstream *bfd = NULL;
537
538 if ((bfd = bstream_create(fd, sz == 0 ? (size_t) (128 * BLOCK) : sz)) == NULL) {
539 close_stream(fd);
540 msg = createException(MAL, "createdb", SQLSTATE(HY001) MAL_MALLOC_FAIL);
541 } else {
542 if (bstream_next(bfd) >= 0)
543 msg = SQLstatementIntern(c, &bfd->buf, "sql.init", TRUE, FALSE, NULL);
544 bstream_destroy(bfd);
545 }
546 }
547 } else
548 msg = createException(MAL, "createdb", SQLSTATE(HY001) "Couldn't open file %s", filename);
549 } while (p && msg == MAL_SUCCEED);
550 GDKfree(fullname);
551 } else
552 msg = createException(MAL, "createdb", SQLSTATE(HY001) "Could not read createdb.sql");
553
554 /* Commit after all the startup scripts have been processed */
555 assert(m->session->tr->active);
556 if (mvc_status(m) < 0 || msg)
557 other = mvc_rollback(m, 0, NULL, false);
558 else
559 other = mvc_commit(m, 0, NULL, false);
560
561 if (other && !msg) /* 'msg' variable might be set or not, as well as 'other'. Throw the earliest one */
562 msg = other;
563 else if (other)
564 freeException(other);
565
566 if (msg)
567 fprintf(stderr, "%s", msg);
568#endif
569 } else { /* handle upgrades */
570 if (!m->sa)
571 m->sa = sa_create();
572 if (!m->sa) {
573 msg = createException(MAL, "createdb", SQLSTATE(HY001) MAL_MALLOC_FAIL);
574 } else if (maybeupgrade) {
575 if ((msg = SQLtrans(m)) == MAL_SUCCEED) {
576 int res = SQLupgrades(c, m);
577 /* Commit at the end of the upgrade */
578 assert(m->session->tr->active);
579 if (mvc_status(m) < 0 || res)
580 msg = mvc_rollback(m, 0, NULL, false);
581 else
582 msg = mvc_commit(m, 0, NULL, false);
583 }
584 }
585 maybeupgrade = 0;
586 }
587 fflush(stdout);
588 fflush(stderr);
589
590 /* send error from create scripts back to the first client */
591 if (msg) {
592 msg = handle_error(m, 0, msg);
593 *m->errstr = 0;
594 sqlcleanup(m, mvc_status(m));
595 }
596
597 other = SQLresetClient(c);
598 MT_lock_unset(&sql_contextLock);
599 if (other && !msg) /* 'msg' variable might be set or not, as well as 'other'. Throw the earliest one */
600 msg = other;
601 else if (other)
602 freeException(other);
603 if (msg != MAL_SUCCEED)
604 return msg;
605
606 if (GDKinmemory())
607 return MAL_SUCCEED;
608
609 if ((sqllogthread = THRcreate((void (*)(void *)) mvc_logmanager, NULL, MT_THR_DETACHED, "logmanager")) == 0) {
610 throw(SQL, "SQLinit", SQLSTATE(42000) "Starting log manager failed");
611 }
612 if (!(SQLdebug&1024)) {
613 if ((idlethread = THRcreate((void (*)(void *)) mvc_idlemanager, NULL, MT_THR_DETACHED, "idlemanager")) == 0) {
614 throw(SQL, "SQLinit", SQLSTATE(42000) "Starting idle manager failed");
615 }
616 }
617 if( wlc_state == WLC_STARTUP)
618 return WLCinit();
619 return MAL_SUCCEED;
620}
621
622#define TRANS_ABORTED SQLSTATE(25005) "Current transaction is aborted (please ROLLBACK)\n"
623
624str
625handle_error(mvc *m, int pstatus, str msg)
626{
627 str new = 0, newmsg= MAL_SUCCEED;
628
629 /* transaction already broken */
630 if (m->type != Q_TRANS && pstatus < 0) {
631 new = createException(SQL,"sql.execute",TRANS_ABORTED);
632 } else if( GDKerrbuf && GDKerrbuf[0]){
633 new = GDKstrdup(GDKerrbuf);
634 GDKerrbuf[0] = 0;
635 } else if( *m->errstr){
636 new = GDKstrdup(m->errstr);
637 m->errstr[0] = 0;
638 }
639 if( new && msg){
640 newmsg = GDKzalloc( strlen(msg) + strlen(new) + 64);
641 if (newmsg == NULL) {
642 newmsg = createException(SQL, "sql.execute", SQLSTATE(HY001) MAL_MALLOC_FAIL);
643 } else {
644 strcpy(newmsg, msg);
645 /* strcat(newmsg,"!"); */
646 strcat(newmsg,new);
647 }
648 freeException(new);
649 freeException(msg);
650 } else
651 if( msg)
652 newmsg = msg;
653 else
654 if( new)
655 newmsg = new;
656 return newmsg;
657}
658
659str
660SQLautocommit(mvc *m)
661{
662 str msg = MAL_SUCCEED;
663
664 if (m->session->auto_commit && m->session->tr->active) {
665 if (mvc_status(m) < 0) {
666 msg = mvc_rollback(m, 0, NULL, false);
667 } else {
668 msg = mvc_commit(m, 0, NULL, false);
669 }
670 }
671 return msg;
672}
673
674str
675SQLtrans(mvc *m)
676{
677 m->caching = m->cache;
678 if (!m->session->tr->active) {
679 sql_session *s;
680
681 if (mvc_trans(m) < 0)
682 throw(SQL, "sql.trans", SQLSTATE(HY001) MAL_MALLOC_FAIL);
683 s = m->session;
684 if (!s->schema) {
685 if (s->schema_name)
686 GDKfree(s->schema_name);
687 s->schema_name = monet5_user_get_def_schema(m, m->user_id);
688 if (!s->schema_name) {
689 mvc_cancel_session(m);
690 throw(SQL, "sql.trans", SQLSTATE(HY001) MAL_MALLOC_FAIL);
691 }
692 assert(s->schema_name);
693 s->schema = find_sql_schema(s->tr, s->schema_name);
694 assert(s->schema);
695 }
696 }
697 return MAL_SUCCEED;
698}
699
700#ifdef HAVE_EMBEDDED
701extern char* createdb_inline;
702#endif
703
704str
705SQLinitClient(Client c)
706{
707 str msg = MAL_SUCCEED;
708
709#ifdef _SQL_SCENARIO_DEBUG
710 fprintf(stderr, "#SQLinitClient\n");
711#endif
712
713 MT_lock_set(&sql_contextLock);
714 if (SQLinitialized == 0) {// && (msg = SQLprelude(NULL)) != MAL_SUCCEED)
715 MT_lock_unset(&sql_contextLock);
716 return msg;
717 }
718#ifndef HAVE_EMBEDDED
719 msg = SQLprepareClient(c, 1);
720#else
721 msg = SQLprepareClient(c, 0);
722#endif
723 MT_lock_unset(&sql_contextLock);
724 return msg;
725}
726
727str
728SQLinitClientFromMAL(Client c)
729{
730 str msg = MAL_SUCCEED;
731
732 if ((msg = SQLinitClient(c)) != MAL_SUCCEED) {
733 c->mode = FINISHCLIENT;
734 return msg;
735 }
736
737 mvc* m = ((backend*) c->sqlcontext)->mvc;
738
739 /* Crucial step:
740 * MAL scripts that interact with the sql module
741 * must have a properly initialized transaction.
742 */
743 if ((msg = SQLtrans(m)) != MAL_SUCCEED) {
744 c->mode = FINISHCLIENT;
745 return msg;
746 }
747 return msg;
748}
749
750str
751SQLexitClient(Client c)
752{
753 str err;
754
755#ifdef _SQL_SCENARIO_DEBUG
756 fprintf(stderr, "#SQLexitClient\n");
757#endif
758
759 MT_lock_set(&sql_contextLock);
760 if (SQLinitialized == FALSE) {
761 MT_lock_unset(&sql_contextLock);
762 throw(SQL, "SQLexitClient", SQLSTATE(42000) "Catalogue not available");
763 }
764 err = SQLresetClient(c);
765 MT_lock_unset(&sql_contextLock);
766 if (err != MAL_SUCCEED)
767 return err;
768 MALexitClient(c);
769 return MAL_SUCCEED;
770}
771
772str
773SQLstatement(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
774{
775 str *expr = getArgReference_str(stk, pci, 1);
776 bit output = TRUE;
777
778 (void) mb;
779 if (pci->argc == 3)
780 output = *getArgReference_bit(stk, pci, 2);
781
782 return SQLstatementIntern(cntxt, expr, "SQLstatement", TRUE, output, NULL);
783}
784
785str
786SQLcompile(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
787{
788 str *ret = getArgReference_str(stk, pci, 0);
789 str *expr = getArgReference_str(stk, pci, 1);
790 str msg;
791
792 (void) mb;
793 *ret = NULL;
794 if ((msg = SQLstatementIntern(cntxt, expr, "SQLcompile", FALSE, FALSE, NULL)) != MAL_SUCCEED)
795 return msg;
796 if((*ret = _STRDUP("SQLcompile")) == NULL)
797 throw(SQL,"sql.compile",SQLSTATE(HY001) MAL_MALLOC_FAIL);
798 return MAL_SUCCEED;
799}
800
801/*
802 * Locate a file with SQL commands and execute it. For the time being a 1MB
803 * file limit is implicitly imposed. If the file can not be located in the
804 * script library, we assume it is sufficiently self descriptive.
805 * (Respecting the file system context where the call is executed )
806 */
807str
808SQLinclude(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
809{
810 stream *fd;
811 bstream *bfd;
812 str *name = getArgReference_str(stk, pci, 1);
813 str msg = MAL_SUCCEED, fullname;
814 str *expr;
815 mvc *m;
816 size_t sz;
817
818 fullname = MSP_locate_sqlscript(*name, 0);
819 if (fullname == NULL)
820 fullname = *name;
821 fd = open_rastream(fullname);
822 if (mnstr_errnr(fd) == MNSTR_OPEN_ERROR) {
823 close_stream(fd);
824 throw(MAL, "sql.include", SQLSTATE(42000) "could not open file: %s\n", *name);
825 }
826 sz = getFileSize(fd);
827 if (sz > (size_t) 1 << 29) {
828 close_stream(fd);
829 throw(MAL, "sql.include", SQLSTATE(42000) "file %s too large to process", fullname);
830 }
831 if((bfd = bstream_create(fd, sz == 0 ? (size_t) (128 * BLOCK) : sz)) == NULL) {
832 close_stream(fd);
833 throw(MAL, "sql.include", SQLSTATE(HY001) MAL_MALLOC_FAIL);
834 }
835 if (bstream_next(bfd) < 0) {
836 bstream_destroy(bfd);
837 throw(MAL, "sql.include", SQLSTATE(42000) "could not read %s\n", *name);
838 }
839
840 expr = &bfd->buf;
841 msg = SQLstatementIntern(cntxt, expr, "sql.include", TRUE, FALSE, NULL);
842 bstream_destroy(bfd);
843 m = ((backend *) cntxt->sqlcontext)->mvc;
844 if (m->sa)
845 sa_destroy(m->sa);
846 m->sa = NULL;
847 (void) mb;
848 return msg;
849}
850
851/*
852 * The SQL reader collects a (sequence) of statements from the input
853 * stream, but only when no unresolved 'nxt' character is visible.
854 * In combination with SQLparser this ensures that all statements
855 * are handled one by one.
856 *
857 * The SQLreader is called from two places: the SQL parser and
858 * the MAL debugger.
859 * The former only occurs during the parsing phase and the
860 * second only during exection.
861 * This means we can safely change the language setting for
862 * the duration of these calls.
863 */
864
865/* #define _SQL_READER_DEBUG */
866str
867SQLreader(Client c)
868{
869 bool go = true;
870 str msg = MAL_SUCCEED;
871 bool more = true;
872 bool commit_done = false;
873 backend *be = (backend *) c->sqlcontext;
874 bstream *in = c->fdin;
875 int language = -1;
876 mvc *m = NULL;
877 bool blocked = isa_block_stream(in->s);
878 int isSQLinitialized;
879
880 MT_lock_set(&sql_contextLock);
881 isSQLinitialized = SQLinitialized;
882 MT_lock_unset(&sql_contextLock);
883
884 if (isSQLinitialized == FALSE) {
885 c->mode = FINISHCLIENT;
886 return MAL_SUCCEED;
887 }
888 if (!be || c->mode <= FINISHCLIENT) {
889#ifdef _SQL_READER_DEBUG
890 fprintf(stderr, "#SQL client finished\n");
891#endif
892 c->mode = FINISHCLIENT;
893 return MAL_SUCCEED;
894 }
895#ifdef _SQL_READER_DEBUG
896 fprintf(stderr, "#SQLparser: start reading SQL %s\n", (blocked ? "Blocked read" : ""));
897#endif
898 language = be->language; /* 'S' for SQL, 'D' from debugger */
899 m = be->mvc;
900 m->errstr[0] = 0;
901 /*
902 * Continue processing any left-over input from the previous round.
903 */
904
905#ifdef _SQL_READER_DEBUG
906 fprintf(stderr, "#pos %d len %d eof %d \n", in->pos, in->len, in->eof);
907#endif
908 while (more) {
909 more = false;
910
911 /* Different kinds of supported statements sequences
912 A; -- single line s
913 A \n B; -- multi line S
914 A; B; -- compound single block s
915 A; -- many multi line
916 B \n C; -- statements in one block S
917 */
918 /* auto_commit on end of statement */
919 if (language != 'D' && m->scanner.mode == LINE_N && !commit_done) {
920 msg = SQLautocommit(m);
921 go = msg == MAL_SUCCEED;
922 commit_done = true;
923 }
924
925 if (go && in->pos >= in->len) {
926 ssize_t rd;
927
928 if (c->bak) {
929#ifdef _SQL_READER_DEBUG
930 fprintf(stderr, "#Switch to backup stream\n");
931#endif
932 in = c->fdin;
933 blocked = isa_block_stream(in->s);
934 m->scanner.rs = c->fdin;
935 c->fdin->pos += c->yycur;
936 c->yycur = 0;
937 }
938 if (in->eof || !blocked) {
939 if (language != 'D')
940 language = 0;
941
942 /* The rules of auto_commit require us to finish
943 and start a transaction on the start of a new statement (s A;B; case) */
944 if (language != 'D' && !(m->emod & mod_debug) && !commit_done) {
945 msg = SQLautocommit(m);
946 go = msg == MAL_SUCCEED;
947 commit_done = true;
948 }
949
950 if (go && ((!blocked && mnstr_write(c->fdout, c->prompt, c->promptlength, 1) != 1) || mnstr_flush(c->fdout))) {
951 go = false;
952 break;
953 }
954 in->eof = false;
955 }
956 if (in->buf == NULL) {
957 more = false;
958 go = false;
959 } else if (go && (rd = bstream_next(in)) <= 0) {
960#ifdef _SQL_READER_DEBUG
961 fprintf(stderr, "#rd %d language %d eof %d\n", rd, language, in->eof);
962#endif
963 if (be->language == 'D' && !in->eof) {
964 in->pos++;// skip 's' or 'S'
965 return msg;
966 }
967
968 if (rd == 0 && language !=0 && in->eof) {
969 /* we hadn't seen the EOF before, so just try again
970 (this time with prompt) */
971 more = true;
972 continue;
973 }
974 go = false;
975 break;
976 } else if (go && language == 0) {
977 if (in->buf[in->pos] == 's' && !in->eof) {
978 while ((rd = bstream_next(in)) > 0)
979 ;
980 }
981 be->language = in->buf[in->pos++];
982 if (be->language == 's') {
983 be->language = 'S';
984 m->scanner.mode = LINE_1;
985 } else if (be->language == 'S') {
986 m->scanner.mode = LINE_N;
987 }
988 } else if (go && language == 'D' && !in->eof) {
989 in->pos++;// skip 's' or 'S'
990 }
991#ifdef _SQL_READER_DEBUG
992 fprintf(stderr, "#SQL blk:%s\n", in->buf + in->pos);
993#endif
994 }
995 }
996 if ( (c->sessiontimeout && (GDKusec() - c->session) > c->sessiontimeout) || !go || (strncmp(CURRENT(c), "\\q", 2) == 0)) {
997 in->pos = in->len; /* skip rest of the input */
998 c->mode = FINISHCLIENT;
999 return msg;
1000 }
1001 return msg;
1002}
1003
1004/*
1005 * The SQL block is stored in the client input buffer, from which it
1006 * can be parsed by the SQL parser. The client structure contains
1007 * a small table of bounded tables. This should be reset before we
1008 * parse a new statement sequence.
1009 * Before we parse the sql statement, we look for any variable settings
1010 * for specific commands.
1011 * The most important one is to prepare code to be handled by the debugger.
1012 * The current analysis is simple and fulfills our short-term needs.
1013 * A future version may analyze the parameter settings in more detail.
1014 */
1015
1016#define MAX_QUERY (64*1024*1024)
1017
1018static int
1019caching(mvc *m)
1020{
1021 return m->caching;
1022}
1023
1024static int
1025cachable(mvc *m, sql_rel *r)
1026{
1027 if (m->emode == m_prepare) /* prepared plans are always cached */
1028 return 1;
1029 if (m->emode == m_plan) /* we plan to display without execution */
1030 return 0;
1031 if (m->type == Q_TRANS ) /* m->type == Q_SCHEMA || cachable to make sure we have trace on alter statements */
1032 return 0;
1033 /* we don't store queries with a large footprint */
1034 if(r && sa_size(m->sa) > MAX_QUERY)
1035 return 0;
1036 return 1;
1037}
1038
1039/*
1040 * The core part of the SQL interface, parse the query and
1041 * store away the template (non)optimized code in the query cache
1042 * and the MAL module
1043 */
1044
1045str
1046SQLparser(Client c)
1047{
1048 bstream *in = c->fdin;
1049 stream *out = c->fdout;
1050 str msg = NULL;
1051 backend *be;
1052 mvc *m;
1053 int oldvtop, oldstop;
1054 int pstatus = 0;
1055 int err = 0, opt = 0;
1056 char *q = NULL;
1057
1058 be = (backend *) c->sqlcontext;
1059 if (be == 0) {
1060 /* leave a message in the log */
1061 fprintf(stderr, "SQL state descriptor missing, cannot handle client!\n");
1062 /* stop here, instead of printing the exception below to the
1063 * client in an endless loop */
1064 c->mode = FINISHCLIENT;
1065 throw(SQL, "SQLparser", SQLSTATE(42000) "State descriptor missing, aborting");
1066 }
1067 oldvtop = c->curprg->def->vtop;
1068 oldstop = c->curprg->def->stop;
1069 be->vtop = oldvtop;
1070#ifdef _SQL_PARSER_DEBUG
1071 fprintf(stderr, "#SQL compilation \n");
1072 fprintf(stderr,"debugger? %d(%d)\n", (int) be->mvc->emode, (int) be->mvc->emod);
1073#endif
1074 m = be->mvc;
1075 m->type = Q_PARSE;
1076 /* clean up old stuff */
1077 q = m->query;
1078 m->query = NULL;
1079 GDKfree(q); /* may be NULL */
1080
1081 if (be->language != 'X') {
1082 if ((msg = SQLtrans(m)) != MAL_SUCCEED) {
1083 c->mode = FINISHCLIENT;
1084 return msg;
1085 }
1086 }
1087 pstatus = m->session->status;
1088
1089 /* sqlparse needs sql allocator to be available. It can be NULL at
1090 * this point if this is a recursive call. */
1091 if (!m->sa)
1092 m->sa = sa_create();
1093 if (!m->sa) {
1094 c->mode = FINISHCLIENT;
1095 throw(SQL, "SQLparser", SQLSTATE(HY001) MAL_MALLOC_FAIL " for SQL allocator");
1096 }
1097
1098 m->emode = m_normal;
1099 m->emod = mod_none;
1100 if (be->language == 'X') {
1101 int n = 0, v, off, len;
1102
1103 if (strncmp(in->buf + in->pos, "export ", 7) == 0)
1104 n = sscanf(in->buf + in->pos + 7, "%d %d %d", &v, &off, &len);
1105
1106 if (n == 2 || n == 3) {
1107 if (mvc_export_chunk(be, out, v, off, n == 3 ? len : m->reply_size)) {
1108 msg = createException(SQL, "SQLparser", SQLSTATE(45000) "Result set construction failed");
1109 goto finalize;
1110 }
1111
1112 in->pos = in->len; /* HACK: should use parsed length */
1113 return MAL_SUCCEED;
1114 }
1115 if (strncmp(in->buf + in->pos, "close ", 6) == 0) {
1116 res_table *t;
1117
1118 v = (int) strtol(in->buf + in->pos + 6, NULL, 0);
1119 t = res_tables_find(m->results, v);
1120 if (t)
1121 m->results = res_tables_remove(m->results, t);
1122 in->pos = in->len; /* HACK: should use parsed length */
1123 return MAL_SUCCEED;
1124 }
1125 if (strncmp(in->buf + in->pos, "release ", 8) == 0) {
1126 cq *q = NULL;
1127
1128 v = (int) strtol(in->buf + in->pos + 8, NULL, 0);
1129 if ((q = qc_find(m->qc, v)) != NULL)
1130 qc_delete(m->qc, q);
1131 in->pos = in->len; /* HACK: should use parsed length */
1132 return MAL_SUCCEED;
1133 }
1134 if (strncmp(in->buf + in->pos, "auto_commit ", 12) == 0) {
1135 int commit;
1136 v = (int) strtol(in->buf + in->pos + 12, NULL, 10);
1137 commit = (!m->session->auto_commit && v);
1138 m->session->auto_commit = (v) != 0;
1139 m->session->ac_on_commit = m->session->auto_commit;
1140 if (m->session->tr->active) {
1141 if (commit) {
1142 msg = mvc_commit(m, 0, NULL, true);
1143 } else {
1144 msg = mvc_rollback(m, 0, NULL, true);
1145 }
1146 }
1147 in->pos = in->len; /* HACK: should use parsed length */
1148 if (msg != NULL)
1149 goto finalize;
1150 return MAL_SUCCEED;
1151 }
1152 if (strncmp(in->buf + in->pos, "reply_size ", 11) == 0) {
1153 v = (int) strtol(in->buf + in->pos + 11, NULL, 10);
1154 if (v < -1) {
1155 msg = createException(SQL, "SQLparser", SQLSTATE(42000) "Reply_size cannot be negative");
1156 goto finalize;
1157 }
1158 m->reply_size = v;
1159 in->pos = in->len; /* HACK: should use parsed length */
1160 return MAL_SUCCEED;
1161 }
1162 if (strncmp(in->buf + in->pos, "sizeheader", 10) == 0) {
1163 v = (int) strtol(in->buf + in->pos + 10, NULL, 10);
1164 m->sizeheader = v != 0;
1165 in->pos = in->len; /* HACK: should use parsed length */
1166 return MAL_SUCCEED;
1167 }
1168 if (strncmp(in->buf + in->pos, "quit", 4) == 0) {
1169 c->mode = FINISHCLIENT;
1170 return MAL_SUCCEED;
1171 }
1172 msg = createException(SQL, "SQLparser", SQLSTATE(42000) "Unrecognized X command: %s\n", in->buf + in->pos);
1173 goto finalize;
1174 }
1175 if (be->language !='S') {
1176 msg = createException(SQL, "SQLparser", SQLSTATE(42000) "Unrecognized language prefix: %ci\n", be->language);
1177 goto finalize;
1178 }
1179
1180 if ((err = sqlparse(m)) ||
1181 /* Only forget old errors on transaction boundaries */
1182 (mvc_status(m) && m->type != Q_TRANS) || !m->sym) {
1183 if (!err &&m->scanner.started) /* repeat old errors, with a parsed query */
1184 err = mvc_status(m);
1185 if (err && *m->errstr) {
1186 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
1187 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
1188 else
1189 msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
1190 *m->errstr = 0;
1191 }
1192 if (m->sym)
1193 msg = handle_error(m, pstatus, msg);
1194 sqlcleanup(m, err);
1195 goto finalize;
1196 }
1197 assert(m->session->schema != NULL);
1198 /*
1199 * We have dealt with the first parsing step and advanced the input reader
1200 * to the next statement (if any).
1201 * Now is the time to also perform the semantic analysis, optimize and
1202 * produce code.
1203 */
1204 be->q = NULL;
1205 q = query_cleaned(QUERY(m->scanner));
1206 m->query = q;
1207
1208 if (q == NULL) {
1209 err = 1;
1210 msg = createException(PARSE, "SQLparser", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1211 } else if (m->emode == m_execute) {
1212 assert(m->sym->data.lval->h->type == type_int);
1213 be->q = qc_find(m->qc, m->sym->data.lval->h->data.i_val);
1214 if (!be->q) {
1215 err = -1;
1216 msg = createException(SQL, "EXEC", SQLSTATE(07003) "No prepared statement with id: %d\n", m->sym->data.lval->h->data.i_val);
1217 *m->errstr = 0;
1218 msg = handle_error(m, pstatus, msg);
1219 sqlcleanup(m, err);
1220 goto finalize;
1221 } else if (!be->q->prepared) {
1222 err = -1;
1223 msg = createException(SQL, "EXEC", SQLSTATE(07005) "Given handle id is not for a " "prepared statement: %d\n", m->sym->data.lval->h->data.i_val);
1224 *m->errstr = 0;
1225 msg = handle_error(m, pstatus, msg);
1226 sqlcleanup(m, err);
1227 goto finalize;
1228 }
1229 m->type = be->q->type;
1230 scanner_query_processed(&(m->scanner));
1231 } else if (caching(m) && cachable(m, NULL) && m->emode != m_prepare && (be->q = qc_match(m->qc, m, m->sym, m->args, m->argc, m->scanner.key ^ m->session->schema->base.id)) != NULL) {
1232 /* query template was found in the query cache */
1233 scanner_query_processed(&(m->scanner));
1234 m->no_mitosis = be->q->no_mitosis;
1235 } else {
1236 sql_rel *r;
1237
1238 r = sql_symbol2relation(m, m->sym);
1239
1240 if (!r || (err = mvc_status(m) && m->type != Q_TRANS && *m->errstr)) {
1241 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
1242 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
1243 else
1244 msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
1245 *m->errstr = 0;
1246 msg = handle_error(m, pstatus, msg);
1247 sqlcleanup(m, err);
1248 goto finalize;
1249 }
1250
1251 if ((!caching(m) || !cachable(m, r)) && m->emode != m_prepare) {
1252 /* Query template should not be cached */
1253 scanner_query_processed(&(m->scanner));
1254
1255 err = 0;
1256 if (backend_callinline(be, c) < 0 ||
1257 backend_dumpstmt(be, c->curprg->def, r, 1, 0, q) < 0)
1258 err = 1;
1259 else opt = 1;
1260 } else {
1261 /* Add the query tree to the SQL query cache
1262 * and bake a MAL program for it.
1263 */
1264 char *escaped_q;
1265 char qname[IDLENGTH];
1266 be->q = NULL;
1267 (void) snprintf(qname, IDLENGTH, "%c%d_%d", (m->emode == m_prepare?'p':'s'), m->qc->id++, m->qc->clientid);
1268 escaped_q = sql_escape_str(q);
1269 if(!escaped_q) {
1270 err = 1;
1271 msg = createException(PARSE, "SQLparser", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1272 } else {
1273 be->q = qc_insert(m->qc, m->sa, /* the allocator */
1274 r, /* keep relational query */
1275 qname, /* its MAL name) */
1276 m->sym, /* the sql symbol tree */
1277 m->args, /* the argument list */
1278 m->argc, m->scanner.key ^ m->session->schema->base.id, /* the statement hash key */
1279 m->type, /* the type of the statement */
1280 escaped_q,
1281 m->no_mitosis,
1282 m->emode == m_prepare);
1283 }
1284 if(!be->q) {
1285 err = 1;
1286 msg = createException(PARSE, "SQLparser", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1287 }
1288 scanner_query_processed(&(m->scanner));
1289 be->q->code = (backend_code) backend_dumpproc(be, c, be->q, r);
1290 if (!be->q->code)
1291 err = 1;
1292 be->q->stk = 0;
1293
1294 /* passed over to query cache, used during dumpproc */
1295 m->sa = NULL;
1296 m->sym = NULL;
1297 /* register name in the namespace */
1298 be->q->name = putName(be->q->name);
1299 if(!be->q->name) {
1300 err = 1;
1301 msg = createException(PARSE, "SQLparser", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1302 }
1303 }
1304 }
1305 if (err)
1306 m->session->status = -10;
1307 if (err == 0) {
1308 /* no parsing error encountered, finalize the code of the query wrapper */
1309 if (be->q) {
1310 if (m->emode == m_prepare){
1311 /* For prepared queries, return a table with result set structure*/
1312 /* optimize the code block and rename it */
1313 err = mvc_export_prepare(m, c->fdout, be->q, "");
1314 } else if( m->emode == m_execute || m->emode == m_normal || m->emode == m_plan){
1315 /* call procedure generation (only in cache mode) */
1316 backend_call(be, c, be->q);
1317 }
1318 }
1319
1320 pushEndInstruction(c->curprg->def);
1321 /* check the query wrapper for errors */
1322 chkTypes(c->usermodule, c->curprg->def, TRUE);
1323
1324 /* in case we had produced a non-cachable plan, the optimizer should be called */
1325 if (opt ) {
1326 msg = SQLoptimizeQuery(c, c->curprg->def);
1327
1328 if (msg != MAL_SUCCEED) {
1329 sqlcleanup(m, err);
1330 goto finalize;
1331 }
1332 }
1333 //printFunction(c->fdout, c->curprg->def, 0, LIST_MAL_ALL);
1334 /* we know more in this case than chkProgram(c->fdout, c->usermodule, c->curprg->def); */
1335 if (c->curprg->def->errors) {
1336 msg = c->curprg->def->errors;
1337 c->curprg->def->errors = 0;
1338 /* restore the state */
1339 MSresetInstructions(c->curprg->def, oldstop);
1340 freeVariables(c, c->curprg->def, NULL, oldvtop);
1341 if (msg == NULL && *m->errstr){
1342 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
1343 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
1344 else
1345 msg = createException(PARSE, "SQLparser", SQLSTATE(M0M27) "Semantic errors %s", m->errstr);
1346 *m->errstr = 0;
1347 } else if (msg) {
1348 str newmsg;
1349 newmsg = createException(PARSE, "SQLparser", SQLSTATE(M0M27) "Semantic errors %s", msg);
1350 freeException(msg);
1351 msg = newmsg;
1352 }
1353 }
1354 }
1355finalize:
1356 if (msg) {
1357 sqlcleanup(m, 0);
1358 q = m->query;
1359 m->query = NULL;
1360 GDKfree(q);
1361 }
1362 return msg;
1363}
1364
1365str
1366SQLengine(Client c)
1367{
1368 backend *be = (backend *) c->sqlcontext;
1369 return SQLengineIntern(c, be);
1370}
1371
1372str
1373SQLCacheRemove(Client c, str nme)
1374{
1375 Symbol s;
1376
1377#ifdef _SQL_CACHE_DEBUG
1378 fprintf(stderr, "#SQLCacheRemove %s\n", nme);
1379#endif
1380
1381 s = findSymbolInModule(c->usermodule, nme);
1382 if (s == NULL)
1383 throw(MAL, "cache.remove", SQLSTATE(42000) "internal error, symbol missing\n");
1384 deleteSymbol(c->usermodule, s);
1385 return MAL_SUCCEED;
1386}
1387
1388str
1389SQLcallback(Client c, str msg)
1390{
1391 char *newerr;
1392
1393 if (msg &&
1394 (newerr = GDKmalloc(strlen(msg) + 1)) != NULL) {
1395 /* remove exception decoration */
1396 char *m, *n, *p, *s;
1397 size_t l;
1398
1399 m = msg;
1400 p = newerr;
1401 while (m && *m) {
1402 n = strchr(m, '\n');
1403 s = getExceptionMessageAndState(m);
1404 if (n) {
1405 n++; /* include newline */
1406 l = n - s;
1407 } else {
1408 l = strlen(s);
1409 }
1410 memcpy(p, s, l);
1411 p += l;
1412 m = n;
1413 }
1414 *p = 0;
1415 freeException(msg);
1416 msg = GDKrealloc(newerr, strlen(newerr) + 1);
1417 }
1418 return MALcallback(c, msg);
1419}
1420
1421str
1422SYSupdate_tables(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
1423{
1424 mvc *m = ((backend *) cntxt->sqlcontext)->mvc;
1425
1426 (void) mb;
1427 (void) stk;
1428 (void) pci;
1429
1430 sql_trans_update_tables(m->session->tr, mvc_bind_schema(m, "sys"));
1431 return MAL_SUCCEED;
1432}
1433
1434str
1435SYSupdate_schemas(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
1436{
1437 mvc *m = ((backend *) cntxt->sqlcontext)->mvc;
1438
1439 (void) mb;
1440 (void) stk;
1441 (void) pci;
1442
1443 sql_trans_update_schemas(m->session->tr);
1444 return MAL_SUCCEED;
1445}
1446