Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * SQL execution
15 : * N. Nes, M.L. Kersten
16 : */
17 : /*
18 : * Execution of SQL instructions.
19 : * Before we are can process SQL statements the global catalog should be initialized.
20 : */
21 : #include "monetdb_config.h"
22 : #include "mal_backend.h"
23 : #include "sql_scenario.h"
24 : #include "sql_result.h"
25 : #include "sql_gencode.h"
26 : #include "sql_assert.h"
27 : #include "sql_execute.h"
28 : #include "sql_env.h"
29 : #include "sql_mvc.h"
30 : #include "sql_user.h"
31 : #include "sql_optimizer.h"
32 : #include "sql_datetime.h"
33 : #include "rel_select.h"
34 : #include "rel_rel.h"
35 : #include "rel_exp.h"
36 : #include "rel_dump.h"
37 : #include "gdk_time.h"
38 : #include "optimizer.h"
39 : #include "opt_inline.h"
40 : #include <unistd.h>
41 :
42 : /* #define _SQL_COMPILE */
43 :
44 : /*
45 : * BEWARE: SQLstatementIntern only commits after all statements found
46 : * in expr are executed, when autocommit mode is enabled.
47 : *
48 : * The tricky part for this statement is to ensure that the SQL statement
49 : * is executed within the client context specified. This leads to context juggling.
50 : */
51 :
52 : /*
53 : * The trace operation collects the events in the BATs
54 : * and creates a secondary result set upon termination
55 : * of the query.
56 : *
57 : * SQLsetTrace extends the MAL plan with code to collect the events.
58 : * from the profile cache and returns it as a secondary resultset.
59 : */
60 : static str
61 4 : SQLsetTrace(Client cntxt, MalBlkPtr mb)
62 : {
63 4 : InstrPtr q, resultset;
64 4 : InstrPtr tbls, cols, types, clen, scale;
65 4 : str msg = MAL_SUCCEED;
66 4 : int k;
67 :
68 4 : if((msg = startTrace(cntxt)) != MAL_SUCCEED)
69 : return msg;
70 4 : clearTrace(cntxt);
71 :
72 126 : for(k = mb->stop-1; k>0; k--)
73 126 : if( getInstrPtr(mb,k)->token ==ENDsymbol)
74 : break;
75 4 : mb->stop = k;
76 :
77 4 : q = newStmt(mb, profilerRef, stoptraceRef);
78 4 : if (q == NULL) {
79 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
80 : }
81 4 : pushInstruction(mb, q);
82 :
83 : /* cook a new resultSet instruction */
84 4 : resultset = newInstruction(mb,sqlRef, resultSetRef);
85 4 : if (resultset == NULL) {
86 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
87 : }
88 4 : getArg(resultset,0) = newTmpVariable(mb, TYPE_int);
89 :
90 : /* build table defs */
91 4 : tbls = newStmt(mb,batRef, newRef);
92 4 : if (tbls == NULL) {
93 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
94 : }
95 4 : setVarType(mb, getArg(tbls,0), newBatType(TYPE_str));
96 4 : tbls = pushType(mb, tbls, TYPE_str);
97 4 : pushInstruction(mb, tbls);
98 :
99 4 : q = newStmt(mb,batRef,appendRef);
100 4 : if (q == NULL) {
101 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
102 : }
103 4 : q = pushArgument(mb,q,getArg(tbls,0));
104 4 : q = pushStr(mb,q,".trace");
105 4 : k = getArg(q,0);
106 4 : pushInstruction(mb, q);
107 :
108 4 : q = newStmt(mb,batRef,appendRef);
109 4 : if (q == NULL) {
110 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
111 : }
112 4 : q = pushArgument(mb,q,k);
113 4 : q = pushStr(mb,q,".trace");
114 4 : pushInstruction(mb, q);
115 :
116 4 : resultset = pushArgument(mb,resultset, getArg(q,0));
117 :
118 : /* build colum defs */
119 4 : cols = newStmt(mb,batRef, newRef);
120 4 : if (cols == NULL) {
121 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
122 : }
123 4 : setVarType(mb, getArg(cols,0), newBatType(TYPE_str));
124 4 : cols = pushType(mb, cols, TYPE_str);
125 4 : pushInstruction(mb, cols);
126 :
127 4 : q = newStmt(mb,batRef,appendRef);
128 4 : if (q == NULL) {
129 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
130 : }
131 4 : q = pushArgument(mb,q,getArg(cols,0));
132 4 : q = pushStr(mb,q,"usec");
133 4 : k = getArg(q,0);
134 4 : pushInstruction(mb, q);
135 :
136 4 : q = newStmt(mb,batRef,appendRef);
137 4 : if (q == NULL) {
138 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
139 : }
140 4 : q = pushArgument(mb,q, k);
141 4 : q = pushStr(mb,q,"statement");
142 4 : pushInstruction(mb, q);
143 :
144 4 : resultset = pushArgument(mb,resultset, getArg(q,0));
145 :
146 : /* build type defs */
147 4 : types = newStmt(mb,batRef, newRef);
148 4 : if (types == NULL) {
149 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
150 : }
151 4 : setVarType(mb, getArg(types,0), newBatType(TYPE_str));
152 4 : types = pushType(mb, types, TYPE_str);
153 4 : pushInstruction(mb, types);
154 :
155 4 : q = newStmt(mb,batRef,appendRef);
156 4 : if (q == NULL) {
157 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
158 : }
159 4 : q = pushArgument(mb,q, getArg(types,0));
160 4 : q = pushStr(mb,q,"bigint");
161 4 : k = getArg(q,0);
162 4 : pushInstruction(mb, q);
163 :
164 4 : q = newStmt(mb,batRef,appendRef);
165 4 : if (q == NULL) {
166 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
167 : }
168 4 : q = pushArgument(mb,q, k);
169 4 : q = pushStr(mb,q,"clob");
170 4 : pushInstruction(mb, q);
171 :
172 4 : resultset = pushArgument(mb,resultset, getArg(q,0));
173 :
174 : /* build scale defs */
175 4 : clen = newStmt(mb,batRef, newRef);
176 4 : if (clen == NULL) {
177 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
178 : }
179 4 : setVarType(mb, getArg(clen,0), newBatType(TYPE_int));
180 4 : clen = pushType(mb, clen, TYPE_int);
181 4 : pushInstruction(mb, clen);
182 :
183 4 : q = newStmt(mb,batRef,appendRef);
184 4 : if (q == NULL) {
185 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
186 : }
187 4 : q = pushArgument(mb,q, getArg(clen,0));
188 4 : q = pushInt(mb,q,64);
189 4 : k = getArg(q,0);
190 4 : pushInstruction(mb, q);
191 :
192 4 : q = newStmt(mb,batRef,appendRef);
193 4 : if (q == NULL) {
194 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
195 : }
196 4 : q = pushArgument(mb,q, k);
197 4 : q = pushInt(mb,q,0);
198 4 : pushInstruction(mb, q);
199 :
200 4 : resultset = pushArgument(mb,resultset, getArg(q,0));
201 :
202 : /* build scale defs */
203 4 : scale = newStmt(mb,batRef, newRef);
204 4 : if (scale == NULL) {
205 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
206 : }
207 4 : setVarType(mb, getArg(scale,0), newBatType(TYPE_int));
208 4 : scale = pushType(mb, scale, TYPE_int);
209 4 : pushInstruction(mb, scale);
210 :
211 4 : q = newStmt(mb,batRef,appendRef);
212 4 : if (q == NULL) {
213 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
214 : }
215 4 : q = pushArgument(mb,q, getArg(scale,0));
216 4 : q = pushInt(mb,q,0);
217 4 : k = getArg(q,0);
218 4 : pushInstruction(mb, q);
219 :
220 4 : q = newStmt(mb,batRef,appendRef);
221 4 : if (q == NULL) {
222 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
223 : }
224 4 : q = pushArgument(mb, q, k);
225 4 : q = pushInt(mb,q,0);
226 4 : pushInstruction(mb, q);
227 :
228 4 : resultset = pushArgument(mb,resultset, getArg(q,0));
229 :
230 : /* add the ticks column */
231 :
232 4 : q = newStmt(mb, profilerRef, getTraceRef);
233 4 : if (q == NULL) {
234 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
235 : }
236 4 : q = pushStr(mb, q, putName("usec"));
237 4 : resultset = pushArgument(mb,resultset, getArg(q,0));
238 4 : pushInstruction(mb, q);
239 :
240 : /* add the stmt column */
241 4 : q = newStmt(mb, profilerRef, getTraceRef);
242 4 : if (q == NULL) {
243 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
244 : }
245 4 : q = pushStr(mb, q, putName("stmt"));
246 4 : resultset = pushArgument(mb,resultset, getArg(q,0));
247 4 : pushInstruction(mb, q);
248 :
249 4 : pushInstruction(mb,resultset);
250 4 : pushEndInstruction(mb);
251 4 : msg = chkTypes(cntxt->usermodule, mb, TRUE);
252 4 : renameVariables(mb);
253 4 : return msg;
254 : }
255 :
256 : str
257 543723 : SQLrun(Client c, mvc *m)
258 : {
259 543723 : str msg = MAL_SUCCEED;
260 543723 : MalBlkPtr mb = c->curprg->def;
261 :
262 543723 : assert(!*m->errstr);
263 :
264 543723 : TRC_INFO(SQL_EXECUTION, "Executing: %s", c->query);
265 543725 : MT_thread_setworking(c->query);
266 :
267 543724 : if (m->emod & mod_explain) {
268 63 : if (c->curprg->def)
269 63 : printFunction(c->fdout, mb, 0, LIST_MAL_NAME | LIST_MAL_VALUE | LIST_MAL_TYPE | LIST_MAL_MAPI);
270 : } else {
271 543661 : if (m->emod & mod_trace){
272 4 : if((msg = SQLsetTrace(c,mb)) == MAL_SUCCEED) {
273 4 : setVariableScope(mb);
274 4 : MT_lock_set(&mal_contextLock);
275 4 : c->idle = 0;
276 4 : c->lastcmd = time(0);
277 4 : MT_lock_unset(&mal_contextLock);
278 4 : msg = runMAL(c, mb, 0, 0);
279 4 : stopTrace(c);
280 : }
281 : } else {
282 543657 : setVariableScope(mb);
283 543657 : MT_lock_set(&mal_contextLock);
284 543657 : c->idle = 0;
285 543657 : c->lastcmd = time(0);
286 543657 : MT_lock_unset(&mal_contextLock);
287 543657 : msg = runMAL(c, mb, 0, 0);
288 : }
289 543661 : resetMalBlk(mb);
290 : }
291 : /* after the query has been finished we enter the idle state */
292 543708 : MT_lock_set(&mal_contextLock);
293 543724 : c->idle = time(0);
294 543724 : c->lastcmd = 0;
295 543724 : MT_lock_unset(&mal_contextLock);
296 543724 : MT_thread_setworking(NULL);
297 543724 : return msg;
298 : }
299 :
300 : /*
301 : * Escape single quotes and backslashes. This is important to do before calling
302 : * SQLstatementIntern, if we are pasting user provided strings into queries
303 : * passed to the SQLstatementIntern. Otherwise we open ourselves to SQL
304 : * injection attacks.
305 : *
306 : * It returns the input string with all the single quotes(') and the backslashes
307 : * (\) doubled, or NULL, if it could not allocate enough space.
308 : *
309 : * The caller is responsible to free the returned value.
310 : */
311 : str
312 0 : SQLescapeString(str s)
313 : {
314 0 : str ret = NULL;
315 0 : char *p, *q;
316 0 : size_t len = 0;
317 :
318 0 : if(!s) {
319 : return NULL;
320 : }
321 :
322 : /* At most we will need 2*strlen(s) + 1 characters */
323 0 : len = strlen(s);
324 0 : ret = (str)GDKmalloc(2*len + 1);
325 0 : if (!ret) {
326 : return NULL;
327 : }
328 :
329 0 : for (p = s, q = ret; *p != '\0'; p++, q++) {
330 0 : *q = *p;
331 0 : if (*p == '\'') {
332 0 : *(++q) = '\'';
333 : }
334 0 : else if (*p == '\\') {
335 0 : *(++q) = '\\';
336 : }
337 : }
338 :
339 0 : *q = '\0';
340 0 : return ret;
341 : }
342 :
343 : str
344 10764 : SQLstatementIntern(Client c, const char *expr, const char *nme, bit execute, bit output, res_table **result)
345 : {
346 10764 : int status = 0, err = 0, oldvtop, oldstop = 1, oldvid, inited = 0, ac, sizeframes, topframes;
347 10764 : unsigned int label;
348 10764 : mvc *o = NULL, *m = NULL;
349 10764 : sql_frame **frames;
350 10764 : list *global_vars;
351 10764 : buffer *b = NULL;
352 10764 : char *n = NULL;
353 10764 : bstream *bs = NULL;
354 10764 : stream *buf = NULL;
355 10764 : str msg = MAL_SUCCEED;
356 10764 : backend *be = NULL, *sql = (backend *) c->sqlcontext;
357 10764 : Symbol backup = NULL;
358 10764 : size_t len = strlen(expr);
359 :
360 10764 : if (!sql) {
361 9 : inited = 1;
362 9 : msg = SQLinitClient(c, NULL, NULL, NULL);
363 9 : sql = (backend *) c->sqlcontext;
364 : }
365 10764 : if (msg){
366 0 : freeException(msg);
367 0 : throw(SQL, "sql.statement", SQLSTATE(HY002) "Catalogue not available");
368 : }
369 :
370 10764 : m = sql->mvc;
371 10764 : ac = m->session->auto_commit;
372 10764 : o = MNEW(mvc);
373 10764 : if (!o) {
374 0 : if (inited) {
375 0 : msg = SQLresetClient(c);
376 0 : freeException(msg);
377 : }
378 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
379 : }
380 10764 : *o = *m;
381 :
382 : /* create private allocator */
383 10764 : m->sa = NULL;
384 10764 : if ((msg = SQLtrans(m)) != MAL_SUCCEED) {
385 0 : be = sql;
386 0 : sql = NULL;
387 0 : goto endofcompile;
388 : }
389 10764 : status = m->session->status;
390 :
391 10764 : m->type = Q_PARSE;
392 10764 : be = sql;
393 10764 : sql = backend_create(m, c);
394 10764 : if (sql == NULL) {
395 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
396 0 : goto endofcompile;
397 : }
398 10764 : sql->output_format = be->output_format;
399 10764 : if (!output) {
400 10752 : sql->output_format = OFMT_NONE;
401 : }
402 10764 : sql->depth++;
403 :
404 10764 : m->user_id = m->role_id = USER_MONETDB;
405 10764 : if (result)
406 1628 : m->reply_size = -2; /* do not clean up result tables */
407 :
408 : /* mimic a client channel on which the query text is received */
409 10764 : b = malloc(sizeof(buffer));
410 10764 : if (b == NULL) {
411 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
412 0 : goto endofcompile;
413 : }
414 10764 : n = malloc(len + 1 + 1);
415 10764 : if (n == NULL) {
416 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
417 0 : goto endofcompile;
418 : }
419 10764 : strcpy_len(n, expr, len + 1);
420 10764 : n[len] = '\n';
421 10764 : n[len + 1] = 0;
422 10764 : len++;
423 10764 : buffer_init(b, n, len);
424 10764 : buf = buffer_rastream(b, "sqlstatement");
425 10764 : if (buf == NULL) {
426 0 : buffer_destroy(b); /* n and b will be freed by the buffer */
427 0 : b = NULL;
428 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
429 0 : goto endofcompile;
430 : }
431 10764 : bs = bstream_create(buf, b->len);
432 10764 : if (bs == NULL) {
433 0 : mnstr_destroy(buf);
434 0 : buffer_destroy(b);
435 0 : b = NULL;
436 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
437 0 : goto endofcompile;
438 : }
439 10764 : scanner_init(&m->scanner, bs, NULL);
440 10764 : m->scanner.mode = LINE_N;
441 10764 : bstream_next(m->scanner.rs);
442 :
443 10764 : m->params = NULL;
444 10764 : m->session->auto_commit = 0;
445 10764 : if (!m->sa && !(m->sa = sa_create(m->pa)) ) {
446 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
447 0 : goto endofcompile;
448 : }
449 :
450 : /*
451 : * System has been prepared to parse it and generate code.
452 : * Scan the complete string for SQL statements, stop at the first error.
453 : */
454 10764 : c->sqlcontext = sql;
455 10764 : if (c->curprg) {
456 10742 : backup = c->curprg;
457 10742 : c->curprg = NULL;
458 : }
459 :
460 304300 : while (msg == MAL_SUCCEED && m->scanner.rs->pos < m->scanner.rs->len) {
461 293542 : sql_rel *r;
462 :
463 293542 : m->sym = NULL;
464 587084 : if ((err = sqlparse(m)) ||
465 : /* Only forget old errors on transaction boundaries */
466 587084 : (mvc_status(m) && m->type != Q_TRANS) || !m->sym) {
467 2 : if (!err)
468 2 : err = mvc_status(m);
469 2 : if (*m->errstr){
470 0 : if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
471 0 : msg = createException(PARSE, "SQLparser", "%s", m->errstr);
472 : else
473 0 : msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
474 0 : *m->errstr = 0;
475 : }
476 2 : sqlcleanup(sql, err);
477 2 : execute = 0;
478 2 : if (!err)
479 2 : continue;
480 0 : goto endofcompile;
481 : }
482 :
483 : /*
484 : * We have dealt with the first parsing step and advanced the input reader
485 : * to the next statement (if any).
486 : * Now is the time to also perform the semantic analysis,
487 : * optimize and produce code.
488 : * We don't search the cache for a previous incarnation yet.
489 : */
490 293540 : if((msg = MSinitClientPrg(c, sql_private_module_name, nme)) != MAL_SUCCEED) {
491 0 : goto endofcompile;
492 : }
493 293540 : oldvtop = c->curprg->def->vtop;
494 293540 : oldstop = c->curprg->def->stop;
495 293540 : oldvid = c->curprg->def->vid;
496 293540 : r = sql_symbol2relation(sql, m->sym);
497 :
498 293540 : assert(m->emode != m_prepare);
499 293540 : scanner_query_processed(&(m->scanner));
500 293540 : if ((err = mvc_status(m)) ) {
501 6 : if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
502 6 : msg = createException(PARSE, "SQLparser", "%s", m->errstr);
503 : else
504 0 : msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
505 6 : *m->errstr = 0;
506 6 : msg = handle_error(m, status, msg);
507 6 : sqlcleanup(sql, err);
508 : /* restore the state */
509 6 : MSresetInstructions(c->curprg->def, oldstop);
510 6 : freeVariables(c, c->curprg->def, c->glb, oldvtop, oldvid);
511 6 : c->curprg->def->errors = 0;
512 6 : goto endofcompile;
513 : }
514 : /* generate MAL code */
515 293534 : be->depth++;
516 293534 : setVarType(c->curprg->def, 0, 0);
517 293534 : if (backend_dumpstmt(be, c->curprg->def, r, 1, 1, NULL) < 0)
518 0 : err = 1;
519 293534 : be->depth--;
520 :
521 293534 : if (err == 0) {
522 293534 : if (msg == MAL_SUCCEED)
523 293534 : msg = SQLoptimizeQuery(c, c->curprg->def);
524 293534 : if (msg)
525 : err = 1;
526 : }
527 :
528 293534 : if (err) {
529 0 : status = -10;
530 0 : if (msg)
531 0 : msg = handle_error(m, status, msg);
532 0 : sqlcleanup(sql, err);
533 : /* restore the state */
534 0 : MSresetInstructions(c->curprg->def, oldstop);
535 0 : freeVariables(c, c->curprg->def, c->glb, oldvtop, oldvid);
536 0 : c->curprg->def->errors = 0;
537 0 : goto endofcompile;
538 : }
539 :
540 293534 : if (execute) {
541 293534 : if (!output)
542 293522 : sql->out = NULL; /* no output stream */
543 293534 : be->depth++;
544 293534 : msg = SQLrun(c,m);
545 293534 : be->depth--;
546 293534 : MSresetInstructions(c->curprg->def, oldstop);
547 293534 : freeVariables(c, c->curprg->def, NULL, oldvtop, oldvid);
548 293534 : sqlcleanup(sql, 0);
549 293534 : if (!execute)
550 : goto endofcompile;
551 : }
552 293534 : if (sql->results) {
553 1588 : if (result) { /* return all results sets */
554 1587 : *result = sql->results;
555 : } else {
556 1 : if (sql->results == be->results)
557 0 : be->results = NULL;
558 1 : res_tables_destroy(sql->results);
559 : }
560 1588 : sql->results = NULL;
561 : }
562 : }
563 : /*
564 : * We are done; a MAL procedure resides in the cache.
565 : */
566 10758 : endofcompile:
567 10764 : if (execute)
568 10762 : MSresetInstructions(c->curprg->def, 1);
569 :
570 10764 : if (backup)
571 10742 : c->curprg = backup;
572 :
573 10764 : c->sqlcontext = be;
574 10764 : backend_destroy(sql);
575 10764 : buffer_destroy(b);
576 10764 : bstream_destroy(m->scanner.rs);
577 10764 : if (m->sa)
578 10764 : sa_destroy(m->sa);
579 10764 : m->sa = NULL;
580 10764 : m->sym = NULL;
581 10764 : m->runs = NULL;
582 : /* variable stack maybe resized, ie we need to keep the new stack */
583 10764 : label = m->label;
584 10764 : status = m->session->status;
585 10764 : global_vars = m->global_vars;
586 10764 : sizeframes = m->sizeframes;
587 10764 : topframes = m->topframes;
588 10764 : frames = m->frames;
589 10764 : *m = *o;
590 10764 : _DELETE(o);
591 10764 : m->label = label;
592 10764 : m->global_vars = global_vars;
593 10764 : m->sizeframes = sizeframes;
594 10764 : m->topframes = topframes;
595 10764 : m->frames = frames;
596 10764 : m->session->status = status;
597 10764 : m->session->auto_commit = ac;
598 10764 : if (inited) {
599 9 : str other = SQLresetClient(c);
600 9 : freeException(other);
601 : }
602 : return msg;
603 : }
604 :
605 : str
606 250189 : SQLengineIntern(Client c, backend *be)
607 : {
608 250189 : str msg = MAL_SUCCEED;
609 : //char oldlang = be->language;
610 250189 : mvc *m = be->mvc;
611 :
612 250189 : assert (m->emode != m_deallocate && m->emode != m_prepare);
613 250189 : assert (c->curprg->def->stop > 2);
614 :
615 : //be->language = 'D';
616 250189 : if (MALcommentsOnly(c->curprg->def))
617 : msg = MAL_SUCCEED;
618 : else
619 250189 : msg = SQLrun(c,m);
620 :
621 250189 : if (m->type == Q_SCHEMA && m->qc != NULL)
622 15985 : qc_clean(m->qc);
623 250189 : be->q = NULL;
624 250189 : if (msg)
625 17654 : m->session->status = -10;
626 250189 : sqlcleanup(be, (!msg) ? 0 : -1);
627 250189 : MSresetInstructions(c->curprg->def, 1);
628 250187 : freeVariables(c, c->curprg->def, NULL, be->vtop, be->vid);
629 : //be->language = oldlang;
630 : /*
631 : * Any error encountered during execution should block further processing
632 : * unless auto_commit has been set.
633 : */
634 250189 : return msg;
635 : }
636 :
637 : void
638 33 : SQLdestroyResult(res_table *destroy)
639 : {
640 33 : res_table_destroy(destroy);
641 33 : }
642 :
643 : static str
644 184 : RAcommit_statement(backend *be, str msg)
645 : {
646 184 : mvc *m = be->mvc;
647 : /* if an error already exists set the session status to dirty */
648 184 : if (msg != MAL_SUCCEED && m->session->tr->active && !m->session->status)
649 2 : m->session->status = -1;
650 184 : return msg;
651 : }
652 :
653 : /* a hook is provided to execute relational algebra expressions */
654 : str
655 0 : RAstatement(Client c, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
656 : {
657 0 : int pos = 0;
658 0 : str *expr = getArgReference_str(stk, pci, 1);
659 0 : bit *opt = getArgReference_bit(stk, pci, 2);
660 0 : backend *be = NULL;
661 0 : mvc *m = NULL;
662 0 : str msg = MAL_SUCCEED;
663 0 : sql_rel *rel;
664 0 : list *refs;
665 :
666 0 : if ((msg = getSQLContext(c, mb, &m, &be)) != NULL)
667 : return msg;
668 0 : if ((msg = checkSQLContext(c)) != NULL)
669 : return msg;
670 0 : if ((msg = SQLtrans(m)) != MAL_SUCCEED)
671 : return msg;
672 0 : if (!m->sa)
673 0 : m->sa = sa_create(m->pa);
674 0 : if (!m->sa)
675 0 : return RAcommit_statement(be, createException(SQL,"RAstatement",SQLSTATE(HY013) MAL_MALLOC_FAIL));
676 0 : refs = sa_list(m->sa);
677 0 : rel = rel_read(m, *expr, &pos, refs);
678 0 : if (*opt && rel)
679 0 : rel = sql_processrelation(m, rel, 0, 0, 0, 0);
680 0 : if (!rel) {
681 0 : if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
682 0 : msg = createException(SQL, "RAstatement", "%s", m->errstr);
683 : else
684 0 : msg = createException(SQL, "RAstatement", SQLSTATE(42000) "%s", m->errstr);
685 : } else {
686 0 : if ((msg = MSinitClientPrg(c, sql_private_module_name, "test")) != MAL_SUCCEED)
687 0 : return RAcommit_statement(be, msg);
688 :
689 : /* generate MAL code, ignoring any code generation error */
690 0 : setVarType(c->curprg->def, 0, 0);
691 0 : if (backend_dumpstmt(be, c->curprg->def, rel, 0, 1, NULL) < 0) {
692 0 : msg = createException(SQL,"RAstatement","Program contains errors"); // TODO: use macro definition.
693 : } else {
694 0 : SQLaddQueryToCache(c);
695 0 : msg = SQLoptimizeFunction(c,c->curprg->def);
696 0 : if( msg == MAL_SUCCEED)
697 0 : msg = SQLrun(c,m);
698 0 : resetMalBlk(c->curprg->def);
699 0 : SQLremoveQueryFromCache(c);
700 : }
701 0 : rel_destroy(rel);
702 : }
703 0 : return RAcommit_statement(be, msg);
704 : }
705 :
706 : static char *
707 31 : parseIdent(char *in, char *out)
708 : {
709 242 : while (*in && *in != '"') {
710 211 : if (*in == '\\' && (*(in + 1) == '\\' || *(in + 1) == '"')) {
711 3 : *out++ = *(in + 1);
712 3 : in+=2;
713 : } else {
714 208 : *out++ = *in++;
715 : }
716 : }
717 31 : *out++ = '\0';
718 31 : return in;
719 : }
720 :
721 : struct local_var_entry {
722 : char *vname;
723 : sql_subtype tpe;
724 : } local_var_entry;
725 :
726 : struct global_var_entry {
727 : char *vname;
728 : sql_schema *s;
729 : } global_var_entry;
730 :
731 : static str
732 184 : RAstatement2_return(backend *be, mvc *m, int nlevels, struct global_var_entry *gvars, int gentries, str msg)
733 : {
734 200 : while (nlevels) { /* clean added frames */
735 16 : stack_pop_frame(m);
736 16 : nlevels--;
737 : }
738 184 : for (int i = 0 ; i < gentries ; i++) { /* clean any added global variables */
739 0 : struct global_var_entry gv = gvars[i];
740 0 : (void) remove_global_var(m, gv.s, gv.vname);
741 : }
742 184 : sa_reset(m->ta);
743 184 : return RAcommit_statement(be, msg);
744 : }
745 :
746 : str
747 184 : RAstatement2(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
748 : {
749 184 : int pos = 0, nlevels = 0, *lkeys = NULL, lcap = 0, lentries = 0, gcap = 0, gentries = 0;
750 184 : str mod = *getArgReference_str(stk, pci, 1);
751 184 : str nme = *getArgReference_str(stk, pci, 2);
752 184 : str expr = *getArgReference_str(stk, pci, 3);
753 184 : str sig = *getArgReference_str(stk, pci, 4);
754 184 : str types = pci->argc == 6 ? *getArgReference_str(stk, pci, 5) : NULL;
755 184 : backend *be = NULL;
756 184 : mvc *m = NULL;
757 184 : str msg = MAL_SUCCEED;
758 184 : sql_rel *rel;
759 184 : list *refs, *ops;
760 184 : struct local_var_entry *lvars = NULL;
761 184 : struct global_var_entry *gvars = NULL;
762 :
763 184 : if ((msg = getSQLContext(cntxt, mb, &m, &be)) != NULL)
764 : return msg;
765 184 : if ((msg = checkSQLContext(cntxt)) != NULL)
766 : return msg;
767 184 : if ((msg = SQLtrans(m)) != MAL_SUCCEED)
768 : return msg;
769 184 : if (!m->sa)
770 184 : m->sa = sa_create(m->pa);
771 184 : if (!m->sa)
772 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(HY013) MAL_MALLOC_FAIL));
773 :
774 184 : ops = sa_list(m->sa);
775 212 : while (sig && *sig) {
776 28 : sql_schema *sh = NULL;
777 28 : sql_type *t = NULL;
778 28 : sql_subtype tpe;
779 28 : char *p, *vtype = NULL, *sch = NULL, *var = NULL;
780 28 : int d, s, level = strtol(sig, &p, 10);
781 :
782 28 : var = p+1;
783 28 : assert(*p == '"');
784 28 : p = parseIdent(p+1, var);
785 28 : p++;
786 28 : if (*p == '"') { /* global variable, parse schema and name */
787 3 : sch = var;
788 3 : var = p+1;
789 3 : p = parseIdent(p+1, var);
790 3 : p++;
791 : }
792 :
793 28 : assert(*p == ' ');
794 28 : p++; /* skip space and get type */
795 28 : vtype = p;
796 28 : p = strchr(p, '(');
797 28 : *p++ = '\0';
798 :
799 : /* get digits and scale */
800 28 : d = strtol(p, &p, 10);
801 28 : p++; /* skip , */
802 28 : s = strtol(p, &p, 10);
803 28 : p+=2; /* skip ) and , or ' ' */
804 28 : sig = p;
805 :
806 28 : if (!sql_find_subtype(&tpe, vtype, d, s)) {
807 6 : if (!(t = mvc_bind_type(m, vtype))) /* try an external type */
808 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(42000) "SQL type %s(%d, %d) not found\n", vtype, d, s));
809 6 : sql_init_subtype(&tpe, t, d, s);
810 : }
811 :
812 28 : if (sch) {
813 3 : assert(level == 0);
814 3 : if (!(sh = mvc_bind_schema(m, sch)))
815 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(3F000) "No such schema '%s'", sch));
816 3 : if (!find_global_var(m, sh, var)) { /* don't add the same global variable again */
817 0 : if (!push_global_var(m, sch, var, &tpe)) /* if doesn't exist, add it, then remove it before returning */
818 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(HY013) MAL_MALLOC_FAIL));
819 0 : if (gentries == gcap) {
820 0 : if (gcap == 0) {
821 0 : gcap = 8;
822 0 : gvars = SA_NEW_ARRAY(m->ta, struct global_var_entry, gcap);
823 : } else {
824 0 : int ngcap = gcap * 4;
825 0 : gvars = SA_RENEW_ARRAY(m->ta, struct global_var_entry, gvars, ngcap, gcap);
826 : gcap = ngcap;
827 : }
828 0 : gvars[gentries++] = (struct global_var_entry) {.s = sh, .vname = var,};
829 : }
830 : }
831 3 : list_append(ops, exp_var(m->sa, sa_strdup(m->sa, sch), sa_strdup(m->sa, var), &tpe, 0));
832 : } else {
833 25 : char opname[BUFSIZ];
834 :
835 25 : if (lentries == lcap) {
836 13 : if (lcap == 0) {
837 13 : lcap = 8;
838 13 : lkeys = SA_NEW_ARRAY(m->ta, int, lcap);
839 13 : lvars = SA_NEW_ARRAY(m->ta, struct local_var_entry, lcap);
840 : } else {
841 0 : int nlcap = lcap * 4;
842 0 : lkeys = SA_RENEW_ARRAY(m->ta, int, lkeys, nlcap, lcap);
843 0 : lvars = SA_RENEW_ARRAY(m->ta, struct local_var_entry, lvars, nlcap, lcap);
844 : lcap = nlcap;
845 : }
846 : }
847 25 : lkeys[lentries] = level;
848 25 : lvars[lentries] = (struct local_var_entry) {.tpe = tpe, .vname = var,};
849 25 : lentries++;
850 :
851 25 : snprintf(opname, BUFSIZ, "%d%%%s", level, var); /* engineering trick */
852 25 : list_append(ops, exp_var(m->sa, NULL, sa_strdup(m->sa, opname), &tpe, level));
853 : }
854 : }
855 184 : if (lentries) {
856 13 : GDKqsort(lkeys, lvars, NULL, lentries, sizeof(int), sizeof(struct local_var_entry), TYPE_int, false, false);
857 :
858 38 : for (int i = 0 ; i < lentries ; i++) {
859 25 : int next_level = lkeys[i];
860 25 : struct local_var_entry next_val = lvars[i];
861 :
862 25 : assert(next_level != 0); /* no global variables here */
863 41 : while (nlevels < next_level) { /* add gap levels */
864 16 : if (!stack_push_frame(m, NULL))
865 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(HY013) MAL_MALLOC_FAIL));
866 16 : nlevels++;
867 : }
868 25 : if (!frame_push_var(m, next_val.vname, &next_val.tpe))
869 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(HY013) MAL_MALLOC_FAIL));
870 : }
871 : }
872 :
873 184 : refs = sa_list(m->sa);
874 184 : rel = rel_read(m, expr, &pos, refs);
875 184 : if (rel)
876 180 : rel = sql_processrelation(m, rel, 0, 0, 0, 0);
877 180 : if (!rel) {
878 4 : if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
879 4 : msg = createException(SQL, "RAstatement2", "%s", m->errstr);
880 : else
881 0 : msg = createException(SQL, "RAstatement2", SQLSTATE(42000) "%s", m->errstr);
882 180 : } else if (rel && types && is_simple_project(rel->op)) { /* Test if types match */
883 180 : list *types_list = sa_list(m->sa);
884 180 : str token, rest;
885 :
886 1579 : for (token = strtok_r(types, "%", &rest); token; token = strtok_r(NULL, "%", &rest))
887 1399 : list_append(types_list, token);
888 :
889 180 : if (list_length(types_list) != list_length(rel->exps))
890 0 : msg = createException(SQL, "RAstatement2", SQLSTATE(42000) "The number of projections don't match between the generated plan and the expected one: %d != %d",
891 : list_length(types_list), list_length(rel->exps));
892 : else {
893 180 : int i = 1;
894 1579 : for (node *n = rel->exps->h, *m = types_list->h ; n && m && !msg ; n = n->next, m = m->next) {
895 1399 : sql_exp *e = (sql_exp *) n->data;
896 1399 : sql_subtype *t = exp_subtype(e);
897 1399 : str got = sql_subtype_string(be->mvc->ta, t), expected = (str) m->data;
898 :
899 1399 : if (!got)
900 0 : msg = createException(SQL, "RAstatement2", SQLSTATE(HY013) MAL_MALLOC_FAIL);
901 1399 : else if (strcmp(expected, got) != 0)
902 2 : msg = createException(SQL, "RAstatement2", SQLSTATE(42000) "Parameter %d has wrong SQL type, expected %s, but got %s instead", i, expected, got);
903 1399 : i++;
904 : }
905 : }
906 : }
907 184 : if (!msg && monet5_create_relational_function(m, mod, nme, rel, NULL, ops, 0) < 0)
908 0 : msg = createException(SQL, "RAstatement2", "%s", m->errstr);
909 184 : rel_destroy(rel);
910 184 : return RAstatement2_return(be, m, nlevels, gvars, gentries, msg);
911 : }
912 :
913 : str
914 184 : RAstatementEnd(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
915 : {
916 184 : backend *be = NULL;
917 184 : mvc *m = NULL;
918 184 : str msg = MAL_SUCCEED;
919 :
920 184 : (void) stk;
921 184 : (void) pci;
922 184 : if ((msg = getSQLContext(cntxt, mb, &m, &be)) != NULL)
923 : return msg;
924 184 : if ((msg = checkSQLContext(cntxt)) != NULL)
925 : return msg;
926 :
927 184 : sqlcleanup(be, 0);
928 184 : return SQLautocommit(m);
929 : }
|