📄 vdbeaux.c
字号:
** 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); for(i=0; i<p->nAgg; i++){ sqlite3VdbeAggReset(0, &p->apAgg[i], 0); } p->contextStack = 0; p->contextStackDepth = 0; p->contextStackTop = 0; sqliteFree(p->zErrMsg); p->zErrMsg = 0;}/*** Set the number of result columns that will be returned by this SQL** statement. This is now set at compile time, rather than during** execution of the vdbe program so that sqlite3_column_count() can** be called on an SQL statement before sqlite3_step().*/void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ Mem *pColName; int n; assert( 0==p->nResColumn ); p->nResColumn = nResColumn; n = nResColumn*2; p->aColName = pColName = (Mem*)sqliteMalloc( sizeof(Mem)*n ); if( p->aColName==0 ) return; while( n-- > 0 ){ (pColName++)->flags = MEM_Null; }}/*** Set the name of the idx'th column to be returned by the SQL statement.** zName must be a pointer to a nul terminated string.**** This call must be made after a call to sqlite3VdbeSetNumCols().**** If N==P3_STATIC it means that zName is a pointer to a constant static** string and we can just copy the pointer. If it is P3_DYNAMIC, then ** the string is freed using sqliteFree() when the vdbe is finished with** it. Otherwise, N bytes of zName are copied.*/int sqlite3VdbeSetColName(Vdbe *p, int idx, const char *zName, int N){ int rc; Mem *pColName; assert( idx<(2*p->nResColumn) ); if( sqlite3_malloc_failed ) return SQLITE_NOMEM; assert( p->aColName!=0 ); pColName = &(p->aColName[idx]); if( N==P3_DYNAMIC || N==P3_STATIC ){ rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, SQLITE_STATIC); }else{ rc = sqlite3VdbeMemSetStr(pColName, zName, N, SQLITE_UTF8,SQLITE_TRANSIENT); } if( rc==SQLITE_OK && N==P3_DYNAMIC ){ pColName->flags = (pColName->flags&(~MEM_Static))|MEM_Dyn; pColName->xDel = 0; } return rc;}/*** A read or write transaction may or may not be active on database handle** db. If a transaction is active, commit it. If there is a** write-transaction spanning more than one database file, this routine** takes care of the master journal trickery.*/static int vdbeCommit(sqlite3 *db){ int i; int nTrans = 0; /* Number of databases with an active write-transaction */ int rc = SQLITE_OK; int needXcommit = 0; for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt && sqlite3BtreeIsInTrans(pBt) ){ needXcommit = 1; if( i!=1 ) nTrans++; } } /* If there are any write-transactions at all, invoke the commit hook */ if( needXcommit && db->xCommitCallback ){ int rc; sqlite3SafetyOff(db); rc = db->xCommitCallback(db->pCommitArg); sqlite3SafetyOn(db); if( rc ){ return SQLITE_CONSTRAINT; } } /* The simple case - no more than one database file (not counting the ** TEMP database) has a transaction active. There is no need for the ** master-journal. ** ** If the return value of sqlite3BtreeGetFilename() is a zero length ** string, it means the main database is :memory:. In that case we do ** not support atomic multi-file commits, so use the simple case then ** too. */ if( 0==strlen(sqlite3BtreeGetFilename(db->aDb[0].pBt)) || nTrans<=1 ){ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ rc = sqlite3BtreeSync(pBt, 0); } } /* Do the commit only if all databases successfully synced */ if( rc==SQLITE_OK ){ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ sqlite3BtreeCommit(pBt); } } } } /* The complex case - There is a multi-file write-transaction active. ** This requires a master journal file to ensure the transaction is ** committed atomicly. */#ifndef SQLITE_OMIT_DISKIO else{ char *zMaster = 0; /* File-name for the master journal */ char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt); OsFile master; /* Select a master journal file name */ do { u32 random; sqliteFree(zMaster); sqlite3Randomness(sizeof(random), &random); zMaster = sqlite3MPrintf("%s-mj%08X", zMainFile, random&0x7fffffff); if( !zMaster ){ return SQLITE_NOMEM; } }while( sqlite3OsFileExists(zMaster) ); /* Open the master journal. */ memset(&master, 0, sizeof(master)); rc = sqlite3OsOpenExclusive(zMaster, &master, 0); if( rc!=SQLITE_OK ){ sqliteFree(zMaster); return rc; } /* Write the name of each database file in the transaction into the new ** master journal file. If an error occurs at this point close ** and delete the master journal file. All the individual journal files ** still have 'null' as the master journal pointer, so they will roll ** back independently if a failure occurs. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( i==1 ) continue; /* Ignore the TEMP database */ if( pBt && sqlite3BtreeIsInTrans(pBt) ){ char const *zFile = sqlite3BtreeGetJournalname(pBt); if( zFile[0]==0 ) continue; /* Ignore :memory: databases */ rc = sqlite3OsWrite(&master, zFile, strlen(zFile)+1); if( rc!=SQLITE_OK ){ sqlite3OsClose(&master); sqlite3OsDelete(zMaster); sqliteFree(zMaster); return rc; } } } /* Sync the master journal file. Before doing this, open the directory ** the master journal file is store in so that it gets synced too. */ zMainFile = sqlite3BtreeGetDirname(db->aDb[0].pBt); rc = sqlite3OsOpenDirectory(zMainFile, &master); if( rc!=SQLITE_OK || (rc = sqlite3OsSync(&master))!=SQLITE_OK ){ sqlite3OsClose(&master); sqlite3OsDelete(zMaster); sqliteFree(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 sqlite3BtreeSync(), ** 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; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt && sqlite3BtreeIsInTrans(pBt) ){ rc = sqlite3BtreeSync(pBt, zMaster); if( rc!=SQLITE_OK ){ sqlite3OsClose(&master); sqliteFree(zMaster); return rc; } } } sqlite3OsClose(&master); /* 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(zMaster); assert( rc==SQLITE_OK ); sqliteFree(zMaster); zMaster = 0; rc = sqlite3OsSyncDirectory(zMainFile); if( rc!=SQLITE_OK ){ /* This is not good. The master journal file has been deleted, but ** the directory sync failed. There is no completely safe course of ** action from here. The individual journals contain the name of the ** master journal file, but there is no way of knowing if that ** master journal exists now or if it will exist after the operating ** system crash that may follow the fsync() failure. */ return rc; } /* All files and directories have already been synced, so the following ** calls to sqlite3BtreeCommit() are only closing files and deleting ** 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. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ sqlite3BtreeCommit(pBt); } } }#endif return rc;}/*** 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 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/*** 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( p->pc<0 ){ /* No commit or rollback needed if the program never started */ }else 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 && 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 ){ 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 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){ if( p->magic!=VDBE_MAGIC_RUN && p->magic!=VDBE_MAGIC_HALT ){ sqlite3Error(p->db, SQLITE_MISUSE, 0); return SQLITE_MISUSE; } /* 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. */ sqlite3VdbeHalt(p); /* 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 ){ sqlite3Error(p->db, p->rc, "%s", p->zErrMsg); sqliteFree(p->zErrMsg); p->zErrMsg = 0; }else if( p->rc ){ sqlite3Error(p->db, p->rc, 0); }else{ sqlite3Error(p->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(p->db, p->rc, 0); } /* Reclaim all memory used by the VDBE */ Cleanup(p); /* Save profiling information from this VDBE run. */ assert( p->pTos<&p->aStack[p->pc<0?0:p->pc] || sqlite3_malloc_failed==1 );#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; p->aborted = 0; if( p->rc==SQLITE_SCHEMA ){ sqlite3ResetInternalSchema(p->db, 0); } return p->rc;} /*** 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); }else if( p->magic!=VDBE_MAGIC_INIT ){ return SQLITE_MISUSE; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -