sqlite浅析3--sqlite源码分析--SQLITE指令代码源码分析-SQLITE虚拟机浅析

前端之家收集整理的这篇文章主要介绍了sqlite浅析3--sqlite源码分析--SQLITE指令代码源码分析-SQLITE虚拟机浅析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1. VDBE

1.1Opcode实例分析

1.1.1Opcode

Opcode的指令说明:

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.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。

获取页面后使用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.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;

}




如果觉得我的文章对您有用,请打赏。您的支持是对我莫大的认可

原文链接:https://www.f2er.com/sqlite/198514.html

猜你在找的Sqlite相关文章