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

 

 

1.       VDBE

 

 

1.1          Opcode实例分析

1.1.1           Opcode

 

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.1           Init

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.2           Transaction

 

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.1           Goto

 

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.2           openRead

 

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, iDb, 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, 0, 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.3           Rewind

 

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, pgno, 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,pgno, 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, &((u8 *)(pFile->pMapRegion))[offset], nCopy);

     pBuf = &((u8 *)pBuf)[nCopy];

     amt -= nCopy;

     offset += nCopy;

   }

 }

#endif

 

 got = seekAndRead(pFile,offset, pBuf, 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], 0, amt-got);

   return SQLITE_IOERR_SHORT_READ;

 }

}

 

 

 

1.1.1           Column

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, 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;

}

 

 

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->z, 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.1           ResultRow

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, db->pTraceArg, p, 0);

 }

 

 /* Return SQLITE_ROW

 */

 p->pc = (int)(pOp - aOp) + 1;

 rc = SQLITE_ROW;

 goto vdbe_return;

}

 

 

1.1.2           Next

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, SeekGE, or

** OP_Rewind opcode used to position thecursor.  Next is not allowed

** to follow SeekLT, SeekLE, 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, SeekGE, and Rewind.

 ** The Prev opcode is only used after SeekLT, SeekLE, 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.3           Halt

 

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;

}

 




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






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值