1. VDBE
1.1Opcode实例分析
1.1.1Opcode
Opcode的指令说明:
这里通过一个sql语句的指令来开始opcode的源码浅析,
第一句addr0:init的p2为14,所以跳转到addr14;
Addr14: transaction p1为0,所以是maindatabase;p2为0,所以是“读事务”;P3为3,p5为1,这些与schema相关;
Addr15:Goto,14之后顺序向15执行,goto语句跳转到P2的地址1;
Addr1:OpenRead打开数据库表的只读游标,P2指向root page的页号3,P5这里不限定P2的值,P3表示当前是main数据库,P1则是游标的ID,P4则表示当前表的列数8;
Addr2:Rewind表示循环查找,P1指向表的第一条记录,P2在表为空时进行跳转到13;
Addr3:Column进行列操作,P1是游标ID,P2是column id,p3存储P2的数据,P4做数据存储的备用,用来处理P4_MEM类型数据,P5是flag;
Addr11: ResultRow提供返回结果,r(p1)到r(p1+p2-1);
Addr12:Next来移动游标,P1是游标ID,p2是在有数据的时候要跳转的地址,p3p4p5暂略;
Addr13:Halt马上退出,释放游标,P1是返回码,P2和rollback相关,P4P5和错误消息相关。
为加深理解,再展示一个语句进行对比。
下面再结合代码对上面的语句进行分析。
1.1.1Init
Init代码实现和注释如下,其最常用的功能是跳转到P2,所以这段代码真正有意义的只有jump_to_p2这句。
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Opcode: Init P1 P2 * P4 *
** Synopsis: Start at P2
**
** Programs contain a single instance ofthis opcode as the very first
** opcode.
**
** If tracing is enabled (by thesqlite3_trace()) interface,then
** the UTF-8 string contained in P4 isemitted on the trace callback.
** Or if P4 is blank,use the stringreturned by sqlite3_sql().
**
** If P2 is not zero,jump toinstruction P2.
**
** Increment the value of P1 so thatOP_Once opcodes will jump the
** first time they are evaluated forthis run.
*/
case OP_Init: { /* jump */
char *zTrace;
int i;
/* If the P4 argument is not NULL,then it must be an sql commentstring.
** The "--" string is broken up to prevent false-positiveswith srcck1.c.
**
** This assert() provides evidence for:
** EVIDENCE-OF: R-50676-09860 The callback can compute the same textthat
** would have been returned by the legacy sqlite3_trace() interface by
** using the X argument when X begins with"--" and invoking
** sqlite3_expanded_sql(P) otherwise.
*/
assert( pOp->p4.z==0 || strncmp(pOp->p4.z,"-" "-",3)==0 );
assert( pOp==p->aOp ); /*Always instruction 0 */
#ifndef sqlITE_OMIT_TRACE
if( (db->mTrace & (sqlITE_TRACE_STMT|sqlITE_TRACE_LEGACY))!=0
&& !p->doingRerun
&& (zTrace = (pOp->p4.z ? pOp->p4.z : p->zsql))!=0
){
#ifndef sqlITE_OMIT_DEPRECATED
if( db->mTrace & sqlITE_TRACE_LEGACY ){
void (*x)(void*,const char*) = (void(*)(void*,constchar*))db->xTrace;
char *z = sqlite3VdbeExpandsql(p,zTrace);
x(db->pTraceArg,z);
sqlite3_free(z);
}else
#endif
{
(void)db->xTrace(sqlITE_TRACE_STMT,db->pTraceArg,p,zTrace);
}
}
#ifdef sqlITE_USE_FCNTL_TRACE
zTrace = (pOp->p4.z ? pOp->p4.z : p->zsql);
if( zTrace ){
int j;
for(j=0; j<db->nDb; j++){
if( DbMaskTest(p->btreeMask,j)==0 ) continue;
sqlite3_file_control(db,db->aDb[j].zDbSName,sqlITE_FCNTL_TRACE,zTrace);
}
}
#endif /* sqlITE_USE_FCNTL_TRACE */
#ifdef sqlITE_DEBUG
if( (db->flags & sqlITE_sqlTrace)!=0
&& (zTrace = (pOp->p4.z ? pOp->p4.z : p->zsql))!=0
){
sqlite3DebugPrintf("sql-trace: %s\n",zTrace);
}
#endif /* sqlITE_DEBUG */
#endif /* sqlITE_OMIT_TRACE */
assert( pOp->p2>0 );
if( pOp->p1>=sqlite3GlobalConfig.iOnceResetThreshold ){
for(i=1; i<p->nOp; i++){
if( p->aOp[i].opcode==OP_Once ) p->aOp[i].p1 = 0;
}
pOp->p1 = 0;
}
pOp->p1++;
goto jump_to_p2;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
jump_to_p2:
pOp = &aOp[pOp->p2 - 1];
break;
}
1.1.2Transaction
Transaction调用流程如下,
Transaction指令是开始一个事务,P1为数据库,0是maindatabase;P2为读或写,0是“读事务”;P3P4P5,这些与schema相关。OP_Transaction的主要处理函数为sqlite3BtreeBeginTrans。
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Opcode: Transaction P1 P2 P3 P4 P5
**
** Begin a transaction on database P1 ifa transaction is not already
** active.
** If P2 is non-zero,then awrite-transaction is started,or if a
** read-transaction is already active,it is upgraded to a write-transaction.
** If P2 is zero,then aread-transaction is started.
**
** P1 is the index of the database fileon which the transaction is
** started. Index 0 is the main database file and index 1is the
** file used for temporary tables. Indices of 2 or more are used for
** attached databases.
**
** If a write-transaction is started andthe Vdbe.usesStmtJournal flag is
** true (this flag is set if the Vdbemay modify more than one row and may
** throw an ABORT exception),astatement transaction may also be opened.
** More specifically,a statementtransaction is opened iff the database
** connection is currently not inautocommit mode,or if there are other
** active statements. A statementtransaction allows the changes made by this
** VDBE to be rolled back after an errorwithout having to roll back the
** entire transaction. If no error isencountered,the statement transaction
** will automatically commit when theVDBE halts.
**
** If P5!=0 then this opcode also checksthe schema cookie against P3
** and the schema generation counteragainst P4.
** The cookie changes its value wheneverthe database schema changes.
** This operation is used to detect whenthat the cookie has changed
** and that the current process needs toreread the schema. If the schema
** cookie in P3 differs from the schemacookie in the database header or
** if the schema generation counter in P4differs from the current
** generation counter,then ansqlITE_SCHEMA error is raised and execution
** halts. The sqlite3_step() wrapper function mightthen reprepare the
** statement and rerun it from thebeginning.
*/
case OP_Transaction: {
Btree *pBt;
int iMeta;
int iGen;
assert( p->bIsReader );
assert( p->readOnly==0 || pOp->p2==0 );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( DbMaskTest(p->btreeMask,pOp->p1) );
if( pOp->p2 && (db->flags & sqlITE_QueryOnly)!=0 ){
rc = sqlITE_READONLY;
goto abort_due_to_error;
}
pBt = db->aDb[pOp->p1].pBt;
if( pBt ){
rc = sqlite3BtreeBeginTrans(pBt,pOp->p2);
testcase( rc==sqlITE_BUSY_SNAPSHOT );
testcase( rc==sqlITE_BUSY_RECOVERY );
if( rc!=sqlITE_OK ){
if( (rc&0xff)==sqlITE_BUSY ){
p->pc = (int)(pOp - aOp);
p->rc = rc;
goto vdbe_return;
}
goto abort_due_to_error;
}
if( pOp->p2 && p->usesStmtJournal
&& (db->autoCommit==0 || db->nVdbeRead>1)
){
assert( sqlite3BtreeIsInTrans(pBt) );
if( p->iStatement==0 ){
assert( db->nStatement>=0 && db->nSavepoint>=0 );
db->nStatement++;
p->iStatement = db->nSavepoint + db->nStatement;
}
rc = sqlite3VtabSavepoint(db,SAVEPOINT_BEGIN,p->iStatement-1);
if( rc==sqlITE_OK ){
rc = sqlite3BtreeBeginStmt(pBt,p->iStatement);
}
/* Store the current value of the database handles deferred constraint
** counter. If the statement transaction needs to be rolled back,
** the value of this counter needs to be restored too. */
p->nStmtDefCons = db->nDeferredCons;
p->nStmtDefImmCons = db->nDeferredImmCons;
}
/* Gather the schema version number for checking:
** IMPLEMENTATION-OF: R-03189-51135 As each sql statement runs,theschema
** version is checked to ensure that the schema has not changed sincethe
** sql statement was prepared.
*/
sqlite3BtreeGetMeta(pBt,BTREE_SCHEMA_VERSION,(u32 *)&iMeta);
iGen = db->aDb[pOp->p1].pSchema->iGeneration;
}else{
iGen = iMeta = 0;
}
assert( pOp->p5==0 || pOp->p4type==P4_INT32 );
if( pOp->p5 && (iMeta!=pOp->p3 || iGen!=pOp->p4.i) ){
sqlite3DbFree(db,p->zErrMsg);
p->zErrMsg = sqlite3DbStrDup(db,"database schema haschanged");
/* If the schema-cookie from the database file matches the cookie
** stored with the in-memory representation of the schema,do
** not reload the schema from the database file.
**
** If virtual-tables are in use,this is not just an optimization.
** Often,v-tables store their data in other sqlite tables,which
** are queried from within xNext() and other v-table methods using
** prepared queries. If such a query is out-of-date,we do not want to
** discard the database schema,as the user code implementing the
** v-table would have to be ready for the sqlite3_vtab structure itself
** to be invalidated whenever sqlite3_step() is called from within
** a v-table method.
*/
if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
sqlite3ResetOneSchema(db,pOp->p1);
}
p->expired = 1;
rc = sqlITE_SCHEMA;
}
if( rc ) goto abort_due_to_error;
break;
}
sqlite3BtreeBeginTrans开始一个新事务,wrflag为0是读,1是写,>=2是排他型,如果需要对数据库进行修改,比如调用sqlite3BtreeCreateTable、sqlite3BtreeInsert、sqlite3BtreeUpdateMeta类型的函数前,必须加写事务。
这个函数的处理逻辑就是数据库加锁的相关规则,需先在介绍里理解清楚各种锁和相互关系,再阅读代码。
///////////////////////////////////////////////////////////////////////////////////////////////////
int sqlite3BtreeBeginTrans(Btree *p,intwrflag){
BtShared *pBt = p->pBt;
int rc = sqlITE_OK;
sqlite3BtreeEnter(p); // 给btree加mutex
btreeIntegrity(p); //完整性检查,就是transcation相关值的检查
/* If the btree is already in a write-transaction,or it
** is already in a read-transaction and a read-transaction
** is requested,this is a no-op.
*/
if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ &&!wrflag) ){
goto trans_begun;
}
assert( pBt->inTransaction==TRANS_WRITE ||IfNotOmitAV(pBt->bDoTruncate)==0 );
/* Write transactions are not possible on a read-only database */
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
rc = sqlITE_READONLY;
goto trans_begun;
}
#ifndef sqlITE_OMIT_SHARED_CACHE
{
sqlite3 *pBlock = 0;
/* If another database handle has already opened a write transaction
** on this shared-btree structure and a second write transaction is
** requested,return sqlITE_LOCKED.
*/
if( (wrflag && pBt->inTransaction==TRANS_WRITE)
|| (pBt->btsFlags & BTS_PENDING)!=0
){
pBlock = pBt->pWriter->db;
}else if( wrflag>1 ){
BtLock *pIter;
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
if( pIter->pBtree!=p ){
pBlock = pIter->pBtree->db;
break;
}
}
}
if( pBlock ){
sqlite3ConnectionBlocked(p->db,pBlock);
rc = sqlITE_LOCKED_SHAREDCACHE;
goto trans_begun;
}
}
#endif
/* Any read-only or read-write transaction implies a read-lock on
** page 1. So if some other shared-cache client already has a write-lock
** on page 1,the transaction cannot be opened. */
rc = querySharedCacheTableLock(p,MASTER_ROOT,READ_LOCK); //共享缓存的处理
if( sqlITE_OK!=rc ) goto trans_begun;
pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
do {
/* Call lockBtree() until either pBt->pPage1 is populated or
** lockBtree() returns something other than sqlITE_OK. lockBtree()
** may return sqlITE_OK but leave pBt->pPage1 set to 0 if after
** reading page 1 it discovers that the page-size of the database
** file is not pBt->pageSize. In this case lockBtree() will update
** pBt->pageSize to the page-size of the file on disk.
*/
while( pBt->pPage1==0 && sqlITE_OK==(rc = lockBtree(pBt)) );
if( rc==sqlITE_OK && wrflag ){
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
rc = sqlITE_READONLY;
}else{
rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));//开始锁pager
if( rc==sqlITE_OK ){
rc = newDatabase(pBt);//加锁成功后,对于空的db,要添加头信息。
}
}
}
if( rc!=sqlITE_OK ){
unlockBtreeIfUnused(pBt);
}
}while( (rc&0xFF)==sqlITE_BUSY &&pBt->inTransaction==TRANS_NONE &&
btreeInvokeBusyHandler(pBt) );
if( rc==sqlITE_OK ){
if( p->inTrans==TRANS_NONE ){
pBt->nTransaction++;
#ifndef sqlITE_OMIT_SHARED_CACHE
if( p->sharable ){
assert( p->lock.pBtree==p && p->lock.iTable==1 );
p->lock.eLock = READ_LOCK;
p->lock.pNext = pBt->pLock;
pBt->pLock = &p->lock;
}
#endif
}
p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ);
if( p->inTrans>pBt->inTransaction ){
pBt->inTransaction = p->inTrans;
}
if( wrflag ){
MemPage *pPage1 = pBt->pPage1;
#ifndef sqlITE_OMIT_SHARED_CACHE
assert( !pBt->pWriter );
pBt->pWriter = p;
pBt->btsFlags &= ~BTS_EXCLUSIVE;
if( wrflag>1 ) pBt->btsFlags |= BTS_EXCLUSIVE;
#endif
/* If the db-size header field is incorrect (as it may be if an old
** client has been writing the database file),update it now. Doing
** this sooner rather than later means the database size can safely
** re-read the database size from page 1 if a savepoint or transaction
** rollback occurs within the transaction.
*/
if( pBt->nPage!=get4byte(&pPage1->aData[28]) ){
rc = sqlite3PagerWrite(pPage1->pDbPage);
if( rc==sqlITE_OK ){
put4byte(&pPage1->aData[28],pBt->nPage);
}
}
}
}
trans_begun:
if( rc==sqlITE_OK && wrflag ){
/* This call makes sure that the pager has the correct number of
** open savepoints. If the second parameter is greater than 0 and
** the sub-journal is not already open,then it will be opened here.
*/
rc = sqlite3PagerOpenSavepoint(pBt->pPager,p->db->nSavepoint);
}
btreeIntegrity(p);
sqlite3BtreeLeave(p); // 释放互斥锁。
return rc;
}
1.1.1Goto
Goto语句比较简单,就是跳转到P2,代码里改变PC值,再检查一下中断,
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Opcode: Goto * P2 * * *
**
** An unconditional jump to address P2.
** The next instruction executed will be
** the one at index P2 from thebeginning of
** the program.
**
** The P1 parameter is not actually usedby this opcode. However,it
** is sometimes set to 1 instead of 0 asa hint to the command-line shell
** that this Goto is the bottom of a loopand that the lines from P2 down
** to the current line should beindented for EXPLAIN output.
*/
case OP_Goto: { /* jump */
jump_to_p2_and_check_for_interrupt:
pOp = &aOp[pOp->p2 - 1];
/* Opcodes that are used as the bottom of a loop (OP_Next,OP_Prev,
** OP_VNext,OP_RowSetNext,or OP_SorterNext) all jump here upon
** completion. Check to see ifsqlite3_interrupt() has been called
** or if the progress callback needs to be invoked.
**
** This code uses unstructured "goto" statements and does notlook clean.
** But that is not due to sloppy coding habits. The code is written this
** way for performance,to avoid having to run the interrupt andprogress
** checks on every opcode. Thishelps sqlite3_step() to run about 1.5%
** faster according to "valgrind --tool=cachegrind" */
check_for_interrupt:
if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
#ifndef sqlITE_OMIT_PROGRESS_CALLBACK
/* Call the progress callback if it is configured and the requirednumber
** of VDBE ops have been executed (eithersince this invocation of
** sqlite3VdbeExec() or since last time the progress callback wascalled).
** If the progress callback returns non-zero,exit the virtual machinewith
** a return code sqlITE_ABORT.
*/
if( db->xProgress!=0 && nVmStep>=nProgressLimit ){
assert( db->nProgressOps!=0 );
nProgressLimit = nVmStep + db->nProgressOps -(nVmStep%db->nProgressOps);
if( db->xProgress(db->pProgressArg) ){
rc = sqlITE_INTERRUPT;
goto abort_due_to_error;
}
}
#endif
break;
}
1.1.2openRead
OpenRead打开数据库的只读游标,P1存放游标ID,P2指向page no.,P3是数据库,P4是表列数,P5是限定P2的。这个指令主要调用两个函数,allocateCursor分配一个db游标和一个btree游标,初始db游标,sqlite3BtreeCursor则初始btree游标。
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Opcode: OpenRead P1 P2 P3 P4 P5
** Synopsis: root=P2 iDb=P3
**
** Open a read-only cursor for thedatabase table whose root page is
** P2 in a database file. The database file is determined by P3.
** P3==0 means the main database,P3==1means the database used for
** temporary tables,and P3>1 meansused the corresponding attached
** database. Give the new cursor an identifier of P1. The P1
** values need not be contiguous but allP1 values should be small integers.
** It is an error for P1 to be negative.
**
** If P5!=0 then use the content ofregister P2 as the root page,not
** the value of P2 itself.
**
** There will be a read lock on thedatabase whenever there is an
** open cursor. If the database was unlocked prior to thisinstruction
** then a read lock is acquired as partof this instruction. A read
** lock allows other processes to readthe database but prohibits
** any other process from modifying thedatabase. The read lock is
** released when all cursors areclosed. If this instruction attempts
** to get a read lock but fails,thescript terminates with an
** sqlITE_BUSY error code.
**
** The P4 value may be either an integer(P4_INT32) or a pointer to
** a KeyInfo structure (P4_KEYINFO). Ifit is a pointer to a KeyInfo
** structure,then said structuredefines the content and collating
** sequence of the index being opened.Otherwise,if P4 is an integer
** value,it is set to the number ofcolumns in the table.
**
** See also: OpenWrite,ReopenIdx
*/
case OP_OpenRead:
case OP_OpenWrite:
assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 ||pOp->p5==OPFLAG_SEEKEQ );
assert( p->bIsReader );
assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx
|| p->readOnly==0 );
if( p->expired ){
rc = sqlITE_ABORT_ROLLBACK;
goto abort_due_to_error;
}
nField = 0;
pKeyInfo = 0;
p2 = pOp->p2;
iDb = pOp->p3;
assert( iDb>=0 && iDb<db->nDb );
assert( DbMaskTest(p->btreeMask,iDb) );
pDb = &db->aDb[iDb];
pX = pDb->pBt;
assert( pX!=0 );
if( pOp->opcode==OP_OpenWrite ){
assert( OPFLAG_FORDELETE==BTREE_FORDELETE );
wrFlag = BTREE_WRCSR | (pOp->p5 & OPFLAG_FORDELETE);
assert( sqlite3SchemaMutexHeld(db,iDb,0) );
if( pDb->pSchema->file_format < p->minWriteFileFormat ){
p->minWriteFileFormat = pDb->pSchema->file_format;
}
}else{
wrFlag = 0;
}
if( pOp->p5 & OPFLAG_P2ISREG ){
assert( p2>0 );
assert( p2<=(p->nMem+1 - p->nCursor) );
pIn2 = &aMem[p2];
assert( memIsValid(pIn2) );
assert( (pIn2->flags & MEM_Int)!=0 );
sqlite3VdbeMemIntegerify(pIn2);
p2 = (int)pIn2->u.i;
/* The p2 value always comes from a prior OP_CreateTable opcode and
** that opcode will always set the p2 value to 2 or more or else fail.
** If there were a failure,the prepared statement would have halted
** before reaching this instruction. */
assert( p2>=2 );
}
if( pOp->p4type==P4_KEYINFO ){
pKeyInfo = pOp->p4.pKeyInfo;
assert( pKeyInfo->enc==ENC(db) );
assert( pKeyInfo->db==db );
nField = pKeyInfo->nField+pKeyInfo->nXField;
}else if( pOp->p4type==P4_INT32 ){
nField = pOp->p4.i;
}
assert( pOp->p1>=0 );
assert( nField>=0 );
testcase( nField==0 ); /* Tablewith INTEGER PRIMARY KEY and nothing else */
pCur = allocateCursor(p,pOp->p1,nField,CURTYPE_BTREE);
if( pCur==0 ) goto no_mem;
pCur->nullRow = 1;
pCur->isOrdered = 1;
pCur->pgnoRoot = p2;
#ifdef sqlITE_DEBUG
pCur->wrFlag = wrFlag;
#endif
rc = sqlite3BtreeCursor(pX,p2,wrFlag,pKeyInfo,pCur->uc.pCursor);
pCur->pKeyInfo = pKeyInfo;
/* Set the VdbeCursor.isTable variable. PrevIoUs versions of
** sqlite used to check if the root-page flags were sane at this point
** and report database corruption if they were not,but this check has
** since moved into the btree layer.*/
pCur->isTable = pOp->p4type!=P4_KEYINFO;
open_cursor_set_hints:
assert( OPFLAG_BULKCSR==BTREE_BULKLOAD );
assert( OPFLAG_SEEKEQ==BTREE_SEEK_EQ );
testcase( pOp->p5 & OPFLAG_BULKCSR );
#ifdef sqlITE_ENABLE_CURSOR_HINTS
testcase( pOp->p2 & OPFLAG_SEEKEQ );
#endif
sqlite3BtreeCursorHintFlags(pCur->uc.pCursor,
(pOp->p5& (OPFLAG_BULKCSR|OPFLAG_SEEKEQ)));
if( rc ) goto abort_due_to_error;
break;
}
如注释所说,这个函数就是分配一个内存空间做游标,游标的number在P1中传递过来,从memorycell里分配一段空间,mem数组的第0个在数组0位置,第一个在数组len-1位置,再反向生长。分配空间后设置一些基本的信息,然后返回游标指针。
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL
** if we run out of memory.
*/
static VdbeCursor *allocateCursor(
Vdbe *p,/* Thevirtual machine */
int iCur,/* Index ofthe new VdbeCursor */
int nField,/* Number offields in the table or index */
int iDb,/* Databasethe cursor belongs to,or -1 */
u8 eCurType /* Type ofthe new cursor */
){
/* Find the memory cell that will be used to store the blob of memory
** required for this VdbeCursor structure. It is convenient to use a
** vdbe memory cell to manage the memory allocation required for a
** VdbeCursor structure for the following reasons:
**
** * Sometimes cursor numbersare used for a couple of different
** purposes in a vdbe program.The different uses might require
** different sized allocations. Memory cellsprovide growable
** allocations.
**
** * When usingENABLE_MEMORY_MANAGEMENT,memory cell buffers can
** be freed lazily via thesqlite3_release_memory() API. This
** minimizes the number ofmalloc calls made by the system.
**
** The memory cell for cursor 0 is aMem[0]. The rest are allocated from
** the top of the register space.Cursor 1 is at Mem[p->nMem-1].
** Cursor 2 is at Mem[p->nMem-2]. And so forth.
*/
Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem;
int nByte;
VdbeCursor *pCx = 0;
nByte =
ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
(eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);
assert( iCur>=0 && iCur<p->nCursor );
if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
sqlite3VdbeFreeCursor(p,p->apCsr[iCur]);
p->apCsr[iCur] = 0;
}
if( sqlITE_OK==sqlite3VdbeMemClearAndResize(pMem,nByte) ){
p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z;
memset(pCx,offsetof(VdbeCursor,pAltCursor));
pCx->eCurType = eCurType;
pCx->iDb = iDb;
pCx->nField = nField;
pCx->aOffset = &pCx->aType[nField];
if( eCurType==CURTYPE_BTREE ){
pCx->uc.pCursor= (BtCursor*) //注意这句,给btree分配一个游标
&pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
sqlite3BtreeCursorZero(pCx->uc.pCursor);
}
}
return pCx;
}
1.1.3Rewind
Rewind的主要调用流程图:
Rewind的解释和代码实现如下,其主要功能也就是调用sqlite3BtreeFirst将游标移动到数据库表的第一个位置,其他就是状态处理、参数设置和异常处理。后续操作一般为Rowid、Column、Next。
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Opcode: Rewind P1 P2 * * *
**
** The next use of the Rowid or Columnor Next instruction for P1
** will refer to the first entry in thedatabase table or index.
** If the table or index is empty,jumpimmediately to P2.
** If the table or index is not empty,fall through to the following
** instruction.
**
** This opcode leaves the cursorconfigured to move in forward order,
** from the beginning toward theend. In other words,the cursor is
** configured to use Next,not Prev.
*/
case OP_Rewind: { /* jump */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) );
res = 1;
#ifdef sqlITE_DEBUG
pC->seekOp = OP_Rewind;
#endif
if( isSorter(pC) ){
rc = sqlite3VdbeSorterRewind(pC,&res);
}else{
assert( pC->eCurType==CURTYPE_BTREE );
pCrsr = pC->uc.pCursor;
assert( pCrsr );
rc = sqlite3BtreeFirst(pCrsr,&res);
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
}
if( rc ) goto abort_due_to_error;
pC->nullRow = (u8)res;
assert( pOp->p2>0 && pOp->p2<p->nOp );
VdbeBranchTaken(res!=0,2);
if( res ) goto jump_to_p2;
break;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Move the cursor to the first entry inthe table. Return sqlITE_OK
** on success. Set *pRes to 0 if the cursor actually pointsto something
** or set *pRes to 1 if the table isempty.
*/
int sqlite3BtreeFirst(BtCursor *pCur,int *pRes){
int rc;
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
rc = moveToRoot(pCur);
if( rc==sqlITE_OK ){
if( pCur->eState==CURSOR_INVALID ){
assert( pCur->pgnoRoot==0 ||pCur->apPage[pCur->iPage]->nCell==0 );
*pRes = 1;
}else{
assert( pCur->apPage[pCur->iPage]->nCell>0 );
*pRes = 0;
rc = moveToLeftmost(pCur);
}
}
return rc;
}
获取一个页面,并初始它,各参数的含义见注释,主要部分是通过sqlite3PagerGet获取page,一个pager实例是它的参数,后面可以看到会使用其封装的指针函数xGet。
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
** Get a page from the pager and initializeit.
**
** If pCur!=0 then the page is beingfetched as part of a movetochild()
** call.Do additional sanity checking on the page in this case.
** And if the fetch fails,this routinemust decrement pCur->iPage.
**
** The page is fetched as read-writeunless pCur is not NULL and is
** a read-only cursor.
**
** If an error occurs,then *ppPage isundefined. It
** may remain unchanged,or it may beset to an invalid value.
*/
static int getAndInitPage(
BtShared *pBt,/*The database file */
Pgno pgno,/*Number of the page to get */
MemPage **ppPage,/*Write the page pointer here */
BtCursor *pCur,/*Cursor to receive the page,or NULL */
int bReadOnly /*True for a read-only page */
){
int rc;
DbPage *pDbPage;
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pCur==0 || ppPage==&pCur->apPage[pCur->iPage] );
assert( pCur==0 || bReadOnly==pCur->curPagerFlags );
assert( pCur==0 || pCur->iPage>0 );
if( pgno>btreePagecount(pBt) ){
rc = sqlITE_CORRUPT_BKPT;
goto getAndInitPage_error;
}
rc = sqlite3PagerGet(pBt->pPager,pgno,(DbPage**)&pDbPage,bReadOnly);
if( rc ){
goto getAndInitPage_error;
}
*ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
if( (*ppPage)->isInit==0 ){
btreePageFromDbPage(pDbPage,pBt);
rc = btreeInitPage(*ppPage);
if( rc!=sqlITE_OK ){
releasePage(*ppPage);
goto getAndInitPage_error;
}
}
assert( (*ppPage)->pgno==pgno );
assert( (*ppPage)->aData==sqlite3PagerGetData(pDbPage) );
/* If obtaining a child page for a cursor,we must verify that the pageis
** compatible with the root page. */
if( pCur && ((*ppPage)->nCell<1 ||(*ppPage)->intKey!=pCur->curIntKey) ){
rc = sqlITE_CORRUPT_BKPT;
releasePage(*ppPage);
goto getAndInitPage_error;
}
return sqlITE_OK;
getAndInitPage_error:
if( pCur ) pCur->iPage--;
testcase( pgno==0 );
assert( pgno!=0 || rc==sqlITE_CORRUPT );
return rc;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
int sqlite3PagerGet(
Pager *pPager,/* The pageropen on the database file */
Pgno pgno,/* Page numberto fetch */
DbPage **ppPage,/* Write apointer to the page here */
int flags /*PAGER_GET_XXX flags */
){
return pPager->xGet(pPager,ppPage,flags);
}
xGet是函数指针,有不同的实现方式,我们分析其最常用的getPageNormal,它会调用readDbPage,而后者则会调用sqlite3OsRead,通过文件句柄打开文件,读取page,
///////////////////////////////////////////////////////////////////////////////////////////////////
int sqlite3OsRead(sqlite3_file *id,void*pBuf,int amt,i64 offset){
DO_OS_MALLOC_TEST(id);
return id->pMethods->xRead(id,pBuf,amt,offset);
}
OS是一个系统调用的封装文件,对于不同的操作系统提供统一接口(这里主要是os_win,os_unix),例如os_unix,xRead对应的是unixRead,
///////////////////////////////////////////////////////////////////////////////////////////////////
#defineIOMETHODS(FINDER,METHOD,VERSION,CLOSE,LOCK,UNLOCK,CKLOCK,SHMMAP) \
static const sqlite3_io_methods METHOD ={ \
VERSION,/*iVersion */\
CLOSE,/*xClose */\
unixRead,/*xRead */\
unixWrite,/*xWrite */\
unixTruncate,/* xTruncate */ \
unixSync,/*xSync */\
unixFileSize,/*xFileSize */\
LOCK,/*xLock */\
UNLOCK,/*xUnlock */\
CKLOCK,/*xCheckReservedLock */\
unixFileControl,/*xFileControl */ \
unixSectorSize,/*xSectorSize */\
unixDeviceCharacteristics,/*xDeviceCapabilities */\
SHMMAP,/*xShmMap */\
unixShmLock,/*xShmLock */\
unixShmBarrier,/*xShmBarrier */\
unixShmUnmap,/*xShmUnmap */\
unixFetch,/* xFetch */ \
unixUnfetch,/*xUnfetch */\
};\
os_unix.c里对应的接口函数是unixRead,通过seekAndRead调用系统调用读取文件,后面属于操作系统的知识范围,就不再分析了,我们只要知道vdbe是通过怎样的方式访问磁盘文件即可。
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
** Read data from a file into abuffer. Return sqlITE_OK if all
** bytes were read successfully andsqlITE_IOERR if anything goes
** wrong.
*/
static int unixRead(
sqlite3_file *id,
void *pBuf,
int amt,
sqlite3_int64 offset
){
unixFile *pFile = (unixFile *)id;
int got;
/* If this is a database file (not a journal,master-journal or temp
** file),the bytes in the locking range should never be read orwritten. */
#if sqlITE_MAX_MMAP_SIZE>0
/* Deal with as much of this read request as possible by transfering
** data from the memory mapping using memcpy(). */
if( offset<pFile->mmapSize ){
if( offset+amt <= pFile->mmapSize ){
memcpy(pBuf,&((u8 *)(pFile->pMapRegion))[offset],amt);
return sqlITE_OK;
}else{
int nCopy = pFile->mmapSize - offset;
memcpy(pBuf,nCopy);
pBuf = &((u8 *)pBuf)[nCopy];
amt -= nCopy;
offset += nCopy;
}
}
#endif
got = seekAndRead(pFile,offset,amt);
if( got==amt ){
return sqlITE_OK;
}else if( got<0 ){
/* lastErrno set by seekAndRead */
return sqlITE_IOERR_READ;
}else{
storeLastErrno(pFile,0); /* nota system error */
/* Unread parts of the buffer must be zero-filled */
memset(&((char*)pBuf)[got],amt-got);
return sqlITE_IOERR_SHORT_READ;
}
}
1.1.1Column
Column进行列操作,P1是游标ID,P2是column id,p3存储P2的数据,P4做数据存储的备用,用来处理P4_MEM类型数据,P5是flag。
主要调用流程如下,
OP_Column代码比较多,这里只贴一些注释部分的,主要调用流程参考流程图,关键函数后面单独分析,
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Opcode: Column P1 P2 P3 P4 P5
** Synopsis: r[P3]=PX
**
** Interpret the data that cursor P1points to as a structure built using
** the MakeRecord instruction. (See the MakeRecord opcode for additional
** information about the format of thedata.) Extract the P2-th column
** from this record. If there are less that (P2+1)
** values in the record,extract a NULL.
**
** The value extracted is stored inregister P3.
**
** If the column contains fewer than P2fields,then extract a NULL. Or,
** if the P4 argument is a P4_MEM usethe value of the P4 argument as
** the result.
**
** If the OPFLAG_CLEARCACHE bit is seton P5 and P1 is a pseudo-table cursor,
** then the cache of the cursor is resetprior to extracting the column.
** The first OP_Column against apseudo-table after the value of the content
** register has changed should have thisbit set.
**
** If the OPFLAG_LENGTHARG andOPFLAG_TYPEOFARG bits are set on P5 when
** the result is guaranteed to only beused as the argument of a length()
** or typeof() function,respectively. The loading of large blobscan be
** skipped for length() and all contentloading can be skipped for typeof().
*/
case OP_Column: {
int p2; /* columnnumber to retrieve */
VdbeCursor *pC; /* The VDBEcursor */
BtCursor *pCrsr; /* The BTreecursor */
u32 *aOffset; /* aOffset[i]is offset to start of data for i-th column */
int len; /* The lengthof the serialized data for the column */
int i; /* Loop counter*/
Mem *pDest; /* Where towrite the extracted value */
Mem sMem; /* For storingthe record being decoded */
const u8 *zData; /* Part of therecord being decoded */
const u8 *zHdr; /* Next unparsedbyte of the header */
const u8 *zEndHdr; /* Pointer to first byte after the header */
u32 offset; /* Offset intothe data */
u64 offset64; /* 64-bitoffset */
u32 avail; /* Number ofbytes of available data */
u32 t; /* A type code from the recordheader */
Mem *pReg; /* PseudoTableinput register */
pC = p->apCsr[pOp->p1];
p2 = pOp->p2;
1)OP_Column首先调用sqlite3BtreePayloadSize,获取记录的payload的size,然后它调用getCellInfo获取cell区的信息,
///////////////////////////////////////////////////////////////////////////////////////////////////
static sqlITE_NOINLINE voidgetCellInfo(BtCursor *pCur){
if( pCur->info.nSize==0 ){
int iPage = pCur->iPage;
pCur->curFlags |= BTCF_ValidNKey;
btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info);
}else{
assertCellInfo(pCur);
}
}
因为root page、leaf page以及index的page格式不相同,所以在解析cell的时候,虽然使用了统一的解析函数xParseCell,但对不同的page,实现并不一样。
///////////////////////////////////////////////////////////////////////////////////////////////////
static void btreeParseCell(
MemPage *pPage,/* Pagecontaining the cell */
int iCell,/* The cell index. First cell is 0 */
CellInfo *pInfo /* Fill inthis structure */
){
pPage->xParseCell(pPage,findCell(pPage,iCell),pInfo);
}
我们分析其中leaf page对应的btreeParseCellPtr,
对于btreeParseCellPtr的代码,如果看了之前关于payload的存储结构分析,就很容易理解,否则就不知所云。
首先,这个函数功能就是将物理磁盘的叶子页数据填充到内存CellInfo的数据结构里面,物理磁盘的数据其实已经被读出来存放到内存里,被pcell指针指向,但数据还是raw的,raw data的格式如下。
///////////////////////////////////////////////////////////////////////////////////////////////////
static void btreeParseCellPtr(
MemPage *pPage,/* Pagecontaining the cell */
u8 *pCell,/* Pointerto the cell text. */
CellInfo *pInfo /* Fill inthis structure */
){
u8 *pIter; /* Forscanning through pCell */
u32 nPayload; /* Numberof bytes of cell payload */
u64 iKey; /*Extracted Key value */
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( pPage->leaf==0 || pPage->leaf==1 );
assert( pPage->intKeyLeaf );
assert( pPage->childPtrSize==0 );
pIter = pCell;
/* The next block of code is equivalent to:
**
** pIter += getVarint32(pIter,nPayload);
**
** The code is inlined to avoid a function call.
*/
nPayload = *pIter; //获取地址指针
if( nPayload>=0x80 ){ //获取第一个可变长整数
u8 *pEnd = &pIter[8];
nPayload &= 0x7f;
do{
nPayload = (nPayload<<7) | (*++pIter & 0x7f);
}while( (*pIter)>=0x80 && pIter<pEnd );
}
pIter++;
/* The next block of code is equivalent to:
**
** pIter += getVarint(pIter,(u64*)&pInfo->nKey);
**
** The code is inlined to avoid a function call.
*/
iKey = *pIter;
if( iKey>=0x80 ){ //获取第二个可变长整数
u8 *pEnd = &pIter[7];
iKey &= 0x7f;
while(1){
iKey = (iKey<<7) | (*++pIter & 0x7f);
if( (*pIter)<0x80 ) break;
if( pIter>=pEnd ){
iKey = (iKey<<8) | *++pIter;
break;
}
}
}
pIter++; //指向内容
pInfo->nKey = *(i64*)&iKey;
pInfo->nPayload = nPayload;
pInfo->pPayload = pIter;
testcase( nPayload==pPage->maxLocal );
testcase( nPayload==pPage->maxLocal+1 );
if( nPayload<=pPage->maxLocal ){
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflowis required.
*/
pInfo->nSize = nPayload + (u16)(pIter - pCell);
if( pInfo->nSize<4 ) pInfo->nSize = 4;
pInfo->nLocal = (u16)nPayload;
}else{ //处理over flow
btreeParseCellAdjustSizeForOverflow(pPage,pCell,pInfo);
}
}
fetchPayload返回payload的内容和可用字节数,从代码可以看出,仅仅是返回了btree游标的数据指针,没有其他的数据处理过程。
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
** Return a pointer to payloadinformation from the entry that the
** pCur cursor is pointing to. The pointer is to the beginning of
** the key if index btrees(pPage->intKey==0) and is the data for
** table btrees (pPage->intKey==1).The number of bytes of available
** key/data is written into *pAmt. If *pAmt==0,then the value
** returned will not be a valid pointer.
**
** This routine is an optimization. It is common for the entire key
** and data to fit on the local page andfor there to be no overflow
** pages. When that is so,this routine can be used toaccess the
** key and data without making acopy. If the key and/or data spills
** onto overflow pages,thenaccessPayload() must be used to reassemble
** the key/data and copy it into apreallocated buffer.
**
** The pointer returned by this routinelooks directly into the cached
** page of the database. The data might change or move the next time
** any btree routine is called.
*/
static const void *fetchPayload(
BtCursor *pCur,/* Cursorpointing to entry to read from */
u32 *pAmt /* Write thenumber of available bytes here */
){
u32 amt;
assert( pCur!=0 && pCur->iPage>=0 &&pCur->apPage[pCur->iPage]);
assert( pCur->eState==CURSOR_VALID );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( cursorOwnsBtShared(pCur) );
assert(pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
assert( pCur->info.nSize>0 );
assert(pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData ||CORRUPT_DB );
assert( pCur->info.pPayload<pCur->apPage[pCur->iPage]->aDataEnd||CORRUPT_DB);
amt = (int)(pCur->apPage[pCur->iPage]->aDataEnd -pCur->info.pPayload);
if( pCur->info.nLocal<amt ) amt = pCur->info.nLocal;
*pAmt = amt;
return (void*)pCur->info.pPayload;
}
2)再通过sqlite3BtreePayloadFetch获取payload数据指针和也能可用空间大小,并在后续判断size是否超出限制,
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
** For the entry that cursor pCur ispoint to,return as
** many bytes of the key or data as areavailable on the local
** b-tree page. Write the number of available bytes into*pAmt.
**
** The pointer returned isephemeral. The key/data may move
** or be destroyed on the next call toany Btree routine,
** including calls from other threadsagainst the same cache.
** Hence,a mutex on the BtShared shouldbe held prior to calling
** this routine.
**
** These routines is used to get quickaccess to key and data
** in the common case where no overflowpages are used.
*/
const void*sqlite3BtreePayloadFetch(BtCursor *pCur,u32 *pAmt){
return fetchPayload(pCur,pAmt);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
** Return a pointer to payloadinformation from the entry that the
** pCur cursor is pointing to. The pointer is to the beginning of
** the key if index btrees(pPage->intKey==0) and is the data for
** table btrees (pPage->intKey==1).The number of bytes of available
** key/data is written into *pAmt. If *pAmt==0,/* Cursorpointing to entry to read from */
u32 *pAmt /* Write thenumber of available bytes here */
){
u32 amt;
assert( pCur!=0 && pCur->iPage>=0 &&pCur->apPage[pCur->iPage]);
assert( pCur->eState==CURSOR_VALID );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( cursorOwnsBtShared(pCur) );
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell);
assert( pCur->info.nSize>0 );
assert(pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData ||CORRUPT_DB );
assert(pCur->info.pPayload<pCur->apPage[pCur->iPage]->aDataEnd||CORRUPT_DB);
amt = (int)(pCur->apPage[pCur->iPage]->aDataEnd -pCur->info.pPayload);
if( pCur->info.nLocal<amt ) amt = pCur->info.nLocal;
*pAmt = amt;
return (void*)pCur->info.pPayload;
}
3)列记录处理sqlite3GetVarint32,sqlite3VdbeSerialTypeLen
4)记录内容解析,暂分析单页case,对overflow的情况暂不分析,sqlite3VdbeMemGrow申请内存,并在后面来存放数据。
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
** Make sure pMem->z points to awritable allocation of at least
** min(n,32) bytes.
**
** If the bPreserve argument is true,then copy of the content of
** pMem->z into the newallocation. pMem must be either a stringor
** blob if bPreserve is true. If bPreserve is false,any prior content
** in pMem->z is discarded.
*/
sqlITE_NOINLINE intsqlite3VdbeMemGrow(Mem *pMem,int n,int bPreserve){
assert( sqlite3VdbeCheckMemInvariants(pMem) );
assert( (pMem->flags&MEM_RowSet)==0 );
testcase( pMem->db==0 );
/* If the bPreserve flag is set to true,then the memory cell mustalready
** contain a valid string or blob value.*/
assert( bPreserve==0 || pMem->flags&(MEM_Blob|MEM_Str) );
testcase( bPreserve && pMem->z==0 );
assert( pMem->szMalloc==0
|| pMem->szMalloc==sqlite3DbMallocSize(pMem->db,pMem->zMalloc));
if( pMem->szMalloc<n ){
if( n<32 ) n = 32;
if( bPreserve && pMem->szMalloc>0 &&pMem->z==pMem->zMalloc ){
pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db,pMem->z,n);
bPreserve = 0;
}else{
if( pMem->szMalloc>0 ) sqlite3DbFree(pMem->db,pMem->zMalloc);
pMem->zMalloc = sqlite3DbMallocRaw(pMem->db,n);
}
if( pMem->zMalloc==0 ){
sqlite3VdbeMemSetNull(pMem);
pMem->z = 0;
pMem->szMalloc = 0;
return sqlITE_NOMEM_BKPT;
}else{
pMem->szMalloc = sqlite3DbMallocSize(pMem->db,pMem->zMalloc);
}
}
if( bPreserve && pMem->z &&pMem->z!=pMem->zMalloc ){
memcpy(pMem->zMalloc,pMem->n);
}
if( (pMem->flags&MEM_Dyn)!=0 ){
assert( pMem->xDel!=0 && pMem->xDel!=sqlITE_DYNAMIC );
pMem->xDel((void *)(pMem->z));
}
pMem->z = pMem->zMalloc;
pMem->flags &= ~(MEM_Dyn|MEM_Ephem|MEM_Static);
return sqlITE_OK;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
1.1.1ResultRow
ResultRow提供返回结果,r(p1)到r(p1+p2-1),
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Opcode: ResultRow P1 P2 * * *
** Synopsis: output=r[P1@P2]
**
** The registers P1 through P1+P2-1contain a single row of
** results. This opcode causes thesqlite3_step() call to terminate
** with an sqlITE_ROW return code and itsets up the sqlite3_stmt
** structure to provide access to ther(P1)..r(P1+P2-1) values as
** the result row.
*/
case OP_ResultRow: {
Mem *pMem;
int i;
assert( p->nResColumn==pOp->p2 );
assert( pOp->p1>0 );
assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
#ifndef sqlITE_OMIT_PROGRESS_CALLBACK
/* Run the progress counter just before returning.
*/
if( db->xProgress!=0
&& nVmStep>=nProgressLimit
&& db->xProgress(db->pProgressArg)!=0
){
rc = sqlITE_INTERRUPT;
goto abort_due_to_error;
}
#endif
/* If this statement has violated immediate foreign key constraints,do
** not return the number of rows modified. And do not RELEASE thestatement
** transaction. It needs to be rolled back. */
if( sqlITE_OK!=(rc = sqlite3VdbeCheckFk(p,0)) ){
assert( db->flags&sqlITE_CountRows );
assert( p->usesStmtJournal );
goto abort_due_to_error;
}
/* If the sqlITE_CountRows flag is set in sqlite3.flags mask,then
** DML statements invoke this opcode to return the number of rows
** modified to the user. This is the only way that a VM that
** opens a statement transaction may invoke this opcode.
**
** In case this is such a statement,close any statement transaction
** opened by this VM before returning control to the user. This is to
** ensure that statement-transactions are always nested,notoverlapping.
** If the open statement-transaction is not closed here,then the user
** may step another VM that opens its own statement transaction. This
** may lead to overlapping statement transactions.
**
** The statement transaction is never a top-level transaction. Hence
** the RELEASE call below can never fail.
*/
assert( p->iStatement==0 || db->flags&sqlITE_CountRows );
rc = sqlite3VdbeCloseStatement(p,SAVEPOINT_RELEASE);
assert( rc==sqlITE_OK );
/* Invalidate all ephemeral cursor row caches */
p->cacheCtr = (p->cacheCtr + 2)|1;
/* Make sure the results of the current row are \000 terminated
** and have an assigned type. Theresults are de-ephemeralized as
** a side effect.
*/
pMem = p->pResultSet = &aMem[pOp->p1];
for(i=0; i<pOp->p2; i++){
assert( memIsValid(&pMem[i]) );
Deephemeralize(&pMem[i]);
assert( (pMem[i].flags & MEM_Ephem)==0
|| (pMem[i].flags &(MEM_Str|MEM_Blob))==0 );
sqlite3VdbeMemNulTerminate(&pMem[i]);
REGISTER_TRACE(pOp->p1+i,&pMem[i]);
}
if( db->mallocFailed ) goto no_mem;
if( db->mTrace & sqlITE_TRACE_ROW ){
db->xTrace(sqlITE_TRACE_ROW,0);
}
/* Return sqlITE_ROW
*/
p->pc = (int)(pOp - aOp) + 1;
rc = sqlITE_ROW;
goto vdbe_return;
}
1.1.2Next
Next来移动游标,P1是游标ID,p2是在当前游标所在row后面还有数据的时候要跳转的地址,p3p4p5暂略。
Next指令跟在SeekGT,SeekGE,或 OP_Rewind后面,但不能在SeekLT,SeekLE,or OP_Last后面。
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Opcode: Next P1 P2 P3 P4 P5
**
** Advance cursor P1 so that it pointsto the next key/data pair in its
** table or index. If there are no more key/value pairs thenfall through
** to the following instruction. But if the cursor advance was successful,
** jump immediately to P2.
**
** The Next opcode is only validfollowing an SeekGT,or
** OP_Rewind opcode used to position thecursor. Next is not allowed
** to follow SeekLT,or OP_Last.
**
** The P1 cursor must be for a realtable,not a pseudo-table. P1 must have
** been opened prior to this opcode orthe program will segfault.
**
** The P3 value is a hint to the btreeimplementation. If P3==1,that
** means P1 is an sql index and thatthis instruction could have been
** omitted if that index had beenunique. P3 is usually 0. P3 is
** always either 0 or 1.
**
** P4 is always of type P4_ADVANCE. Thefunction pointer points to
** sqlite3BtreeNext().
**
** If P5 is positive and the jump istaken,then event counter
** number P5-1 in the prepared statementis incremented.
case OP_Prev: /* jump */
case OP_Next: /* jump */
assert( pOp->p1>=0 && pOp->p1<p->nCursor ); //参数检查
assert( pOp->p5<ArraySize(p->aCounter) );
pC = p->apCsr[pOp->p1];
res = pOp->p3;
assert( pC!=0 );
assert( pC->deferredMoveto==0 );
assert( pC->eCurType==CURTYPE_BTREE );
assert( res==0 || (res==1 && pC->isTable==0) );
testcase( res==1 );
assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );// 对应的执行函数
assert( pOp->opcode!=OP_Prev ||pOp->p4.xAdvance==sqlite3BtreePrevIoUs );
assert( pOp->opcode!=OP_NextIfOpen ||pOp->p4.xAdvance==sqlite3BtreeNext );
assert( pOp->opcode!=OP_PrevIfOpen ||pOp->p4.xAdvance==sqlite3BtreePrevIoUs);
/* The Next opcode is only used after SeekGT,and Rewind.
** The Prev opcode is only used after SeekLT,and Last. */
assert( pOp->opcode!=OP_Next || pOp->opcode!=OP_NextIfOpen
|| pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE
|| pC->seekOp==OP_Rewind || pC->seekOp==OP_Found);
assert( pOp->opcode!=OP_Prev || pOp->opcode!=OP_PrevIfOpen
|| pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE
|| pC->seekOp==OP_Last );
rc = pOp->p4.xAdvance(pC->uc.pCursor,&res);
next_tail:
pC->cacheStatus = CACHE_STALE;
VdbeBranchTaken(res==0,2);
if( rc ) goto abort_due_to_error;
if( res==0 ){
pC->nullRow = 0;
p->aCounter[pOp->p5]++;
#ifdef sqlITE_TEST
sqlite3_search_count++;
#endif
goto jump_to_p2_and_check_for_interrupt; //跳转
}else{
pC->nullRow = 1;
}
goto check_for_interrupt;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
1.1.3Halt
Halt马上退出,释放游标,P1是返回码,P2和rollback相关,P4P5和错误消息相关。
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Opcode: Halt P1 P2 * P4 P5
**
** Exit immediately. All open cursors,etc are closed
** automatically.
**
** P1 is the result code returned bysqlite3_exec(),sqlite3_reset(),
** or sqlite3_finalize(). For a normal halt,this should be sqlITE_OK(0).
** For errors,it can be some othervalue. If P1!=0 then P2 will determine
** whether or not to rollback thecurrent transaction. Do not rollback
** if P2==OE_Fail. Do the rollback ifP2==OE_Rollback. If P2==OE_Abort,
** then back out all changes that haveoccurred during this execution of the
** VDBE,but do not rollback thetransaction.
**
** If P4 is not null then it is an errormessage string.
**
** P5 is a value between 0 and 4,inclusive,that modifies the P4 string.
**
**0: (no change)
**1: NOT NULL contraint Failed: P4
**2: UNIQUE constraint Failed: P4
**3: CHECK constraint Failed: P4
**4: FOREIGN KEY constraint Failed:P4
**
** If P5 is not zero and P4 is NULL,then everything after the ":" is
** omitted.
**
** There is an implied "Halt 0 00" instruction inserted at the very end of
** every program. So a jump past the last instruction of theprogram
** is the same as executing Halt.
*/
case OP_Halt: {
VdbeFrame *pFrame;
int pcx;
pcx = (int)(pOp - aOp);
if( pOp->p1==sqlITE_OK && p->pFrame ){
/* Halt the sub-program. Return control to the parent frame. */
pFrame = p->pFrame;
p->pFrame = pFrame->pParent;
p->nFrame--;
sqlite3VdbeSetChanges(db,p->nChange);
pcx = sqlite3VdbeFrameRestore(pFrame);
if( pOp->p2==OE_Ignore ){
/* Instruction pcx is the OP_Program that invoked the sub-program
** currently being halted. If the p2 instruction of this OP_Halt
** instruction is set to OE_Ignore,then the sub-program is throwing
** an IGNORE exception. In this case jump to the address specified
** as the p2 of the calling OP_Program.*/
pcx = p->aOp[pcx].p2-1;
}
aOp = p->aOp;
aMem = p->aMem;
pOp = &aOp[pcx];
break;
}
p->rc = pOp->p1;
p->errorAction = (u8)pOp->p2;
p->pc = pcx;
assert( pOp->p5<=4 );
if( p->rc ){
if( pOp->p5 ){
static const char * const azType[] = { "NOT NULL","UNIQUE","CHECK",
"FOREIGN KEY" };
testcase( pOp->p5==1 );
testcase( pOp->p5==2 );
testcase( pOp->p5==3 );
testcase( pOp->p5==4 );
sqlite3VdbeError(p,"%s constraint Failed",azType[pOp->p5-1]);
if( pOp->p4.z ){
p->zErrMsg = sqlite3MPrintf(db,"%z: %s",p->zErrMsg,pOp->p4.z);
}
}else{
sqlite3VdbeError(p,"%s",pOp->p4.z);
}
sqlite3_log(pOp->p1,"abort at %d in [%s]: %s",pcx,p->zsql,p->zErrMsg);
}
rc = sqlite3VdbeHalt(p);
assert( rc==sqlITE_BUSY || rc==sqlITE_OK || rc==sqlITE_ERROR );
if( rc==sqlITE_BUSY ){
p->rc = sqlITE_BUSY;
}else{
assert( rc==sqlITE_OK || (p->rc&0xff)==sqlITE_CONSTRAINT );
assert( rc==sqlITE_OK || db->nDeferredCons>0 ||db->nDeferredImmCons>0 );
rc = p->rc ? sqlITE_ERROR : sqlITE_DONE;
}
goto vdbe_return;
}