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 | */ |
74 | static str |
75 | SQLsetTrace(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 | */ |
202 | static str |
203 | SQLexecutePrepared(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 | |
282 | static str |
283 | SQLrun(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 | */ |
416 | str |
417 | SQLescapeString(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 | |
448 | str |
449 | SQLstatementIntern(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 | */ |
689 | endofcompile: |
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 | |
719 | str |
720 | SQLengineIntern(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 | |
764 | cleanup_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 | |
799 | void SQLdestroyResult(res_table *destroy) { |
800 | res_table_destroy(destroy); |
801 | } |
802 | |
803 | /* a hook is provided to execute relational algebra expressions */ |
804 | str |
805 | RAstatement(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 | |
865 | static int |
866 | is_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 | |
876 | str |
877 | RAstatement2(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 | |