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 * SQL execution
11 * N. Nes, M.L. Kersten
12 */
13/*
14 * Execution of SQL instructions.
15 * Before we are can process SQL statements the global catalog should be initialized.
16 */
17#include "monetdb_config.h"
18#include "mal_backend.h"
19#include "sql_scenario.h"
20#include "sql_result.h"
21#include "sql_gencode.h"
22#include "sql_assert.h"
23#include "sql_execute.h"
24#include "sql_env.h"
25#include "sql_mvc.h"
26#include "sql_user.h"
27#include "sql_optimizer.h"
28#include "sql_datetime.h"
29#include "rel_unnest.h"
30#include "rel_optimizer.h"
31#include "rel_partition.h"
32#include "rel_distribute.h"
33#include "rel_select.h"
34#include "rel_rel.h"
35#include "rel_exp.h"
36#include "rel_dump.h"
37#include "mal_debugger.h"
38#include "mtime.h"
39#include "optimizer.h"
40#include "opt_inline.h"
41#include <unistd.h>
42
43/*
44 * The SQLcompile operation can be used by separate
45 * front-ends to benefit from the SQL functionality.
46 * It expects a string and returns the name of the
47 * corresponding MAL block as it is known in the
48 * SQL_cache, where it can be picked up.
49 * The SQLstatement operation also executes the instruction upon request.
50 *
51 * In both cases the SQL string is handled like an ordinary
52 * user query, following the same optimization paths and
53 * caching.
54 */
55
56/* #define _SQL_COMPILE */
57
58/*
59* BEWARE: SQLstatementIntern only commits after all statements found
60* in expr are executed, when autocommit mode is enabled.
61*
62* The tricky part for this statement is to ensure that the SQL statement
63* is executed within the client context specified. This leads to context juggling.
64*/
65
66/*
67 * The trace operation collects the events in the BATs
68 * and creates a secondary result set upon termination
69 * of the query.
70 *
71 * SQLsetTrace extends the MAL plan with code to collect the events.
72 * from the profile cache and returns it as a secondary resultset.
73 */
74static str
75SQLsetTrace(Client cntxt, MalBlkPtr mb)
76{
77 InstrPtr q, resultset;
78 InstrPtr tbls, cols, types, clen, scale;
79 str msg = MAL_SUCCEED;
80 int k;
81
82 if((msg = startTrace(cntxt)) != MAL_SUCCEED)
83 return msg;
84 clearTrace(cntxt);
85
86 for(k= mb->stop-1; k>0; k--)
87 if( getInstrPtr(mb,k)->token ==ENDsymbol)
88 break;
89 mb->stop=k;
90
91 q= newStmt(mb, profilerRef, stoptraceRef);
92
93 /* cook a new resultSet instruction */
94 resultset = newInstruction(mb,sqlRef, resultSetRef);
95 getArg(resultset,0) = newTmpVariable(mb, TYPE_int);
96
97 /* build table defs */
98 tbls = newStmt(mb,batRef, newRef);
99 setVarType(mb, getArg(tbls,0), newBatType(TYPE_str));
100 tbls = pushType(mb, tbls, TYPE_str);
101
102 q= newStmt(mb,batRef,appendRef);
103 q= pushArgument(mb,q,getArg(tbls,0));
104 q= pushStr(mb,q,".trace");
105 k= getArg(q,0);
106
107 q= newStmt(mb,batRef,appendRef);
108 q= pushArgument(mb,q,k);
109 q= pushStr(mb,q,".trace");
110
111 resultset= pushArgument(mb,resultset, getArg(q,0));
112
113 /* build colum defs */
114 cols = newStmt(mb,batRef, newRef);
115 setVarType(mb, getArg(cols,0), newBatType(TYPE_str));
116 cols = pushType(mb, cols, TYPE_str);
117
118 q= newStmt(mb,batRef,appendRef);
119 q= pushArgument(mb,q,getArg(cols,0));
120 q= pushStr(mb,q,"usec");
121 k= getArg(q,0);
122
123 q= newStmt(mb,batRef,appendRef);
124 q= pushArgument(mb,q, k);
125 q= pushStr(mb,q,"statement");
126
127 resultset= pushArgument(mb,resultset, getArg(q,0));
128
129 /* build type defs */
130 types = newStmt(mb,batRef, newRef);
131 setVarType(mb, getArg(types,0), newBatType(TYPE_str));
132 types = pushType(mb, types, TYPE_str);
133
134 q= newStmt(mb,batRef,appendRef);
135 q= pushArgument(mb,q, getArg(types,0));
136 q= pushStr(mb,q,"bigint");
137 k= getArg(q,0);
138
139 q= newStmt(mb,batRef,appendRef);
140 q= pushArgument(mb,q, k);
141 q= pushStr(mb,q,"clob");
142
143 resultset= pushArgument(mb,resultset, getArg(q,0));
144
145 /* build scale defs */
146 clen = newStmt(mb,batRef, newRef);
147 setVarType(mb, getArg(clen,0), newBatType(TYPE_int));
148 clen = pushType(mb, clen, TYPE_int);
149
150 q= newStmt(mb,batRef,appendRef);
151 q= pushArgument(mb,q, getArg(clen,0));
152 q= pushInt(mb,q,64);
153 k= getArg(q,0);
154
155 q= newStmt(mb,batRef,appendRef);
156 q= pushArgument(mb,q, k);
157 q= pushInt(mb,q,0);
158
159 resultset= pushArgument(mb,resultset, getArg(q,0));
160
161 /* build scale defs */
162 scale = newStmt(mb,batRef, newRef);
163 setVarType(mb, getArg(scale,0), newBatType(TYPE_int));
164 scale = pushType(mb, scale, TYPE_int);
165
166 q= newStmt(mb,batRef,appendRef);
167 q= pushArgument(mb,q, getArg(scale,0));
168 q= pushInt(mb,q,0);
169 k= getArg(q,0);
170
171 q= newStmt(mb,batRef,appendRef);
172 q= pushArgument(mb, q, k);
173 q= pushInt(mb,q,0);
174
175 resultset= pushArgument(mb,resultset, getArg(q,0));
176
177 /* add the ticks column */
178
179 q = newStmt(mb, profilerRef, "getTrace");
180 q = pushStr(mb, q, putName("usec"));
181 resultset= pushArgument(mb,resultset, getArg(q,0));
182
183 /* add the stmt column */
184 q = newStmt(mb, profilerRef, "getTrace");
185 q = pushStr(mb, q, putName("stmt"));
186 resultset= pushArgument(mb,resultset, getArg(q,0));
187
188 pushInstruction(mb,resultset);
189 pushEndInstruction(mb);
190 chkTypes(cntxt->usermodule, mb, TRUE);
191
192 return msg;
193}
194
195/*
196 * Execution of the SQL program is delegated to the MALengine.
197 * Different cases should be distinguished. The default is to
198 * hand over the MAL block derived by the parser for execution.
199 * However, when we received an Execute call, we make a shortcut
200 * and prepare the stack for immediate execution
201 */
202static str
203SQLexecutePrepared(Client c, backend *be, MalBlkPtr mb)
204{
205 mvc *m = be->mvc;
206 int argc, parc;
207 ValPtr *argv, argvbuffer[MAXARG], v;
208 ValRecord *argrec, argrecbuffer[MAXARG];
209 MalStkPtr glb;
210 InstrPtr pci;
211 int i;
212 str ret;
213 cq *q= be->q;
214
215 pci = getInstrPtr(mb, 0);
216 if (pci->argc >= MAXARG){
217 argv = (ValPtr *) GDKmalloc(sizeof(ValPtr) * pci->argc);
218 if( argv == NULL)
219 throw(SQL,"sql.prepare",SQLSTATE(HY001) MAL_MALLOC_FAIL);
220 } else
221 argv = argvbuffer;
222
223 if (pci->retc >= MAXARG){
224 argrec = (ValRecord *) GDKmalloc(sizeof(ValRecord) * pci->retc);
225 if( argrec == NULL){
226 if( argv != argvbuffer)
227 GDKfree(argv);
228 throw(SQL,"sql.prepare",SQLSTATE(HY001) MAL_MALLOC_FAIL);
229 }
230 } else
231 argrec = argrecbuffer;
232
233 /* prepare the target variables */
234 for (i = 0; i < pci->retc; i++) {
235 argv[i] = argrec + i;
236 argv[i]->vtype = getVarGDKType(mb, i);
237 }
238
239 argc = m->argc;
240 parc = q->paramlen;
241
242 if (argc != parc) {
243 if (pci->argc >= MAXARG && argv != argvbuffer)
244 GDKfree(argv);
245 if (pci->retc >= MAXARG && argrec != argrecbuffer)
246 GDKfree(argrec);
247 throw(SQL, "sql.prepare", SQLSTATE(07001) "EXEC: wrong number of arguments for prepared statement: %d, expected %d", argc, parc);
248 } else {
249 for (i = 0; i < m->argc; i++) {
250 atom *arg = m->args[i];
251 sql_subtype *pt = q->params + i;
252
253 if (!atom_cast(m->sa, arg, pt)) {
254 /*sql_error(c, 003, buf); */
255 if (pci->argc >= MAXARG && argv != argvbuffer)
256 GDKfree(argv);
257 if (pci->retc >= MAXARG && argrec != argrecbuffer)
258 GDKfree(argrec);
259 throw(SQL, "sql.prepare", SQLSTATE(07001) "EXEC: wrong type for argument %d of " "prepared statement: %s, expected %s", i + 1, atom_type(arg)->type->sqlname, pt->type->sqlname);
260 }
261 argv[pci->retc + i] = &arg->data;
262 }
263 }
264 glb = (MalStkPtr) (q->stk);
265 ret = callMAL(c, mb, &glb, argv, (m->emod & mod_debug ? 'n' : 0));
266 /* cleanup the arguments */
267 for (i = pci->retc; i < pci->argc; i++) {
268 garbageElement(c, v = &glb->stk[pci->argv[i]]);
269 v->vtype = TYPE_int;
270 v->val.ival = int_nil;
271 }
272 q->stk = (backend_stack) glb; /* save garbageCollected stack */
273 if (glb && SQLdebug & 1)
274 printStack(GDKstdout, mb, glb);
275 if (pci->argc >= MAXARG && argv != argvbuffer)
276 GDKfree(argv);
277 if (pci->retc >= MAXARG && argrec != argrecbuffer)
278 GDKfree(argrec);
279 return ret;
280}
281
282static str
283SQLrun(Client c, backend *be, mvc *m)
284{
285 str msg= MAL_SUCCEED;
286 MalBlkPtr mc = 0, mb=c->curprg->def;
287 InstrPtr p=0;
288 int i,j, retc;
289 ValPtr val;
290
291 if (*m->errstr){
292 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
293 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
294 else
295 msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
296 *m->errstr=0;
297 return msg;
298 }
299 if (m->emode == m_execute && be->q->paramlen != m->argc)
300 throw(SQL, "sql.prepare", SQLSTATE(42000) "EXEC called with wrong number of arguments: expected %d, got %d", be->q->paramlen, m->argc);
301 MT_thread_setworking(m->query);
302 // locate and inline the query template instruction
303 mb = copyMalBlk(c->curprg->def);
304 if (!mb) {
305 MT_thread_setworking(NULL);
306 throw(SQL, "sql.prepare", SQLSTATE(HY001) MAL_MALLOC_FAIL);
307 }
308 mb->history = c->curprg->def->history;
309 c->curprg->def->history = 0;
310
311 /* only consider a re-optimization when we are dealing with query templates */
312 for ( i= 1; i < mb->stop;i++){
313 p = getInstrPtr(mb,i);
314 if( getFunctionId(p) && qc_isapreparedquerytemplate(getFunctionId(p) ) ){
315 msg = SQLexecutePrepared(c, be, p->blk);
316 freeMalBlk(mb);
317 MT_thread_setworking(NULL);
318 return msg;
319 }
320 if( getFunctionId(p) && p->blk && qc_isaquerytemplate(getFunctionId(p)) ) {
321 mc = copyMalBlk(p->blk);
322 if (!mc) {
323 freeMalBlk(mb);
324 MT_thread_setworking(NULL);
325 throw(SQL, "sql.prepare", SQLSTATE(HY001) MAL_MALLOC_FAIL);
326 }
327 retc = p->retc;
328 freeMalBlk(mb); // TODO can be factored out
329 mb = mc;
330 // declare the argument values as a constant
331 // We use the knowledge that the arguments are first on the stack
332 for (j = 0; j < m->argc; j++) {
333 sql_subtype *pt = be->q->params + j;
334 atom *arg = m->args[j];
335
336 if (!atom_cast(m->sa, arg, pt)) {
337 freeMalBlk(mb);
338 MT_thread_setworking(NULL);
339 throw(SQL, "sql.prepare", SQLSTATE(07001) "EXEC: wrong type for argument %d of " "query template : %s, expected %s", i + 1, atom_type(arg)->type->sqlname, pt->type->sqlname);
340 }
341 val= (ValPtr) &arg->data;
342 if (VALcopy(&mb->var[j+retc].value, val) == NULL){
343 freeMalBlk(mb);
344 MT_thread_setworking(NULL);
345 throw(MAL, "sql.prepare", SQLSTATE(HY001) MAL_MALLOC_FAIL);
346 }
347 setVarConstant(mb, j+retc);
348 setVarFixed(mb, j+retc);
349 }
350 mb->stmt[0]->argc = 1;
351 break;
352 }
353 }
354 // JIT optimize the SQL query using all current information
355 // This include template constants, BAT sizes.
356 if( m->emod & mod_debug)
357 mb->keephistory = TRUE;
358 msg = SQLoptimizeQuery(c, mb);
359 if( msg != MAL_SUCCEED){
360 // freeMalBlk(mb);
361 MT_thread_setworking(NULL);
362 return msg;
363 }
364 mb->keephistory = FALSE;
365
366 if (mb->errors){
367 // freeMalBlk(mb);
368 // mal block might be so broken free causes segfault
369 msg = mb->errors;
370 mb->errors = 0;
371 MT_thread_setworking(NULL);
372 return msg;
373 }
374
375 if (m->emod & mod_explain) {
376 if (c->curprg->def)
377 printFunction(c->fdout, mb, 0, LIST_MAL_NAME | LIST_MAL_VALUE | LIST_MAL_TYPE | LIST_MAL_MAPI);
378 } else if( m->emod & mod_debug) {
379 c->idle = 0;
380 c->lastcmd = time(0);
381 msg = runMALDebugger(c, mb);
382 } else {
383 if( m->emod & mod_trace){
384 if((msg = SQLsetTrace(c,mb)) == MAL_SUCCEED) {
385 c->idle = 0;
386 c->lastcmd = time(0);
387 msg = runMAL(c, mb, 0, 0);
388 stopTrace(c);
389 }
390 } else {
391 c->idle = 0;
392 c->lastcmd = time(0);
393 msg = runMAL(c, mb, 0, 0);
394 }
395 }
396 /* after the query has been finished we enter the idle state */
397 c->idle = time(0);
398 c->lastcmd = 0;
399 // release the resources
400 freeMalBlk(mb);
401 MT_thread_setworking(NULL);
402 return msg;
403}
404
405/*
406 * Escape single quotes and backslashes. This is important to do before calling
407 * SQLstatementIntern, if we are pasting user provided strings into queries
408 * passed to the SQLstatementIntern. Otherwise we open ourselves to SQL
409 * injection attacks.
410 *
411 * It returns the input string with all the single quotes(') and the backslashes
412 * (\) doubled, or NULL, if it could not allocate enough space.
413 *
414 * The caller is responsible to free the returned value.
415 */
416str
417SQLescapeString(str s)
418{
419 str ret = NULL;
420 char *p, *q;
421 size_t len = 0;
422
423 if(!s) {
424 return NULL;
425 }
426
427 /* At most we will need 2*strlen(s) + 1 characters */
428 len = strlen(s);
429 ret = (str)GDKmalloc(2*len + 1);
430 if (!ret) {
431 return NULL;
432 }
433
434 for (p = s, q = ret; *p != '\0'; p++, q++) {
435 *q = *p;
436 if (*p == '\'') {
437 *(++q) = '\'';
438 }
439 else if (*p == '\\') {
440 *(++q) = '\\';
441 }
442 }
443
444 *q = '\0';
445 return ret;
446}
447
448str
449SQLstatementIntern(Client c, str *expr, str nme, bit execute, bit output, res_table **result)
450{
451 int status = 0;
452 int err = 0;
453 mvc *o, *m;
454 int ac, sizevars, topvars;
455 sql_var *vars;
456 int oldvtop, oldstop = 1;
457 buffer *b;
458 char *n;
459 bstream *bs;
460 stream *buf;
461 str msg = MAL_SUCCEED;
462 backend *be, *sql = (backend *) c->sqlcontext;
463 size_t len = strlen(*expr);
464 int inited = 0;
465
466#ifdef _SQL_COMPILE
467 mnstr_printf(c->fdout, "#SQLstatement:%s\n", *expr);
468#endif
469 if (!sql) {
470 inited = 1;
471 msg = SQLinitClient(c);
472 sql = (backend *) c->sqlcontext;
473 }
474 if (msg){
475 freeException(msg);
476 throw(SQL, "sql.statement", SQLSTATE(HY002) "Catalogue not available");
477 }
478
479 m = sql->mvc;
480 ac = m->session->auto_commit;
481 o = MNEW(mvc);
482 if (!o) {
483 if (inited)
484 SQLresetClient(c);
485 throw(SQL, "sql.statement", SQLSTATE(HY001) MAL_MALLOC_FAIL);
486 }
487 *o = *m;
488 /* hide query cache, this causes crashes in SQLtrans() due to uninitialized memory otherwise */
489 m->qc = NULL;
490
491 /* create private allocator */
492 m->sa = NULL;
493 if ((msg = SQLtrans(m)) != MAL_SUCCEED) {
494 if (inited)
495 SQLresetClient(c);
496 return msg;
497 }
498 status = m->session->status;
499
500 m->type = Q_PARSE;
501 be = sql;
502 sql = backend_create(m, c);
503 if( sql == NULL)
504 throw(SQL,"sql.statement",SQLSTATE(HY001) MAL_MALLOC_FAIL);
505 sql->output_format = be->output_format;
506 if (!output) {
507 sql->output_format = OFMT_NONE;
508 }
509 sql->depth++;
510 // and do it again
511 m->qc = NULL;
512 m->caching = 0;
513 m->user_id = m->role_id = USER_MONETDB;
514 if (result)
515 m->reply_size = -2; /* do not clean up result tables */
516
517 /* mimic a client channel on which the query text is received */
518 b = (buffer *) GDKmalloc(sizeof(buffer));
519 if( b == NULL)
520 throw(SQL,"sql.statement", SQLSTATE(HY001) MAL_MALLOC_FAIL);
521 n = GDKmalloc(len + 1 + 1);
522 if( n == NULL) {
523 GDKfree(b);
524 throw(SQL,"sql.statement", SQLSTATE(HY001) MAL_MALLOC_FAIL);
525 }
526 strncpy(n, *expr, len);
527 n[len] = '\n';
528 n[len + 1] = 0;
529 len++;
530 buffer_init(b, n, len);
531 buf = buffer_rastream(b, "sqlstatement");
532 if(buf == NULL) {
533 buffer_destroy(b);//n and b will be freed by the buffer
534 throw(SQL,"sql.statement",SQLSTATE(HY001) MAL_MALLOC_FAIL);
535 }
536 bs = bstream_create(buf, b->len);
537 if(bs == NULL) {
538 buffer_destroy(b);//n and b will be freed by the buffer
539 throw(SQL,"sql.statement",SQLSTATE(HY001) MAL_MALLOC_FAIL);
540 }
541 scanner_init(&m->scanner, bs, NULL);
542 m->scanner.mode = LINE_N;
543 bstream_next(m->scanner.rs);
544
545 m->params = NULL;
546 m->argc = 0;
547 m->session->auto_commit = 0;
548 if (!m->sa)
549 m->sa = sa_create();
550 if (!m->sa) {
551 *m = *o;
552 _DELETE(o);
553 bstream_destroy(m->scanner.rs);
554 throw(SQL,"sql.statement",SQLSTATE(HY001) MAL_MALLOC_FAIL);
555 }
556
557 /*
558 * System has been prepared to parse it and generate code.
559 * Scan the complete string for SQL statements, stop at the first error.
560 */
561 c->sqlcontext = sql;
562 while (msg == MAL_SUCCEED && m->scanner.rs->pos < m->scanner.rs->len) {
563 sql_rel *r;
564
565 if (!m->sa)
566 m->sa = sa_create();
567 if (!m->sa) {
568 msg = createException(PARSE, "SQLparser",SQLSTATE(HY001) MAL_MALLOC_FAIL);
569 goto endofcompile;
570 }
571 m->sym = NULL;
572 if ((err = sqlparse(m)) ||
573 /* Only forget old errors on transaction boundaries */
574 (mvc_status(m) && m->type != Q_TRANS) || !m->sym) {
575 if (!err)
576 err = mvc_status(m);
577 if (*m->errstr){
578 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
579 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
580 else
581 msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
582 *m->errstr = 0;
583 }
584 sqlcleanup(m, err);
585 execute = 0;
586 if (!err)
587 continue;
588 goto endofcompile;
589 }
590
591 /*
592 * We have dealt with the first parsing step and advanced the input reader
593 * to the next statement (if any).
594 * Now is the time to also perform the semantic analysis,
595 * optimize and produce code.
596 * We don't search the cache for a previous incarnation yet.
597 */
598 if((msg = MSinitClientPrg(c, "user", nme)) != MAL_SUCCEED) {
599 goto endofcompile;
600 }
601 oldvtop = c->curprg->def->vtop;
602 oldstop = c->curprg->def->stop;
603 r = sql_symbol2relation(m, m->sym);
604#ifdef _SQL_COMPILE
605 mnstr_printf(c->fdout, "#SQLstatement:\n");
606#endif
607 scanner_query_processed(&(m->scanner));
608 if ((err = mvc_status(m)) ) {
609 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
610 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
611 else
612 msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
613 *m->errstr=0;
614 msg = handle_error(m, status, msg);
615 sqlcleanup(m, err);
616 /* restore the state */
617 MSresetInstructions(c->curprg->def, oldstop);
618 freeVariables(c, c->curprg->def, c->glb, oldvtop);
619 c->curprg->def->errors = 0;
620 goto endofcompile;
621 }
622 /* generate MAL code */
623#ifdef _SQL_COMPILE
624 mnstr_printf(c->fdout, "#SQLstatement:pre-compile\n");
625 printFunction(c->fdout, c->curprg->def, 0, LIST_MAL_NAME | LIST_MAL_VALUE | LIST_MAL_MAPI);
626#endif
627 be->depth++;
628 if (backend_callinline(be, c) < 0 ||
629 backend_dumpstmt(be, c->curprg->def, r, 1, 1, NULL) < 0)
630 err = 1;
631 be->depth--;
632#ifdef _SQL_COMPILE
633 mnstr_printf(c->fdout, "#SQLstatement:post-compile\n");
634 printFunction(c->fdout, c->curprg->def, 0, LIST_MAL_NAME | LIST_MAL_VALUE | LIST_MAL_MAPI);
635#endif
636 msg = SQLoptimizeFunction(c, c->curprg->def);
637
638 if (err || c->curprg->def->errors || msg) {
639 /* restore the state */
640 char *error = NULL;
641 MSresetInstructions(c->curprg->def, oldstop);
642 freeVariables(c, c->curprg->def, c->glb, oldvtop);
643 c->curprg->def->errors = 0;
644 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
645 error = createException(PARSE, "SQLparser", "%s", m->errstr);
646 else if (*m->errstr)
647 error = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
648 else
649 error = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", msg);
650 if (msg)
651 freeException(msg);
652 msg = error;
653 *m->errstr = 0;
654 goto endofcompile;
655 }
656#ifdef _SQL_COMPILE
657 mnstr_printf(c->fdout, "#result of sql.eval()\n");
658 printFunction(c->fdout, c->curprg->def, 0, c->listing);
659#endif
660
661 if (!output)
662 sql->out = NULL; /* no output stream */
663 be->depth++;
664 if (execute)
665 msg = SQLrun(c,be,m);
666 be->depth--;
667 MSresetInstructions(c->curprg->def, oldstop);
668 freeVariables(c, c->curprg->def, NULL, oldvtop);
669 sqlcleanup(m, 0);
670 if (!execute)
671 goto endofcompile;
672#ifdef _SQL_COMPILE
673 mnstr_printf(c->fdout, "#parse/execute result %d\n", err);
674#endif
675 }
676 if (m->results) {
677 if (result) { /* return all results sets */
678 *result = m->results;
679 } else {
680 if (m->results == o->results)
681 o->results = NULL;
682 res_tables_destroy(m->results);
683 }
684 m->results = NULL;
685 }
686/*
687 * We are done; a MAL procedure resides in the cache.
688 */
689endofcompile:
690 if (execute)
691 MSresetInstructions(c->curprg->def, 1);
692
693 c->sqlcontext = be;
694 backend_destroy(sql);
695 GDKfree(n);
696 GDKfree(b);
697 bstream_destroy(m->scanner.rs);
698 if (m->sa)
699 sa_destroy(m->sa);
700 m->sa = NULL;
701 m->sym = NULL;
702 /* variable stack maybe resized, ie we need to keep the new stack */
703 status = m->session->status;
704 sizevars = m->sizevars;
705 topvars = m->topvars;
706 vars = m->vars;
707 *m = *o;
708 _DELETE(o);
709 m->sizevars = sizevars;
710 m->topvars = topvars;
711 m->vars = vars;
712 m->session->status = status;
713 m->session->auto_commit = ac;
714 if (inited)
715 SQLresetClient(c);
716 return msg;
717}
718
719str
720SQLengineIntern(Client c, backend *be)
721{
722 str msg = MAL_SUCCEED;
723 char oldlang = be->language;
724 mvc *m = be->mvc;
725
726 if (oldlang == 'X') { /* return directly from X-commands */
727 sqlcleanup(be->mvc, 0);
728 return MAL_SUCCEED;
729 }
730
731#ifdef SQL_SCENARIO_DEBUG
732 fprintf(stderr, "#Ready to execute SQL statement\n");
733#endif
734
735 if (c->curprg->def->stop == 1) {
736 if (mvc_status(m)) {
737 if (*m->errstr){
738 if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
739 msg = createException(PARSE, "SQLparser", "%s", m->errstr);
740 else
741 msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
742 *m->errstr = 0;
743 }
744 goto cleanup_engine;
745 }
746 sqlcleanup(be->mvc, 0);
747 return MAL_SUCCEED;
748 }
749
750 if (m->emode == m_prepare)
751 goto cleanup_engine;
752
753 be->language = 'D';
754 /*
755 * The code below is copied from MALengine, which handles execution
756 * in the context of a user global environment. We have a private
757 * environment.
758 */
759 if (MALcommentsOnly(c->curprg->def))
760 msg = MAL_SUCCEED;
761 else
762 msg = SQLrun(c,be,m);
763
764cleanup_engine:
765 if (m->type == Q_SCHEMA && m->qc != NULL)
766 qc_clean(m->qc);
767 if (msg) {
768 /* don't print exception decoration, just the message */
769/*
770 char *n = NULL;
771 char *o = msg;
772 while ((n = strchr(o, '\n')) != NULL) {
773 *n = '\0';
774 mnstr_printf(c->fdout, "!%s\n", getExceptionMessage(o));
775 *n++ = '\n';
776 o = n;
777 }
778 if (*o != 0)
779 mnstr_printf(c->fdout, "!%s\n", getExceptionMessage(o));
780*/
781 m->session->status = -10;
782 }
783
784 if (m->type != Q_SCHEMA && be->q && msg) {
785 qc_delete(m->qc, be->q);
786 }
787 be->q = NULL;
788 sqlcleanup(be->mvc, (!msg) ? 0 : -1);
789 MSresetInstructions(c->curprg->def, 1);
790 freeVariables(c, c->curprg->def, NULL, be->vtop);
791 be->language = oldlang;
792 /*
793 * Any error encountered during execution should block further processing
794 * unless auto_commit has been set.
795 */
796 return msg;
797}
798
799void SQLdestroyResult(res_table *destroy) {
800 res_table_destroy(destroy);
801}
802
803/* a hook is provided to execute relational algebra expressions */
804str
805RAstatement(Client c, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
806{
807 int pos = 0;
808 str *expr = getArgReference_str(stk, pci, 1);
809 bit *opt = getArgReference_bit(stk, pci, 2);
810 backend *b = NULL;
811 mvc *m = NULL;
812 str msg;
813 sql_rel *rel;
814 list *refs;
815
816 if ((msg = getSQLContext(c, mb, &m, &b)) != NULL)
817 return msg;
818 if ((msg = checkSQLContext(c)) != NULL)
819 return msg;
820 if ((msg = SQLtrans(m)) != MAL_SUCCEED)
821 return msg;
822 if (!m->sa)
823 m->sa = sa_create();
824 if (!m->sa)
825 return createException(SQL,"RAstatement",SQLSTATE(HY001) MAL_MALLOC_FAIL);
826 refs = sa_list(m->sa);
827 rel = rel_read(m, *expr, &pos, refs);
828 if (rel) {
829 int oldvtop = c->curprg->def->vtop;
830 int oldstop = c->curprg->def->stop;
831
832 if (*opt) {
833 rel = rel_unnest(m, rel);
834 rel = rel_optimizer(m, rel, 0);
835 }
836
837 if ((msg = MSinitClientPrg(c, "user", "test")) != MAL_SUCCEED) {
838 rel_destroy(rel);
839 return msg;
840 }
841
842 /* generate MAL code, ignoring any code generation error */
843 if (backend_callinline(b, c) < 0 ||
844 backend_dumpstmt(b, c->curprg->def, rel, 1, 1, NULL) < 0) {
845 msg = createException(SQL,"RAstatement","Program contains errors"); // TODO: use macro definition.
846 } else {
847 SQLaddQueryToCache(c);
848 msg = SQLoptimizeFunction(c,c->curprg->def);
849 }
850 rel_destroy(rel);
851 if( msg == MAL_SUCCEED)
852 msg = SQLrun(c,b,m);
853 if (!msg) {
854 resetMalBlk(c->curprg->def, oldstop);
855 freeVariables(c, c->curprg->def, NULL, oldvtop);
856 }
857 if (!msg)
858 msg = mvc_commit(m, 0, NULL, false);
859 else
860 msg = mvc_rollback(m, 0, NULL, false);
861 }
862 return msg;
863}
864
865static int
866is_a_number(char *v)
867{
868 while(*v) {
869 if (!isdigit((unsigned char) *v))
870 return 0;
871 v++;
872 }
873 return 1;
874}
875
876str
877RAstatement2(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
878{
879 int pos = 0;
880 str *mod = getArgReference_str(stk, pci, 1);
881 str *nme = getArgReference_str(stk, pci, 2);
882 str *expr = getArgReference_str(stk, pci, 3);
883 str *sig = getArgReference_str(stk, pci, 4), c = *sig;
884 backend *be = NULL;
885 mvc *m = NULL;
886 str msg;
887 sql_rel *rel;
888 list *refs, *ops;
889 char buf[BUFSIZ];
890
891 if ((msg = getSQLContext(cntxt, mb, &m, &be)) != NULL)
892 return msg;
893 if ((msg = checkSQLContext(cntxt)) != NULL)
894 return msg;
895 if ((msg = SQLtrans(m)) != MAL_SUCCEED)
896 return msg;
897 if (!m->sa)
898 m->sa = sa_create();
899 if (!m->sa)
900 return createException(SQL,"RAstatement2",SQLSTATE(HY001) MAL_MALLOC_FAIL);
901
902 /* keep copy of signature and relational expression */
903 snprintf(buf, BUFSIZ, "%s %s", *sig, *expr);
904
905 if(!stack_push_frame(m, NULL))
906 return createException(SQL,"RAstatement2",SQLSTATE(HY001) MAL_MALLOC_FAIL);
907 ops = sa_list(m->sa);
908 while (c && *c && !isspace((unsigned char) *c)) {
909 char *vnme = c, *tnme;
910 char *p = strchr(++c, (int)' ');
911 int d,s,nr = -1;
912 sql_subtype t;
913 atom *a;
914
915 *p++ = 0;
916 /* vnme can be name or number */
917 if (is_a_number(vnme+1))
918 nr = strtol(vnme+1, NULL, 10);
919 tnme = p;
920 p = strchr(p, (int)'(');
921 *p++ = 0;
922 tnme = sa_strdup(m->sa, tnme);
923 if (!tnme) {
924 stack_pop_frame(m);
925 return createException(SQL,"RAstatement2",SQLSTATE(HY001) MAL_MALLOC_FAIL);
926 }
927 d = strtol(p, &p, 10);
928 p++; /* skip , */
929 s = strtol(p, &p, 10);
930
931 sql_find_subtype(&t, tnme, d, s);
932 a = atom_general(m->sa, &t, NULL);
933 /* the argument list may have holes and maybe out of order, ie
934 * don't use sql_add_arg, but special numbered version
935 * sql_set_arg(m, a, nr);
936 * */
937 if (nr >= 0) {
938 append(ops, exp_atom_ref(m->sa, nr, &t));
939 if(!sql_set_arg(m, nr, a)) {
940 stack_pop_frame(m);
941 return createException(SQL,"RAstatement2",SQLSTATE(HY001) MAL_MALLOC_FAIL);
942 }
943 } else {
944 if(!stack_push_var(m, vnme+1, &t)) {
945 stack_pop_frame(m);
946 return createException(SQL,"RAstatement2",SQLSTATE(HY001) MAL_MALLOC_FAIL);
947 }
948 append(ops, exp_var(m->sa, sa_strdup(m->sa, vnme+1), &t, m->frame));
949 }
950 c = strchr(p, (int)',');
951 if (c)
952 c++;
953 }
954 refs = sa_list(m->sa);
955 rel = rel_read(m, *expr, &pos, refs);
956 stack_pop_frame(m);
957 if (rel)
958 rel = rel_unnest(m, rel);
959 if (rel)
960 rel = rel_optimizer(m, rel, 0);
961 if (!rel || monet5_create_relational_function(m, *mod, *nme, rel, NULL, ops, 0) < 0)
962 throw(SQL, "sql.register", SQLSTATE(42000) "Cannot register %s", buf);
963 rel_destroy(rel);
964 sqlcleanup(m, 0);
965 return msg;
966}
967