sqlite浅析3--sqlite源码分析--SQLITE指令代码源码分析-SQLITE虚
1. VDBE1.1Opcode实例分析1.1.1OpcodeOpcode的指令说明: http://sqlite.org/opcode.html 这里通过一个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.1InitInit代码实现和注释如下,其最常用的功能是跳转到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: %sn",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.2TransactionTransaction调用流程如下,
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.1GotoGoto语句比较简单,就是跳转到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.2openReadOpenRead打开数据库的只读游标,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.3RewindRewind的主要调用流程图:
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。 获取到页面后使用btreeInitPage进行初始。 /////////////////////////////////////////////////////////////////////////////////////////////////// /* ** 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.1ColumnColumn进行列操作,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.1ResultRowResultRow提供返回结果,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 00 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.2NextNext来移动游标,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.3HaltHalt马上退出,释放游标,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; }
如果觉得我的文章对您有用,请打赏。您的支持是对我莫大的认可! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |