int mvc_rollback(mvc *m, int chain, const char *name) { int res = 0; sql_trans *tr = m->session->tr; if (mvc_debug) fprintf(stderr, "#mvc_rollback %s\n", (name) ? name : ""); assert(tr); assert(m->session->active); /* only abort an active transaction */ store_lock(); if (m->qc) qc_clean(m->qc); if (name && name[0] != '\0') { while (tr && (!tr->name || strcmp(tr->name, name) != 0)) tr = tr->parent; if (!tr) { (void)sql_error(m, 010, "ROLLBACK: no such savepoint: '%s'", name); m->session->status = -1; store_unlock(); return -1; } tr = m->session->tr; while (!tr->name || strcmp(tr->name, name) != 0) { /* make sure we do not reuse changed data */ if (tr->wtime) tr->status = 1; tr = sql_trans_destroy(tr); } m->session->tr = tr; /* restart at savepoint */ m->session->status = tr->status; if (tr->name) tr->name = NULL; m->session->schema = find_sql_schema(m->session->tr, m->session->schema_name); } else if (tr->parent) { /* first release all intermediate savepoints */ while (tr->parent->parent != NULL) { tr = sql_trans_destroy(tr); } m->session-> tr = tr; /* make sure we do not reuse changed data */ if (tr->wtime) tr->status = 1; sql_trans_end(m->session); if (chain) sql_trans_begin(m->session); } store_unlock(); m->type = Q_TRANS; if (mvc_debug) fprintf(stderr, "#mvc_rollback %s done\n", (name) ? name : ""); return res; }
void mvc_trans(mvc *m) { int schema_changed = 0, err = m->session->status; assert(!m->session->active); /* can only start a new transaction */ store_lock(); schema_changed = sql_trans_begin(m->session); if (m->qc && (schema_changed || m->qc->nr > m->cache || err)){ if (schema_changed || err) { int seqnr = m->qc->id; if (m->qc) qc_destroy(m->qc); m->qc = qc_create(m->clientid, seqnr); } else { /* clean all but the prepared statements */ qc_clean(m->qc); } } store_unlock(); }
int mvc_commit(mvc *m, int chain, const char *name) { sql_trans *cur, *tr = m->session->tr; int ok = SQL_OK;//, wait = 0; assert(tr); assert(m->session->active); /* only commit an active transaction */ if (mvc_debug) fprintf(stderr, "#mvc_commit %s\n", (name) ? name : ""); if (m->session->status < 0) { (void)sql_error(m, 010, "40000!COMMIT: transaction is aborted, will ROLLBACK instead"); mvc_rollback(m, chain, name); return -1; } /* savepoint then simply make a copy of the current transaction */ if (name && name[0] != '\0') { sql_trans *tr = m->session->tr; if (mvc_debug) fprintf(stderr, "#mvc_savepoint\n"); store_lock(); m->session->tr = sql_trans_create(m->session->stk, tr, name); store_unlock(); m->type = Q_TRANS; if (m->qc) /* clean query cache, protect against concurrent access on the hash tables (when functions already exists, concurrent mal will build up the hash (not copied in the trans dup)) */ qc_clean(m->qc); m->session->schema = find_sql_schema(m->session->tr, m->session->schema_name); if (mvc_debug) fprintf(stderr, "#mvc_commit %s done\n", name); return 0; } /* first release all intermediate savepoints */ cur = tr; tr = tr->parent; if (tr->parent) { store_lock(); while (tr->parent != NULL && ok == SQL_OK) { tr = sql_trans_destroy(tr); } store_unlock(); } cur -> parent = tr; tr = cur; store_lock(); /* if there is nothing to commit reuse the current transaction */ if (tr->wtime == 0) { if (!chain) sql_trans_end(m->session); m->type = Q_TRANS; if (mvc_debug) fprintf(stderr, "#mvc_commit %s done\n", (name) ? name : ""); store_unlock(); return 0; } /* while (tr->schema_updates && store_nr_active > 1) { store_unlock(); MT_sleep_ms(100); wait += 100; if (wait > 1000) { (void)sql_error(m, 010, "40000!COMMIT: transaction is aborted because of DDL concurrency conflicts, will ROLLBACK instead"); mvc_rollback(m, chain, name); return -1; } store_lock(); } * */ /* validation phase */ if (sql_trans_validate(tr)) { if ((ok = sql_trans_commit(tr)) != SQL_OK) { char *msg = sql_message("40000!COMMIT: transaction commit failed (perhaps your disk is full?) exiting (kernel error: %s)", GDKerrbuf); GDKfatal("%s", msg); _DELETE(msg); } } else { store_unlock(); (void)sql_error(m, 010, "40000!COMMIT: transaction is aborted because of concurrency conflicts, will ROLLBACK instead"); mvc_rollback(m, chain, name); return -1; } sql_trans_end(m->session); if (chain) sql_trans_begin(m->session); store_unlock(); m->type = Q_TRANS; if (mvc_debug) fprintf(stderr, "#mvc_commit %s done\n", (name) ? name : ""); return ok; }
str SQLengineIntern(Client c, backend *be) { str msg = MAL_SUCCEED; MalStkPtr oldglb = c->glb; char oldlang = be->language; mvc *m = be->mvc; InstrPtr p; MalBlkPtr mb; if (oldlang == 'X') { /* return directly from X-commands */ sqlcleanup(be->mvc, 0); return MAL_SUCCEED; } if (m->emod & mod_explain) { if (be->q && be->q->code) printFunction(c->fdout, ((Symbol) (be->q->code))->def, 0, LIST_MAL_NAME | LIST_MAL_VALUE | LIST_MAL_MAPI); else if (be->q) msg = createException(PARSE, "SQLparser", "%s", (*m->errstr) ? m->errstr : "39000!program contains errors"); else if (c->curprg->def) printFunction(c->fdout, c->curprg->def, 0, LIST_MAL_NAME | LIST_MAL_VALUE | LIST_MAL_MAPI); goto cleanup_engine; } if (m->emod & mod_dot) { if (be->q && be->q->code) showFlowGraph(((Symbol) (be->q->code))->def, 0, "stdout-mapi"); else if (be->q) msg = createException(PARSE, "SQLparser", "%s", (*m->errstr) ? m->errstr : "39000!program contains errors"); else if (c->curprg->def) showFlowGraph(c->curprg->def, 0, "stdout-mapi"); goto cleanup_engine; } #ifdef SQL_SCENARIO_DEBUG mnstr_printf(GDKout, "#Ready to execute SQL statement\n"); #endif if (c->curprg->def->stop == 1) { sqlcleanup(be->mvc, 0); return MAL_SUCCEED; } if (m->emode == m_inplace) { msg = SQLexecutePrepared(c, be, be->q); goto cleanup_engine; } if (m->emode == m_prepare) goto cleanup_engine; assert(c->glb == 0 || c->glb == oldglb); /* detect leak */ c->glb = 0; be->language = 'D'; /* * The code below is copied from MALengine, which handles execution * in the context of a user global environment. We have a private * environment. */ if (MALcommentsOnly(c->curprg->def)) { msg = MAL_SUCCEED; } else { msg = (str) runMAL(c, c->curprg->def, 0, 0); } cleanup_engine: if (m->type == Q_SCHEMA) qc_clean(m->qc); if (msg) { enum malexception type = getExceptionType(msg); if (type == OPTIMIZER) { MSresetInstructions(c->curprg->def, 1); freeVariables(c, c->curprg->def, NULL, be->vtop); be->language = oldlang; assert(c->glb == 0 || c->glb == oldglb); /* detect leak */ c->glb = oldglb; if ( msg) GDKfree(msg); return SQLrecompile(c, be); // retry compilation } else { /* don't print exception decoration, just the message */ char *n = NULL; char *o = msg; while ((n = strchr(o, '\n')) != NULL) { *n = '\0'; mnstr_printf(c->fdout, "!%s\n", getExceptionMessage(o)); *n++ = '\n'; o = n; } if (*o != 0) mnstr_printf(c->fdout, "!%s\n", getExceptionMessage(o)); } showErrors(c); m->session->status = -10; } mb = c->curprg->def; if (m->type != Q_SCHEMA && be->q && msg) { qc_delete(m->qc, be->q); } else if (m->type != Q_SCHEMA && be->q && mb && varGetProp(mb, getArg(p = getInstrPtr(mb, 0), 0), runonceProp)) { msg = SQLCacheRemove(c, getFunctionId(p)); qc_delete(be->mvc->qc, be->q); ///* this should invalidate any match */ //be->q->key= -1; //be->q->paramlen = -1; ///* qc_delete(be->q) */ } be->q = NULL; sqlcleanup(be->mvc, (!msg) ? 0 : -1); MSresetInstructions(c->curprg->def, 1); freeVariables(c, c->curprg->def, NULL, be->vtop); be->language = oldlang; /* * Any error encountered during execution should block further processing * unless auto_commit has been set. */ assert(c->glb == 0 || c->glb == oldglb); /* detect leak */ c->glb = oldglb; return msg; }