📄 vdbeaux.c
字号:
needSync = 1; } rc = sqlite3OsWrite(pMaster, zFile, strlen(zFile)+1, offset); offset += strlen(zFile)+1; if( rc!=SQLITE_OK ){ sqlite3OsCloseFree(pMaster); sqlite3OsDelete(pVfs, zMaster, 0); sqlite3DbFree(db, zMaster); return rc; } } } /* Sync the master journal file. If the IOCAP_SEQUENTIAL device ** flag is set this is not required. */ zMainFile = sqlite3BtreeGetDirname(db->aDb[0].pBt); if( (needSync && (0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)) && (rc=sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL))!=SQLITE_OK) ){ sqlite3OsCloseFree(pMaster); sqlite3OsDelete(pVfs, zMaster, 0); sqlite3DbFree(db, zMaster); return rc; } /* Sync all the db files involved in the transaction. The same call ** sets the master journal pointer in each individual journal. If ** an error occurs here, do not delete the master journal file. ** ** If the error occurs during the first call to ** sqlite3BtreeCommitPhaseOne(), then there is a chance that the ** master journal file will be orphaned. But we cannot delete it, ** in case the master journal file name was written into the journal ** file before the failure occured. */ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster); } } sqlite3OsCloseFree(pMaster); if( rc!=SQLITE_OK ){ sqlite3DbFree(db, zMaster); return rc; } /* Delete the master journal file. This commits the transaction. After ** doing this the directory is synced again before any individual ** transaction files are deleted. */ rc = sqlite3OsDelete(pVfs, zMaster, 1); sqlite3DbFree(db, zMaster); zMaster = 0; if( rc ){ return rc; } /* All files and directories have already been synced, so the following ** calls to sqlite3BtreeCommitPhaseTwo() are only closing files and ** deleting or truncating journals. If something goes wrong while ** this is happening we don't really care. The integrity of the ** transaction is already guaranteed, but some stray 'cold' journals ** may be lying around. Returning an error code won't help matters. */ disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ sqlite3BtreeCommitPhaseTwo(pBt); } } sqlite3EndBenignMalloc(); enable_simulated_io_errors(); sqlite3VtabCommit(db); }#endif return rc;}/* ** This routine checks that the sqlite3.activeVdbeCnt count variable** matches the number of vdbe's in the list sqlite3.pVdbe that are** currently active. An assertion fails if the two counts do not match.** This is an internal self-check only - it is not an essential processing** step.**** This is a no-op if NDEBUG is defined.*/#ifndef NDEBUGstatic void checkActiveVdbeCnt(sqlite3 *db){ Vdbe *p; int cnt = 0; p = db->pVdbe; while( p ){ if( p->magic==VDBE_MAGIC_RUN && p->pc>=0 ){ cnt++; } p = p->pNext; } assert( cnt==db->activeVdbeCnt );}#else#define checkActiveVdbeCnt(x)#endif/*** For every Btree that in database connection db which ** has been modified, "trip" or invalidate each cursor in** that Btree might have been modified so that the cursor** can never be used again. This happens when a rollback*** occurs. We have to trip all the other cursors, even** cursor from other VMs in different database connections,** so that none of them try to use the data at which they** were pointing and which now may have been changed due** to the rollback.**** Remember that a rollback can delete tables complete and** reorder rootpages. So it is not sufficient just to save** the state of the cursor. We have to invalidate the cursor** so that it is never used again.*/static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){ int i; for(i=0; i<db->nDb; i++){ Btree *p = db->aDb[i].pBt; if( p && sqlite3BtreeIsInTrans(p) ){ sqlite3BtreeTripAllCursors(p, SQLITE_ABORT); } }}/*** 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. It is harmless to** call this on a VM that is in the SQLITE_MAGIC_HALT state.**** 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 */ int isSpecialError; /* Set to true if SQLITE_NOMEM or IOERR */ /* This function contains the logic that determines if a statement or ** transaction will be committed or rolled back as a result of the ** execution of this virtual machine. ** ** If any of the following errors occur: ** ** SQLITE_NOMEM ** SQLITE_IOERR ** SQLITE_FULL ** SQLITE_INTERRUPT ** ** Then the internal cache might have been left in an inconsistent ** state. We need to rollback the statement transaction, if there is ** one, or the complete transaction if there is no statement transaction. */ if( p->db->mallocFailed ){ p->rc = SQLITE_NOMEM; } closeAllCursorsExceptActiveVtabs(p); if( p->magic!=VDBE_MAGIC_RUN ){ return SQLITE_OK; } checkActiveVdbeCnt(db); /* No commit or rollback needed if the program never started */ if( p->pc>=0 ){ int mrc; /* Primary error code from p->rc */ /* Lock all btrees used by the statement */ sqlite3BtreeMutexArrayEnter(&p->aMutex); /* Check for one of the special errors */ mrc = p->rc & 0xff; isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; if( isSpecialError ){ /* This loop does static analysis of the query to see which of the ** following three categories it falls into: ** ** Read-only ** Query with statement journal ** Query without statement journal ** ** We could do something more elegant than this static analysis (i.e. ** store the type of query as part of the compliation phase), but ** handling malloc() or IO failure is a fairly obscure edge case so ** this is probably easier. Todo: Might be an opportunity to reduce ** code size a very small amount though... */ int notReadOnly = 0; int isStatement = 0; assert(p->aOp || p->nOp==0); for(i=0; i<p->nOp; i++){ switch( p->aOp[i].opcode ){ case OP_Transaction: notReadOnly |= p->aOp[i].p2; break; case OP_Statement: isStatement = 1; break; } } /* If the query was read-only, we need do no rollback at all. Otherwise, ** proceed with the special handling. */ if( notReadOnly || mrc!=SQLITE_INTERRUPT ){ if( p->rc==SQLITE_IOERR_BLOCKED && isStatement ){ xFunc = sqlite3BtreeRollbackStmt; p->rc = SQLITE_BUSY; } else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && isStatement ){ xFunc = sqlite3BtreeRollbackStmt; }else{ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. */ invalidateCursorsOnModifiedBtrees(db); sqlite3RollbackAll(db); db->autoCommit = 1; } } } /* If the auto-commit flag is set and this is the only active vdbe, then ** we do either a commit or rollback of the current transaction. ** ** Note: This block also runs if one of the special errors handled ** above has occured. */ if( db->autoCommit && db->activeVdbeCnt==1 ){ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ /* The auto-commit flag is true, and the vdbe program was ** successful or hit an 'OR FAIL' constraint. This means a commit ** is required. */ int rc = vdbeCommit(db, p); if( rc==SQLITE_BUSY ){ sqlite3BtreeMutexArrayLeave(&p->aMutex); return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ p->rc = rc; sqlite3RollbackAll(db); }else{ sqlite3CommitInternalChanges(db); } }else{ sqlite3RollbackAll(db); } }else if( !xFunc ){ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ if( p->openedStatement ){ xFunc = sqlite3BtreeCommitStmt; } }else if( p->errorAction==OE_Abort ){ xFunc = sqlite3BtreeRollbackStmt; }else{ invalidateCursorsOnModifiedBtrees(db); sqlite3RollbackAll(db); db->autoCommit = 1; } } /* If xFunc is not NULL, then it is one of 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. */ assert(!xFunc || xFunc==sqlite3BtreeCommitStmt || xFunc==sqlite3BtreeRollbackStmt ); for(i=0; xFunc && i<db->nDb; i++){ int rc; Btree *pBt = db->aDb[i].pBt; if( pBt ){ rc = xFunc(pBt); if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){ p->rc = rc; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; } } } /* If this was an INSERT, UPDATE or DELETE and the statement was committed, ** set the change counter. */ if( p->changeCntOn && p->pc>=0 ){ 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 && db->flags&SQLITE_InternChanges ){ sqlite3ResetInternalSchema(db, 0); db->flags = (db->flags | SQLITE_InternChanges); } /* Release the locks */ sqlite3BtreeMutexArrayLeave(&p->aMutex); } /* We have successfully halted and closed the VM. Record this fact. */ if( p->pc>=0 ){ db->activeVdbeCnt--; } p->magic = VDBE_MAGIC_HALT; checkActiveVdbeCnt(db); if( p->db->mallocFailed ){ p->rc = SQLITE_NOMEM; } return SQLITE_OK;}/*** Each VDBE holds the result of the most recent sqlite3_step() call** in p->rc. This routine sets that result back to SQLITE_OK.*/void sqlite3VdbeResetStepResult(Vdbe *p){ p->rc = SQLITE_OK;}/*** Clean up a VDBE after execution but do not delete the VDBE just yet.** Write any error messages into *pzErrMsg. Return the result code.**** After this routine is run, the VDBE should be ready to be executed** again.**** To look at it another way, this routine resets the state of the** virtual machine from VDBE_MAGIC_RUN or VDBE_MAGIC_HALT back to** VDBE_MAGIC_INIT.*/int sqlite3VdbeReset(Vdbe *p){ sqlite3 *db; db = p->db; /* If the VM did not run to completion or if it encountered an ** error, then it might not have been halted properly. So halt ** it now. */ (void)sqlite3SafetyOn(db); sqlite3VdbeHalt(p); (void)sqlite3SafetyOff(db); /* If the VDBE has be run even partially, then transfer the error code ** and error message from the VDBE into the main database structure. But ** if the VDBE has just been set to run but has not actually executed any ** instructions yet, leave the main database error information unchanged. */ if( p->pc>=0 ){ if( p->zErrMsg ){ sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT); db->errCode = p->rc; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; }else if( p->rc ){ sqlite3Error(db, p->rc, 0); }else{ sqlite3Error(db, SQLITE_OK, 0); } }else if( p->rc && p->expired ){ /* The expired flag was set on the VDBE before the first call ** to sqlite3_step(). For consistency (since sqlite3_step() was ** called), set the database error in this case as well. */ sqlite3Error(db, p->rc, 0); sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; } /* Reclaim all memory used by the VDBE */ Cleanup(p); /* Save profiling information from this VDBE run. */#ifdef VDBE_PROFILE { FILE *out = fopen("vdbe_profile.out", "a"); if( out ){ int i; fprintf(out, "---- "); for(i=0; i<p->nOp; i++){ fprintf(out, "%02x", p->aOp[i].opcode); } fprintf(out, "\n"); for(i=0; i<p->nOp; i++){ fprintf(out, "%6d %10lld %8lld ", p->aOp[i].cnt, p->aOp[i].cycles, p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 ); sqlite3VdbePrintOp(out, i, &p->aOp[i]); } fclose(out); } }#endif p->magic = VDBE_MAGIC_INIT; return p->rc & db->errMask;} /*** Clean up and delete a VDBE after execution. Return an integer which is** the result code. Write any error message text into *pzErrMsg.*/int sqlite3VdbeFinalize(Vdbe *p){ int rc = SQLITE_OK; if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ rc = sqlite3VdbeReset(p); assert( (rc & p->db->errMask)==rc ); }else if( p->magic!=VDBE_MAGIC_INIT ){ return SQLITE_MISUSE; } sqlite3VdbeDelete(p); return rc;}/*** Call the destructor for each auxdata entry in pVdbeFunc for which** the corresponding bit in mask is clear. Auxdata entries beyond 31
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -