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 column 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,"varchar");
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 : return msg;
253 : }
254 :
255 : str
256 559218 : SQLrun(Client c, mvc *m)
257 : {
258 559218 : str msg = MAL_SUCCEED;
259 559218 : MalBlkPtr mb = c->curprg->def;
260 :
261 559218 : assert(!*m->errstr);
262 :
263 559218 : TRC_INFO(SQL_EXECUTION, "Executing: %s", c->query);
264 559219 : MT_thread_setworking(c->query);
265 :
266 559218 : if (m->emod & mod_explain) {
267 65 : if (c->curprg->def)
268 65 : printFunction(c->fdout, mb, 0, LIST_MAL_NAME | LIST_MAL_VALUE | LIST_MAL_TYPE | LIST_MAL_MAPI);
269 : } else {
270 559153 : if (m->emod & mod_trace){
271 4 : if((msg = SQLsetTrace(c,mb)) == MAL_SUCCEED) {
272 4 : setVariableScope(mb);
273 4 : MT_lock_set(&mal_contextLock);
274 4 : c->idle = 0;
275 4 : c->lastcmd = time(0);
276 4 : MT_lock_unset(&mal_contextLock);
277 4 : msg = runMAL(c, mb, 0, 0);
278 4 : stopTrace(c);
279 : }
280 : } else {
281 559149 : setVariableScope(mb);
282 559149 : MT_lock_set(&mal_contextLock);
283 559149 : c->idle = 0;
284 559149 : c->lastcmd = time(0);
285 559149 : MT_lock_unset(&mal_contextLock);
286 559149 : msg = runMAL(c, mb, 0, 0);
287 : }
288 559153 : resetMalBlk(mb);
289 : }
290 : /* after the query has been finished we enter the idle state */
291 559209 : MT_lock_set(&mal_contextLock);
292 559218 : c->idle = time(0);
293 559218 : c->lastcmd = 0;
294 559218 : MT_lock_unset(&mal_contextLock);
295 559218 : MT_thread_setworking(NULL);
296 559218 : return msg;
297 : }
298 :
299 : /*
300 : * Escape single quotes and backslashes. This is important to do before calling
301 : * SQLstatementIntern, if we are pasting user provided strings into queries
302 : * passed to the SQLstatementIntern. Otherwise we open ourselves to SQL
303 : * injection attacks.
304 : *
305 : * It returns the input string with all the single quotes(') and the backslashes
306 : * (\) doubled, or NULL, if it could not allocate enough space.
307 : *
308 : * The caller is responsible to free the returned value.
309 : */
310 : str
311 0 : SQLescapeString(str s)
312 : {
313 0 : str ret = NULL;
314 0 : char *p, *q;
315 0 : size_t len = 0;
316 :
317 0 : if(!s) {
318 : return NULL;
319 : }
320 :
321 : /* At most we will need 2*strlen(s) + 1 characters */
322 0 : len = strlen(s);
323 0 : ret = (str)GDKmalloc(2*len + 1);
324 0 : if (!ret) {
325 : return NULL;
326 : }
327 :
328 0 : for (p = s, q = ret; *p != '\0'; p++, q++) {
329 0 : *q = *p;
330 0 : if (*p == '\'') {
331 0 : *(++q) = '\'';
332 : }
333 0 : else if (*p == '\\') {
334 0 : *(++q) = '\\';
335 : }
336 : }
337 :
338 0 : *q = '\0';
339 0 : return ret;
340 : }
341 :
342 : str
343 10395 : SQLstatementIntern(Client c, const char *expr, const char *nme, bit execute, bit output, res_table **result)
344 : {
345 10395 : int status = 0, err = 0, oldvtop, oldstop = 1, inited = 0, ac, sizeframes, topframes;
346 10395 : unsigned int label;
347 10395 : mvc *o = NULL, *m = NULL;
348 10395 : sql_frame **frames;
349 10395 : list *global_vars;
350 10395 : buffer *b = NULL;
351 10395 : char *n = NULL;
352 10395 : bstream *bs = NULL;
353 10395 : stream *buf = NULL;
354 10395 : str msg = MAL_SUCCEED;
355 10395 : backend *be = NULL, *sql = (backend *) c->sqlcontext;
356 10395 : Symbol backup = NULL;
357 10395 : size_t len = strlen(expr);
358 :
359 10395 : if (!sql) {
360 9 : inited = 1;
361 9 : msg = SQLinitClient(c, NULL, NULL, NULL);
362 9 : sql = (backend *) c->sqlcontext;
363 : }
364 10395 : if (msg){
365 0 : freeException(msg);
366 0 : throw(SQL, "sql.statement", SQLSTATE(HY002) "Catalogue not available");
367 : }
368 :
369 10395 : m = sql->mvc;
370 10395 : ac = m->session->auto_commit;
371 10395 : o = MNEW(mvc);
372 10395 : if (!o) {
373 0 : if (inited) {
374 0 : msg = SQLresetClient(c);
375 0 : freeException(msg);
376 : }
377 0 : throw(SQL, "sql.statement", SQLSTATE(HY013) MAL_MALLOC_FAIL);
378 : }
379 10395 : *o = *m;
380 :
381 : /* create private allocator */
382 10395 : m->sa = NULL;
383 10395 : if ((msg = SQLtrans(m)) != MAL_SUCCEED) {
384 0 : be = sql;
385 0 : sql = NULL;
386 0 : goto endofcompile;
387 : }
388 10395 : status = m->session->status;
389 :
390 10395 : m->type = Q_PARSE;
391 10395 : be = sql;
392 10395 : sql = backend_create(m, c);
393 10395 : if (sql == NULL) {
394 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
395 0 : goto endofcompile;
396 : }
397 10395 : sql->output_format = be->output_format;
398 10395 : if (!output) {
399 10383 : sql->output_format = OFMT_NONE;
400 : }
401 10395 : sql->depth++;
402 :
403 10395 : m->user_id = m->role_id = USER_MONETDB;
404 10395 : if (result)
405 1303 : m->reply_size = -2; /* do not clean up result tables */
406 :
407 : /* mimic a client channel on which the query text is received */
408 10395 : b = malloc(sizeof(buffer));
409 10395 : if (b == NULL) {
410 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
411 0 : goto endofcompile;
412 : }
413 10395 : n = malloc(len + 1 + 1);
414 10395 : if (n == NULL) {
415 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
416 0 : goto endofcompile;
417 : }
418 10395 : strcpy_len(n, expr, len + 1);
419 10395 : n[len] = '\n';
420 10395 : n[len + 1] = 0;
421 10395 : len++;
422 10395 : buffer_init(b, n, len);
423 10395 : buf = buffer_rastream(b, "sqlstatement");
424 10395 : if (buf == NULL) {
425 0 : buffer_destroy(b); /* n and b will be freed by the buffer */
426 0 : b = NULL;
427 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
428 0 : goto endofcompile;
429 : }
430 10395 : bs = bstream_create(buf, b->len);
431 10395 : if (bs == NULL) {
432 0 : mnstr_destroy(buf);
433 0 : buffer_destroy(b);
434 0 : b = NULL;
435 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
436 0 : goto endofcompile;
437 : }
438 10395 : scanner_init(&m->scanner, bs, NULL);
439 10395 : m->scanner.mode = LINE_N;
440 10395 : bstream_next(m->scanner.rs);
441 :
442 10395 : m->params = NULL;
443 10395 : m->session->auto_commit = 0;
444 10395 : if (!m->sa && !(m->sa = sa_create(m->pa)) ) {
445 0 : msg = createException(SQL,"sql.statement",SQLSTATE(HY013) MAL_MALLOC_FAIL);
446 0 : goto endofcompile;
447 : }
448 :
449 : /*
450 : * System has been prepared to parse it and generate code.
451 : * Scan the complete string for SQL statements, stop at the first error.
452 : */
453 10395 : c->sqlcontext = sql;
454 10395 : if (c->curprg) {
455 10373 : backup = c->curprg;
456 10373 : c->curprg = NULL;
457 : }
458 :
459 314634 : while (msg == MAL_SUCCEED && m->scanner.rs->pos < m->scanner.rs->len) {
460 304242 : sql_rel *r;
461 :
462 304242 : m->sym = NULL;
463 608484 : if ((err = sqlparse(m)) ||
464 : /* Only forget old errors on transaction boundaries */
465 608484 : (mvc_status(m) && m->type != Q_TRANS) || !m->sym) {
466 2 : if (!err)
467 2 : err = mvc_status(m);
468 2 : if (*m->errstr){
469 0 : if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
470 0 : msg = createException(PARSE, "SQLparser", "%s", m->errstr);
471 : else
472 0 : msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
473 0 : *m->errstr = 0;
474 : }
475 2 : sqlcleanup(sql, err);
476 2 : execute = 0;
477 2 : if (!err)
478 2 : continue;
479 0 : goto endofcompile;
480 : }
481 :
482 : /*
483 : * We have dealt with the first parsing step and advanced the input reader
484 : * to the next statement (if any).
485 : * Now is the time to also perform the semantic analysis,
486 : * optimize and produce code.
487 : * We don't search the cache for a previous incarnation yet.
488 : */
489 304240 : if((msg = MSinitClientPrg(c, sql_private_module_name, nme)) != MAL_SUCCEED) {
490 0 : goto endofcompile;
491 : }
492 304240 : oldvtop = c->curprg->def->vtop;
493 304240 : oldstop = c->curprg->def->stop;
494 304240 : r = sql_symbol2relation(sql, m->sym);
495 :
496 304240 : assert(m->emode != m_prepare);
497 304240 : scanner_query_processed(&(m->scanner));
498 304240 : if ((err = mvc_status(m)) ) {
499 3 : if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
500 3 : msg = createException(PARSE, "SQLparser", "%s", m->errstr);
501 : else
502 0 : msg = createException(PARSE, "SQLparser", SQLSTATE(42000) "%s", m->errstr);
503 3 : *m->errstr = 0;
504 3 : msg = handle_error(m, status, msg);
505 3 : sqlcleanup(sql, err);
506 : /* restore the state */
507 3 : MSresetInstructions(c->curprg->def, oldstop);
508 3 : freeVariables(c, c->curprg->def, c->glb, oldvtop);
509 3 : c->curprg->def->errors = 0;
510 3 : goto endofcompile;
511 : }
512 : /* generate MAL code */
513 304237 : be->depth++;
514 304237 : setVarType(c->curprg->def, 0, 0);
515 304237 : if (backend_dumpstmt(be, c->curprg->def, r, 1, 1, NULL) < 0)
516 0 : err = 1;
517 304237 : be->depth--;
518 :
519 304237 : if (err == 0) {
520 304237 : if (msg == MAL_SUCCEED)
521 304237 : msg = SQLoptimizeQuery(c, c->curprg->def);
522 304237 : if (msg)
523 : err = 1;
524 : }
525 :
526 304237 : if (err) {
527 0 : status = -10;
528 0 : if (msg)
529 0 : msg = handle_error(m, status, msg);
530 0 : sqlcleanup(sql, err);
531 : /* restore the state */
532 0 : MSresetInstructions(c->curprg->def, oldstop);
533 0 : freeVariables(c, c->curprg->def, c->glb, oldvtop);
534 0 : c->curprg->def->errors = 0;
535 0 : goto endofcompile;
536 : }
537 :
538 304237 : if (execute) {
539 304237 : if (!output)
540 304225 : sql->out = NULL; /* no output stream */
541 304237 : be->depth++;
542 304237 : msg = SQLrun(c,m);
543 304237 : be->depth--;
544 304237 : if (c->curprg->def->stop > 1) {
545 0 : assert(0);
546 : MSresetInstructions(c->curprg->def, oldstop);
547 : freeVariables(c, c->curprg->def, NULL, oldvtop);
548 : }
549 304237 : sqlcleanup(sql, 0);
550 304237 : if (!execute)
551 : goto endofcompile;
552 : }
553 304237 : if (sql->results) {
554 1263 : if (result) { /* return all results sets */
555 1262 : *result = sql->results;
556 : } else {
557 1 : if (sql->results == be->results)
558 0 : be->results = NULL;
559 1 : res_tables_destroy(sql->results);
560 : }
561 1263 : sql->results = NULL;
562 : }
563 : }
564 : /*
565 : * We are done; a MAL procedure resides in the cache.
566 : */
567 10392 : endofcompile:
568 10395 : if (execute)
569 10393 : MSresetInstructions(c->curprg->def, 1);
570 :
571 10395 : if (backup)
572 10373 : c->curprg = backup;
573 :
574 10395 : c->sqlcontext = be;
575 10395 : backend_destroy(sql);
576 10395 : buffer_destroy(b);
577 10395 : bstream_destroy(m->scanner.rs);
578 10395 : if (m->sa)
579 10395 : sa_destroy(m->sa);
580 10395 : m->sa = NULL;
581 10395 : m->sym = NULL;
582 10395 : m->runs = NULL;
583 : /* variable stack maybe resized, ie we need to keep the new stack */
584 10395 : label = m->label;
585 10395 : status = m->session->status;
586 10395 : global_vars = m->global_vars;
587 10395 : sizeframes = m->sizeframes;
588 10395 : topframes = m->topframes;
589 10395 : frames = m->frames;
590 10395 : *m = *o;
591 10395 : _DELETE(o);
592 10395 : m->label = label;
593 10395 : m->global_vars = global_vars;
594 10395 : m->sizeframes = sizeframes;
595 10395 : m->topframes = topframes;
596 10395 : m->frames = frames;
597 10395 : m->session->status = status;
598 10395 : m->session->auto_commit = ac;
599 10395 : if (inited) {
600 9 : str other = SQLresetClient(c);
601 9 : freeException(other);
602 : }
603 : return msg;
604 : }
605 :
606 : str
607 254980 : SQLengineIntern(Client c, backend *be)
608 : {
609 254980 : str msg = MAL_SUCCEED;
610 : //char oldlang = be->language;
611 254980 : mvc *m = be->mvc;
612 :
613 254980 : assert (m->emode != m_deallocate && m->emode != m_prepare);
614 254980 : assert (c->curprg->def->stop > 2);
615 :
616 : //be->language = 'D';
617 254980 : if (MALcommentsOnly(c->curprg->def))
618 : msg = MAL_SUCCEED;
619 : else
620 254980 : msg = SQLrun(c,m);
621 :
622 254980 : if (m->type == Q_SCHEMA && m->qc != NULL)
623 16562 : qc_clean(m->qc);
624 254980 : be->q = NULL;
625 254980 : if (msg)
626 17788 : m->session->status = -10;
627 254980 : sqlcleanup(be, (!msg) ? 0 : -1);
628 254980 : MSresetInstructions(c->curprg->def, 1);
629 254979 : freeVariables(c, c->curprg->def, NULL, be->vtop);
630 : //be->language = oldlang;
631 : /*
632 : * Any error encountered during execution should block further processing
633 : * unless auto_commit has been set.
634 : */
635 254980 : return msg;
636 : }
637 :
638 : void
639 33 : SQLdestroyResult(res_table *destroy)
640 : {
641 33 : res_table_destroy(destroy);
642 33 : }
643 :
644 : static str
645 185 : RAcommit_statement(backend *be, str msg)
646 : {
647 185 : mvc *m = be->mvc;
648 : /* if an error already exists set the session status to dirty */
649 185 : if (msg != MAL_SUCCEED && m->session->tr->active && !m->session->status)
650 0 : m->session->status = -1;
651 185 : return msg;
652 : }
653 :
654 : /* a hook is provided to execute relational algebra expressions */
655 : str
656 0 : RAstatement(Client c, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
657 : {
658 0 : int pos = 0;
659 0 : str *expr = getArgReference_str(stk, pci, 1);
660 0 : bit *opt = getArgReference_bit(stk, pci, 2);
661 0 : backend *be = NULL;
662 0 : mvc *m = NULL;
663 0 : str msg = MAL_SUCCEED;
664 0 : sql_rel *rel;
665 0 : list *refs;
666 :
667 0 : if ((msg = getSQLContext(c, mb, &m, &be)) != NULL)
668 : return msg;
669 0 : if ((msg = checkSQLContext(c)) != NULL)
670 : return msg;
671 0 : if ((msg = SQLtrans(m)) != MAL_SUCCEED)
672 : return msg;
673 0 : if (!m->sa)
674 0 : m->sa = sa_create(m->pa);
675 0 : if (!m->sa)
676 0 : return RAcommit_statement(be, createException(SQL,"RAstatement",SQLSTATE(HY013) MAL_MALLOC_FAIL));
677 0 : refs = sa_list(m->sa);
678 0 : rel = rel_read(m, *expr, &pos, refs);
679 0 : if (*opt && rel)
680 0 : rel = sql_processrelation(m, rel, 0, 1, 0, 0);
681 0 : if (!rel) {
682 0 : if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
683 0 : msg = createException(SQL, "RAstatement", "%s", m->errstr);
684 : else
685 0 : msg = createException(SQL, "RAstatement", SQLSTATE(42000) "%s", m->errstr);
686 : } else {
687 0 : if ((msg = MSinitClientPrg(c, sql_private_module_name, "test")) != MAL_SUCCEED)
688 0 : return RAcommit_statement(be, msg);
689 :
690 : /* generate MAL code, ignoring any code generation error */
691 0 : setVarType(c->curprg->def, 0, 0);
692 0 : if (backend_dumpstmt(be, c->curprg->def, rel, 0, 1, NULL) < 0) {
693 0 : msg = createException(SQL,"RAstatement","Program contains errors"); // TODO: use macro definition.
694 : } else {
695 0 : SQLaddQueryToCache(c);
696 0 : msg = SQLoptimizeFunction(c,c->curprg->def);
697 0 : if( msg == MAL_SUCCEED)
698 0 : msg = SQLrun(c,m);
699 0 : resetMalBlk(c->curprg->def);
700 0 : SQLremoveQueryFromCache(c);
701 : }
702 0 : rel_destroy(rel);
703 : }
704 0 : return RAcommit_statement(be, msg);
705 : }
706 :
707 : static char *
708 31 : parseIdent(char *in, char *out)
709 : {
710 242 : while (*in && *in != '"') {
711 211 : if (*in == '\\' && (*(in + 1) == '\\' || *(in + 1) == '"')) {
712 3 : *out++ = *(in + 1);
713 3 : in+=2;
714 : } else {
715 208 : *out++ = *in++;
716 : }
717 : }
718 31 : *out++ = '\0';
719 31 : return in;
720 : }
721 :
722 : struct local_var_entry {
723 : char *vname;
724 : sql_subtype tpe;
725 : } local_var_entry;
726 :
727 : struct global_var_entry {
728 : char *vname;
729 : sql_schema *s;
730 : } global_var_entry;
731 :
732 : static str
733 185 : RAstatement2_return(backend *be, mvc *m, int nlevels, struct global_var_entry *gvars, int gentries, str msg)
734 : {
735 201 : while (nlevels) { /* clean added frames */
736 16 : stack_pop_frame(m);
737 16 : nlevels--;
738 : }
739 185 : for (int i = 0 ; i < gentries ; i++) { /* clean any added global variables */
740 0 : struct global_var_entry gv = gvars[i];
741 0 : (void) remove_global_var(m, gv.s, gv.vname);
742 : }
743 185 : sa_reset(m->ta);
744 185 : return RAcommit_statement(be, msg);
745 : }
746 :
747 : static void
748 1402 : subtype_from_string(mvc *sql, sql_subtype *st, char *type)
749 : {
750 1402 : unsigned digits = 0, scale = 0;
751 :
752 1402 : char *end = strchr(type, '(');
753 1402 : if (end) {
754 53 : end[0] = 0;
755 53 : digits = strtol(end+1, &end, 10);
756 53 : if (end && end[0] == ',')
757 5 : scale = strtol(end+1, NULL, 10);
758 : }
759 1402 : if (!sql_find_subtype(st, type, digits, scale)) {
760 2 : sql_type *t = mvc_bind_type(sql, type);
761 2 : if (t)
762 2 : sql_init_subtype(st, t, 0, 0);
763 : }
764 1402 : }
765 :
766 : str
767 185 : RAstatement2(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
768 : {
769 185 : int pos = 0, nlevels = 0, *lkeys = NULL, lcap = 0, lentries = 0, gcap = 0, gentries = 0;
770 185 : str mod = *getArgReference_str(stk, pci, 1);
771 185 : str nme = *getArgReference_str(stk, pci, 2);
772 185 : str expr = *getArgReference_str(stk, pci, 3);
773 185 : str sig = *getArgReference_str(stk, pci, 4);
774 185 : str types = pci->argc == 6 ? *getArgReference_str(stk, pci, 5) : NULL;
775 185 : backend *be = NULL;
776 185 : mvc *m = NULL;
777 185 : str msg = MAL_SUCCEED;
778 185 : sql_rel *rel;
779 185 : list *refs, *ops;
780 185 : struct local_var_entry *lvars = NULL;
781 185 : struct global_var_entry *gvars = NULL;
782 :
783 185 : if ((msg = getSQLContext(cntxt, mb, &m, &be)) != NULL)
784 : return msg;
785 185 : if ((msg = checkSQLContext(cntxt)) != NULL)
786 : return msg;
787 185 : if ((msg = SQLtrans(m)) != MAL_SUCCEED)
788 : return msg;
789 185 : if (!m->sa)
790 185 : m->sa = sa_create(m->pa);
791 185 : if (!m->sa)
792 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(HY013) MAL_MALLOC_FAIL));
793 :
794 185 : ops = sa_list(m->sa);
795 213 : while (sig && *sig) {
796 28 : sql_schema *sh = NULL;
797 28 : sql_type *t = NULL;
798 28 : sql_subtype tpe;
799 28 : char *p, *vtype = NULL, *sch = NULL, *var = NULL;
800 28 : int d, s, level = strtol(sig, &p, 10);
801 :
802 28 : var = p+1;
803 28 : assert(*p == '"');
804 28 : p = parseIdent(p+1, var);
805 28 : p++;
806 28 : if (*p == '"') { /* global variable, parse schema and name */
807 3 : sch = var;
808 3 : var = p+1;
809 3 : p = parseIdent(p+1, var);
810 3 : p++;
811 : }
812 :
813 28 : assert(*p == ' ');
814 28 : p++; /* skip space and get type */
815 28 : vtype = p;
816 28 : p = strchr(p, '(');
817 28 : *p++ = '\0';
818 :
819 : /* get digits and scale */
820 28 : d = strtol(p, &p, 10);
821 28 : p++; /* skip , */
822 28 : s = strtol(p, &p, 10);
823 28 : p+=2; /* skip ) and , or ' ' */
824 28 : sig = p;
825 :
826 28 : if (!sql_find_subtype(&tpe, vtype, d, s)) {
827 6 : if (!(t = mvc_bind_type(m, vtype))) /* try an external type */
828 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));
829 6 : sql_init_subtype(&tpe, t, d, s);
830 : }
831 :
832 28 : if (sch) {
833 3 : assert(level == 0);
834 3 : if (!(sh = mvc_bind_schema(m, sch)))
835 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(3F000) "No such schema '%s'", sch));
836 3 : if (!find_global_var(m, sh, var)) { /* don't add the same global variable again */
837 0 : if (!push_global_var(m, sch, var, &tpe)) /* if doesn't exist, add it, then remove it before returning */
838 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(HY013) MAL_MALLOC_FAIL));
839 0 : if (gentries == gcap) {
840 0 : if (gcap == 0) {
841 0 : gcap = 8;
842 0 : gvars = SA_NEW_ARRAY(m->ta, struct global_var_entry, gcap);
843 : } else {
844 0 : int ngcap = gcap * 4;
845 0 : gvars = SA_RENEW_ARRAY(m->ta, struct global_var_entry, gvars, ngcap, gcap);
846 : gcap = ngcap;
847 : }
848 0 : gvars[gentries++] = (struct global_var_entry) {.s = sh, .vname = var,};
849 : }
850 : }
851 3 : list_append(ops, exp_var(m->sa, sa_strdup(m->sa, sch), sa_strdup(m->sa, var), &tpe, 0));
852 : } else {
853 25 : char opname[BUFSIZ];
854 :
855 25 : if (lentries == lcap) {
856 13 : if (lcap == 0) {
857 13 : lcap = 8;
858 13 : lkeys = SA_NEW_ARRAY(m->ta, int, lcap);
859 13 : lvars = SA_NEW_ARRAY(m->ta, struct local_var_entry, lcap);
860 : } else {
861 0 : int nlcap = lcap * 4;
862 0 : lkeys = SA_RENEW_ARRAY(m->ta, int, lkeys, nlcap, lcap);
863 0 : lvars = SA_RENEW_ARRAY(m->ta, struct local_var_entry, lvars, nlcap, lcap);
864 : lcap = nlcap;
865 : }
866 : }
867 25 : lkeys[lentries] = level;
868 25 : lvars[lentries] = (struct local_var_entry) {.tpe = tpe, .vname = var,};
869 25 : lentries++;
870 :
871 25 : snprintf(opname, BUFSIZ, "%d%%%s", level, var); /* engineering trick */
872 25 : list_append(ops, exp_var(m->sa, NULL, sa_strdup(m->sa, opname), &tpe, level));
873 : }
874 : }
875 185 : if (lentries) {
876 13 : GDKqsort(lkeys, lvars, NULL, lentries, sizeof(int), sizeof(struct local_var_entry), TYPE_int, false, false);
877 :
878 38 : for (int i = 0 ; i < lentries ; i++) {
879 25 : int next_level = lkeys[i];
880 25 : struct local_var_entry next_val = lvars[i];
881 :
882 25 : assert(next_level != 0); /* no global variables here */
883 41 : while (nlevels < next_level) { /* add gap levels */
884 16 : if (!stack_push_frame(m, NULL))
885 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(HY013) MAL_MALLOC_FAIL));
886 16 : nlevels++;
887 : }
888 25 : if (!frame_push_var(m, next_val.vname, &next_val.tpe))
889 0 : return RAstatement2_return(be, m, nlevels, gvars, gentries, createException(SQL,"RAstatement2",SQLSTATE(HY013) MAL_MALLOC_FAIL));
890 : }
891 : }
892 :
893 185 : refs = sa_list(m->sa);
894 185 : rel = rel_read(m, expr, &pos, refs);
895 185 : if (rel)
896 181 : rel = sql_processrelation(m, rel, 0, 1, 0, 0);
897 181 : if (!rel) {
898 4 : if (strlen(m->errstr) > 6 && m->errstr[5] == '!')
899 4 : msg = createException(SQL, "RAstatement2", "%s", m->errstr);
900 : else
901 0 : msg = createException(SQL, "RAstatement2", SQLSTATE(42000) "%s", m->errstr);
902 181 : } else if (rel && types && is_simple_project(rel->op)) { /* Test if types match */
903 181 : list *types_list = sa_list(m->sa);
904 180 : str token, rest;
905 :
906 1582 : for (token = strtok_r(types, "%", &rest); token; token = strtok_r(NULL, "%", &rest))
907 1402 : list_append(types_list, token);
908 :
909 181 : if (list_length(types_list) != list_length(rel->exps))
910 0 : msg = createException(SQL, "RAstatement2", SQLSTATE(42000) "The number of projections don't match between the generated plan and the expected one: %d != %d",
911 : list_length(types_list), list_length(rel->exps));
912 : else {
913 181 : int i = 1;
914 1583 : for (node *n = rel->exps->h, *m = types_list->h ; n && m && !msg ; n = n->next, m = m->next) {
915 1402 : sql_exp *e = n->data, *ne = NULL;
916 1402 : sql_subtype *t = exp_subtype(e), et;
917 :
918 1402 : subtype_from_string(be->mvc, &et, m->data);
919 1402 : if (!is_subtype(t, &et) && (ne = exp_check_type(be->mvc, &et, rel, e, type_equal)) == NULL) {
920 0 : str got = sql_subtype_string(be->mvc->ta, t), expected = (str) m->data;
921 0 : if (!got)
922 0 : msg = createException(SQL, "RAstatement2", SQLSTATE(HY013) MAL_MALLOC_FAIL);
923 0 : msg = createException(SQL, "RAstatement2", SQLSTATE(42000) "Parameter %d has wrong SQL type, expected %s, but got %s instead", i, expected, got);
924 : }
925 0 : if (ne) {
926 3 : exp_setname(be->mvc, ne, exp_relname(e), exp_name(e));
927 3 : n->data = ne;
928 : }
929 1402 : i++;
930 : }
931 : }
932 : }
933 185 : if (!msg && monet5_create_relational_function(m, mod, nme, rel, NULL, ops, 0) < 0)
934 0 : msg = createException(SQL, "RAstatement2", "%s", m->errstr);
935 185 : rel_destroy(rel);
936 185 : return RAstatement2_return(be, m, nlevels, gvars, gentries, msg);
937 : }
938 :
939 : str
940 185 : RAstatementEnd(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
941 : {
942 185 : backend *be = NULL;
943 185 : mvc *m = NULL;
944 185 : str msg = MAL_SUCCEED;
945 :
946 185 : (void) stk;
947 185 : (void) pci;
948 185 : if ((msg = getSQLContext(cntxt, mb, &m, &be)) != NULL)
949 : return msg;
950 185 : if ((msg = checkSQLContext(cntxt)) != NULL)
951 : return msg;
952 :
953 185 : sqlcleanup(be, 0);
954 185 : return SQLautocommit(m);
955 : }
|