/* ** Clean up the VM after execution. ** ** This routine will automatically close any cursors, lists, and/or ** sorters that were left open. It also deletes the values of ** variables in the aVar[] array. */ static void Cleanup(Vdbe *p){ int i; if( p->aStack ){ releaseMemArray(p->aStack, 1 + (p->pTos - p->aStack)); p->pTos = &p->aStack[-1]; } closeAllCursors(p); releaseMemArray(p->aMem, p->nMem); if( p->pList ){ sqlite3VdbeKeylistFree(p->pList); p->pList = 0; } if( p->contextStack ){ for(i=0; i<p->contextStackTop; i++){ sqlite3VdbeKeylistFree(p->contextStack[i].pList); } sqliteFree(p->contextStack); } sqlite3VdbeSorterReset(p); sqlite3VdbeAggReset(0, &p->agg, 0); p->contextStack = 0; p->contextStackDepth = 0; p->contextStackTop = 0; sqliteFree(p->zErrMsg); p->zErrMsg = 0; }
/* ** Find every active VM other than pVdbe and change its status to ** aborted. This happens when one VM causes a rollback due to an ** ON CONFLICT ROLLBACK clause (for example). The other VMs must be ** aborted so that they do not have data rolled out from underneath ** them leading to a segfault. */ static void abortOtherActiveVdbes(Vdbe *pVdbe){ Vdbe *pOther; for(pOther=pVdbe->db->pVdbe; pOther; pOther=pOther->pNext){ if( pOther==pVdbe ) continue; if( pOther->magic!=VDBE_MAGIC_RUN || pOther->pc<0 ) continue; closeAllCursors(pOther); pOther->aborted = 1; } }
/* ** This routine is called the when a VDBE tries to halt. If the VDBE ** has made changes and is in autocommit mode, then commit those ** changes. If a rollback is needed, then do the rollback. ** ** This routine is the only way to move the state of a VM from ** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT. ** ** Return an error code. If the commit could not complete because of ** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it ** means the close did not happen and needs to be repeated. */ int sqlite3VdbeHalt(Vdbe *p){ sqlite3 *db = p->db; int i; int (*xFunc)(Btree *pBt) = 0; /* Function to call on each btree backend */ if( p->magic!=VDBE_MAGIC_RUN ){ /* Already halted. Nothing to do. */ assert( p->magic==VDBE_MAGIC_HALT ); return SQLITE_OK; } closeAllCursors(p); checkActiveVdbeCnt(db); if( db->autoCommit && db->activeVdbeCnt==1 ){ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ /* The auto-commit flag is true, there are no other active queries ** using this handle and the vdbe program was successful or hit an ** 'OR FAIL' constraint. This means a commit is required. */ int rc = vdbeCommit(db); if( rc==SQLITE_BUSY ){ return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ p->rc = rc; xFunc = sqlite3BtreeRollback; } }else{ xFunc = sqlite3BtreeRollback; } }else{ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ xFunc = sqlite3BtreeCommitStmt; }else if( p->errorAction==OE_Abort ){ xFunc = sqlite3BtreeRollbackStmt; }else{ xFunc = sqlite3BtreeRollback; db->autoCommit = 1; abortOtherActiveVdbes(p); } } /* If xFunc is not NULL, then it is one of sqlite3BtreeRollback, ** sqlite3BtreeRollbackStmt or sqlite3BtreeCommitStmt. Call it once on ** each backend. If an error occurs and the return code is still ** SQLITE_OK, set the return code to the new error value. */ for(i=0; xFunc && i<db->nDb; i++){ int rc; Btree *pBt = db->aDb[i].pBt; if( pBt ){ rc = xFunc(pBt); if( p->rc==SQLITE_OK ) p->rc = rc; } } /* If this was an INSERT, UPDATE or DELETE, set the change counter. */ if( p->changeCntOn ){ if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){ sqlite3VdbeSetChanges(db, p->nChange); }else{ sqlite3VdbeSetChanges(db, 0); } p->nChange = 0; } /* Rollback or commit any schema changes that occurred. */ if( p->rc!=SQLITE_OK ){ sqlite3RollbackInternalChanges(db); }else if( db->flags & SQLITE_InternChanges ){ sqlite3CommitInternalChanges(db); } /* We have successfully halted and closed the VM. Record this fact. */ if( p->pc>=0 ){ db->activeVdbeCnt--; } p->magic = VDBE_MAGIC_HALT; checkActiveVdbeCnt(db); return SQLITE_OK; }
/* ** Clean up the VM after execution. ** ** This routine will automatically close any cursors, lists, and/or ** sorters that were left open. It also deletes the values of ** variables in the azVariable[] array. */ static void Cleanup(Vdbe *p){ int i; if( p->aStack ){ Mem *pTos = p->pTos; while( pTos>=p->aStack ){ if( pTos->flags & MEM_Dyn ){ sqliteFree(pTos->z); } pTos--; } p->pTos = pTos; } closeAllCursors(p); if( p->aMem ){ for(i=0; i<p->nMem; i++){ if( p->aMem[i].flags & MEM_Dyn ){ sqliteFree(p->aMem[i].z); } } } sqliteFree(p->aMem); p->aMem = 0; p->nMem = 0; if( p->pList ){ sqliteVdbeKeylistFree(p->pList); p->pList = 0; } sqliteVdbeSorterReset(p); if( p->pFile ){ if( p->pFile!=stdin ) fclose(p->pFile); p->pFile = 0; } if( p->azField ){ sqliteFree(p->azField); p->azField = 0; } p->nField = 0; if( p->zLine ){ sqliteFree(p->zLine); p->zLine = 0; } p->nLineAlloc = 0; sqliteVdbeAggReset(&p->agg); if( p->aSet ){ for(i=0; i<p->nSet; i++){ sqliteHashClear(&p->aSet[i].hash); } } sqliteFree(p->aSet); p->aSet = 0; p->nSet = 0; if( p->keylistStack ){ int ii; for(ii = 0; ii < p->keylistStackDepth; ii++){ sqliteVdbeKeylistFree(p->keylistStack[ii]); } sqliteFree(p->keylistStack); p->keylistStackDepth = 0; p->keylistStack = 0; } sqliteFree(p->contextStack); p->contextStack = 0; sqliteFree(p->zErrMsg); p->zErrMsg = 0; }