SQLite入门与分析(七)—-浅谈SQLite的虚拟机

写在前面:虚拟机技术在现在是一个非常热的技术,它的历史也很悠久。最早的虚拟机可追溯到IBM的VM/370,到上个世纪90年代,在计算机程序设计语言领域又出现一件革命性的事情——Java语言的出现,它与c++最大的不同在于它必须在Java虚拟机上运行。Java虚拟机掀起了虚拟机技术的热潮,随后,Microsoft也不甘落后,雄心勃勃的推出了.Net平台。由于在这里主要讨论SQLite的虚拟机,不打算对这些做过多评论,但是作为对比,我会先对Java虚拟机作一个概述。好了,下面进入正题。

1、概述

所谓虚拟机是指对真实计算机资源环境的一个抽象,它为解释性语言程序提供了一套完整的计算机接口。虚拟机的思想对现在的编译有很大影响,其思路是先编译成虚拟机指令,然后针对不同计算机实现该虚拟机。 虚拟机定义了一组抽象的逻辑组件,这些组件包括寄存器组、数据栈和指令集等等。虚拟机指令的解释执行包括3步: 1.获取指令参数;

  1. 执行该指令对应的功能;
  2. 分派下一条指令。 其中第一步和第三步构成了虚拟机的执行开销。 很多语言都采用了虚拟机作为运行环境。作为下一代计算平台的竞争者,Sun的Java和微软的.NET平台都采用了虚拟机技术。Java的支撑环境是Java虚拟机(Java Virtual Machine,JVM),.NET的支撑环境是通用语言运行库(Common Language Runtime,CLR)。JVM是典型的虚拟机架构。 Java平台结构如图所示。从图中可以看出,JVM处于核心位置,它的下方是移植接口。移植接口由依赖平台的和不依赖平台的两部分组成,其中依赖于平台的部分称为适配器。JVM通过移植接口在具体的操作系统上实现。如果在Java操作系统(Java Operation System, JOS)上实现,则不需要依赖于平台的适配器,因为这部分工作已由JOS完成。因此对于JVM来说,操作系统和更低的硬件层是透明的。在JVM的上方,是Java类和Java应用程序接口(Java API)。在Java API上可以编写Java应用程序和Java小程序(applet)。所以对于Java应用程序和applet这一层次来说,操作系统和硬件就更是透明的了。我们编写的Java程序,可以在任何Java平台上运行而无需修改。

document/2015-09-15/55f7c9d26a949 JVM定义了独立于平台的类文件格式和字节码形式的指令集。在任何Java程序的字节码表示形式中,变量和方法的引用都是使用符号,而不是使用具体的数字。由于内存的布局要在运行时才确定,所以类的变量和方法的改变不会影响现存的字节码。例如,一个Java程序引用了其他系统中的某个类,该系统中那个类的更新不会使这个Java程序崩溃。这也提高了Java的平台独立性。 虚拟机一般都采用了基于栈的架构,这种架构易于实现。虚拟机方法显著提高了程序语言的可移植性和安全性,但同时也导致了执行效率的下降。

2、Java虚拟机

2.1、概述

Java虚拟机的主要任务是装载Class文件并执行其中的字节码。Java虚拟机包含一个类装载器(class loader),它从程序和API中装载class文件,Java API中只有程序执行时需要的那些类才会被装载,字节码由执行引擎来执行。 不同的Java虚拟机,执行引擎的实现可能不同。在软件实现的虚拟机中,一般有几下几中实现方式: (1) 解释执行:实现简单,但速度较慢,这是Java最初阶段的实现方式。 (2) 即时编译(just-in-time):执行较快,但消耗内存。在这种情况下,第一次执行的字节码会编译成本地机器代码,然后被缓存,以后可以重用。 (3) 自适应优化器:虚拟机开始的时候解释字节码,但是会监视程序的运行,并记录下使用最频繁的代码,然后把这些代码编译成本地代码,而其它的代码仍保持为字节码。该方法既提高的运行速度,又减少了内存开销。 同样,虚拟机也可由硬件来实现,它用本地方法执行Java字节码。

document/2015-09-15/55f7c9e80956e

2.2、Java虚拟机

Java虚拟机的结构分为:类装载子系统,运行时数据区,执行引擎,本地方法接口。其中运行时数据区又分为:方法区,堆,Java栈,PC寄存器,本地方法栈。

document/2015-09-15/55f7c9f8a8989

关于Java虚拟机就介绍到此,由于Java虚拟机内容庞大,在这里不可能一一介绍,如果想更多了解Java虚拟机,参见《深入Java虚拟机》。

3、SQLite虚拟机

在SQLite的后端(backend)的上一层,通常叫做虚拟数据库引擎(virtual database engine),或者叫做虚拟机(virtual machine)。从作用上来说,它是SQLite的核心。用户程序发出的SQL语句请求,由前端(frontend)编译器(以后会继续介绍)处理,生成字节代码程序(bytecode programs),然后由VM解释执行。VM执行时,又会调用B-tree模块的相关的接口,并输出执行的结果(本节将以一个具体的查询过程来描述这一过程)。

3.1、虚拟机的内部结构

先来看一个简单的例子:

  1. int main(int argc, char **argv)
  2. {
  3. int rc, i, id, cid;
  4. char *name;
  5. char *sql;
  6. char *zErr;
  7. sqlite3 *db; sqlite3_stmt *stmt;
  8. sql="select id,name,cid from episodes";
  9. //打开数据库
  10. sqlite3_open("test.db", &db);
  11. //编译sql语句
  12. sqlite3_prepare(db, sql, strlen(sql), &stmt, NULL);
  13. //调用VM,执行VDBE程序
  14. rc = sqlite3_step(stmt);
  15. while(rc == SQLITE_ROW) {
  16. id = sqlite3_column_int(stmt, 0);
  17. name = (char *)sqlite3_column_text(stmt, 1);
  18. cid = sqlite3_column_int(stmt, 2);
  19. if(name != NULL){
  20. fprintf(stderr, "Row: id=%i, cid=%i, name='%s'\n", id,cid,name);
  21. } else {
  22. /* Field is NULL */
  23. fprintf(stderr, "Row: id=%i, cid=%i, name=NULL\n", id,cid);
  24. }
  25. rc = sqlite3_step(stmt);
  26. }
  27. //释放资源
  28. sqlite3_finalize(stmt);
  29. //关闭数据库
  30. sqlite3_close(db);
  31. return 0;
  32. }

这段程序很简单,它的功能就是遍历整个表,并把查询结果输出。 在SQLite 中,用户发出的SQL语句,都会由编译器生成一个虚拟机实例。在上面的例子中,变量sql代表的SQL语句经过sqlite3_prepare()处理后,便生成一个虚拟机实例——stmt。虚拟机实例从外部看到的结构是sqlite3_stmt所代表的数据结构,而在内部,是一个vdbe数据结构代表的实例。 关于这点可以看看它们的定义:

  1. //sqlite3.h
  2. typedef struct sqlite3_stmt sqlite3_stmt;
  3. vdbe的定义:
  4. //虚拟机数据结构 vdbeInt.h
  5. struct Vdbe {
  6. sqlite3 *db; /* The whole database */
  7. Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
  8. FILE *trace; /* Write an execution trace here, if not NULL */
  9. int nOp; /* Number of instructions in the program(指令的条数) */
  10. int nOpAlloc; /* Number of slots allocated for aOp[]*/
  11. Op *aOp; /* Space to hold the virtual machine's program(指令)*/
  12. int nLabel; /* Number of labels used */
  13. int nLabelAlloc; /* Number of slots allocated in aLabel[] */
  14. int *aLabel; /* Space to hold the labels */
  15. Mem *aStack; /* The operand stack, except string values(栈空间) */
  16. Mem *pTos; /* Top entry in the operand stack(栈顶指针) */
  17. Mem **apArg; /* Arguments to currently executing user function */
  18. Mem *aColName; /* Column names to return */
  19. int nCursor; /* Number of slots in apCsr[] */
  20. Cursor **apCsr; /* One element of this array for each open cursor(游标数组) */
  21. int nVar; /* Number of entries in aVar[] */
  22. Mem *aVar; /* Values for the OP_Variable opcode*/
  23. char **azVar; /* Name of variables */
  24. int okVar; /* True if azVar[] has been initialized */
  25. int magic; /* Magic number for sanity checking */
  26. int nMem; /* Number of memory locations currently allocated */
  27. Mem *aMem; /* The memory locations(保存临时变量的Mem)*/
  28. int nCallback; /* Number of callbacks invoked so far(回调的次数) */
  29. int cacheCtr; /* Cursor row cache generation counter */
  30. Fifo sFifo; /* A list of ROWIDs */
  31. int contextStackTop; /* Index of top element in the context stack */
  32. int contextStackDepth; /* The size of the "context" stack */
  33. Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/
  34. int pc; /* The program counter(初始程序计数器) */
  35. int rc; /* Value to return(返回结果) */
  36. unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */
  37. int errorAction; /* Recovery action to do in case of an error */
  38. int inTempTrans; /* True if temp database is transactioned */
  39. int returnStack[100]; /* Return address stack for OP_Gosub & OP_Return */
  40. int returnDepth; /* Next unused element in returnStack[] */
  41. int nResColumn; /* Number of columns in one row of the result set */
  42. char **azResColumn; /* Values for one row of result */
  43. int popStack; /* Pop the stack this much on entry to VdbeExec()(出栈的项数) */
  44. char *zErrMsg; /* Error message written here */
  45. u8 resOnStack; /* True if there are result values on the stack(有结果在栈上则为真)*/
  46. u8 explain; /* True if EXPLAIN present on SQL command */
  47. u8 changeCntOn; /* True to update the change-counter */
  48. u8 aborted; /* True if ROLLBACK in another VM causes an abort */
  49. u8 expired; /* True if the VM needs to be recompiled */
  50. u8 minWriteFileFormat; /* Minimum file format for writable database files */
  51. int nChange; /* Number of db changes made since last reset */
  52. i64 startTime; /* Time when query started - used for profiling */
  53. #ifdef SQLITE_SSE
  54. int fetchId; /* Statement number used by sqlite3_fetch_statement */
  55. int lru; /* Counter used for LRU cache replacement */
  56. #endif
  57. };

由vdbe的定义,可以总结出SQLite虚拟机的内部结构:

document/2015-09-15/55f7ca32a6568

3.2、指令

  1. int nOp; /* Number of instructions in the program(指令的条数) */
  2. Op *aOp; /* Space to hold the virtual machine's program(指令)*/

aOp数组保存有SQL经过编译后生成的所有指令,对于上面的例子为:

  1. 0Goto(0x5b-91) |0|0c
  2. 1Integer(0x2d-45) |0|0
  3. 2OpenRead(0x0c-12)|0|2
  4. 3SetNumColumns(0x64-100)|0|03
  5. 4Rewind(0x77-119) |0|0a
  6. 5Rowid(0x23-35) |0|0
  7. 6Column(0x02-2) |0|1
  8. 7Column(0x02-2) |0|2
  9. 8Callback(0x36-54)|3|0
  10. 9Next(0x68) |0|5
  11. 10Close
  12. 11Halt
  13. 12Transaction(0x66-102)|0|0
  14. 13VerifyCookie(0x61-97)|0|1
  15. 14Goto(0x5b-91) |0|1|

sqlite3_step()引起VDBE解释引擎执行这段代码,下面来分析该段指令的执行过程: Goto:这是一条跳转指令,它的作用仅仅是跳到第12条指令; Transaction:开始一个事务(读事务); Goto:跳到第1条指令; Integer:把操作数P1入栈,这里的0表示OpenRead指令打开的数据库的编号; OpenRead:打开表的游标,数据库的编号从栈顶中取得,P1为游标的编号,P2为root page。 如果P2<=0,则从栈中取得root page no; SetNumColumns:对P1确定的游标的列数设置为P2(在这里为3),在OP_Column指令执行前,该指令应该被调用来

  1. 设置表的列数;

Rewind:移动当前游标(P1)移到表或索引的第一条记录; Rowid:把当前游标(P1)指向的记录的关键字压入栈; Column:解析当前游标指定的记录的数据,p1为当前游标索引号,p2为列号,并将结果压入栈中; Callback:该指令执行后,PC将指向下一条指令。该指令的执行会结束sqlite3_step()的运行,并向其返回

  1. SQLITE_ROW ——如果存在记录的话;并将VDBEPC指针指向下一条指令——即Next指令,所以当
  2. 重新 调用sqlite3_step()执行VDBE程序时,会执行Next指令(具体的分析见后面的指令实例分析);

Next:将游标移到下一条记录,并将PC指向第5条指令; Close:关闭数据库。

3.3、栈

  1. Mem *aStack; /* The operand stack, except string values(栈空间) */
  2. Mem *pTos; /* Top entry in the operand stack(栈顶指针) */

aStack是VDBE执行时使用的栈,它主要用来保指令执行进需要的参数,以及指令执行时产生的中间结果(参见后面的指令实例分析)。 在计算机硬件领域,基于寄存器的架构已经压倒基于栈的架构成为当今的主流,但是在解释性的虚拟机领域,基于栈架构的实现占了上风。

  1. 从编译的角度来看,许多编程语言可以很容易地被编译成栈架构机器语言。如果采用寄存器架构,编译器为了获得好的性能必须进行优化,如全局寄存器分配(这需要对数据流进行分析)。这种复杂的优化工作使虚拟机的便捷性大打折扣。
  2. 如果采用寄存器架构,虚拟机必须经常保存和恢复寄存器中的内容。与硬件计算机相比,这些操作在虚拟机中的开销要大得多。因为每一条虚拟机指令都需要进行很费时的指令分派操作。虽然其它的指令也要分派,但是它们的语义内容更丰富。
  3. 采用寄存器架构时,指令对应的操作数位于不同寄存器中,对操作数的寻址也是一个问题。而在基于栈的虚拟机中,操作数位于栈顶或紧跟在虚拟机指令之后。由于基于栈的架构的简便性,一些查询语言的实现也采用了此种架构。 SQLite的虚拟机就是基于栈架构的实现。每一个vdbe都有一个栈顶指针,它保存着vdbe的初始栈顶值。而在解释引擎中也有一个pTos,它们是有区别的: (1)vdbe的pTos:在一趟vdbe执行的过程中不会变化,直到相应的指令修改它为止,在上面的例子中,Callback指令会修改其值(见指令分析)。 (2)而解释引擎中的pTos是随着指令的执行而动态变化的,在上面的例子中,Integer,Column指令的执行都会引起解释引擎pTos的改变。

3.4、指令计数器(PC)

每一个vdbe都有一个程序计数器,用来保存初始的计数器值。和pTos一样,解释引擎也有一个pc,它用来指向VM下一条要执行的指令。

3.5、解释引擎

经过编译器生成的vdbe最终都是由解释引擎解释执行的,SQLite的解释引擎实现的原理非常简单,本质上就是一个包含大量case语句的for循环,但是由于SQLite的指令较多(在version 3.3.6中是139条),所以代码比较庞大。 SQLite的解释引擎是在一个方法中实现的:

  1. int sqlite3VdbeExec(
  2. Vdbe *p /* The VDBE */
  3. )

具体代码如下(为了阅读,去掉了一些不影响阅读的代码,具体见SQLite的源码):

  1. /*执行VDBE程序.当从数据库中取出一行数据时,该函数会调用回调函数(如果有的话),
  2. **或者返回SQLITE_ROW.
  3. */
  4. int sqlite3VdbeExec(
  5. Vdbe *p /* The VDBE */
  6. ){
  7. //指令计数器
  8. int pc; /* The program counter */
  9. //当前指令
  10. Op *pOp; /* Current operation */
  11. int rc = SQLITE_OK; /* Value to return */
  12. //数据库
  13. sqlite3 *db = p->db; /* The database */
  14. u8 encoding = ENC(db); /* The database encoding */
  15. //栈顶
  16. Mem *pTos; /* Top entry in the operand stack */
  17. if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE;
  18. //当前栈顶指针
  19. pTos = p->pTos;
  20. if( p->rc==SQLITE_NOMEM ){
  21. /* This happens if a malloc() inside a call to sqlite3_column_text() or
  22. ** sqlite3_column_text16() failed. */
  23. goto no_mem;
  24. }
  25. p->rc = SQLITE_OK;
  26. //如果需要进行出栈操作,则进行出栈操作
  27. if( p->popStack ){
  28. popStack(&pTos, p->popStack);
  29. p->popStack = 0;
  30. }
  31. //表明栈中没有结果
  32. p->resOnStack = 0;
  33. db->busyHandler.nBusy = 0;
  34. //执行指令
  35. for(pc=p->pc; rc==SQLITE_OK; pc++){
  36. //取出操作码
  37. pOp = &p->aOp[pc];
  38. switch( pOp->opcode ){
  39. //跳到操作数P2指向的指令
  40. case OP_Goto: { /* no-push */
  41. CHECK_FOR_INTERRUPT;
  42. //设置pc
  43. pc = pOp->p2 - 1;
  44. break;
  45. }
  46. //P1入栈
  47. case OP_Integer: {
  48. //当前栈顶指针上移
  49. pTos++;
  50. //设为整型
  51. pTos->flags = MEM_Int;
  52. //取操作数P1,并赋值
  53. pTos->i = pOp->p1;
  54. break;
  55. }
  56. //其它指令的实现
  57. }//end switch
  58. }//end for
  59. }

3.6、指令实例分析

由于篇幅限制,仅给出几条的指令的实现,其它具体实现见源码。

1、Callback指令

//该指令执行后,PC将指向下一条指令. //栈中栈顶的P1个值为查询的结果.该指令会导致sqlite3_step()函数将以SQLITE_ROW为返回码 //而结束运行.此时用户程序就可以通过sqlite3_column_XXX读取位于栈中的数据了. //当sqlite3_step()再一次运行时,栈顶的P1个值会在执行Next指令前自动出栈.

  1. case OP_Callback: { /* no-push */
  2. Mem *pMem;
  3. Mem *pFirstColumn;
  4. assert( p->nResColumn==pOp->p1 );
  5. /* Data in the pager might be moved or changed out from under us
  6. ** in between the return from this sqlite3_step() call and the
  7. ** next call to sqlite3_step(). So deephermeralize everything on
  8. ** the stack. Note that ephemeral data is never stored in memory
  9. ** cells so we do not have to worry about them.
  10. */
  11. pFirstColumn = &pTos[0-pOp->p1];
  12. for(pMem = p->aStack; pMem<pFirstColumn; pMem++){
  13. Deephemeralize(pMem);
  14. }
  15. /* Invalidate all ephemeral cursor row caches */
  16. p->cacheCtr = (p->cacheCtr + 2)|1;
  17. /* Make sure the results of the current row are \000 terminated
  18. ** and have an assigned type. The results are deephemeralized as
  19. ** as side effect.
  20. */
  21. for(; pMem<=pTos; pMem++ ){
  22. sqlite3VdbeMemNulTerminate(pMem);
  23. //设置结果集中的数据类型
  24. storeTypeInfo(pMem, encoding);
  25. }
  26. /* Set up the statement structure so that it will pop the current
  27. ** results from the stack when the statement returns.
  28. */
  29. p->resOnStack = 1; //栈上有结果
  30. p->nCallback++; //回调次数加1
  31. //出栈的数据个数,在下次执行VDBE时,会先进行出栈操作
  32. p->popStack = pOp->p1;
  33. //程序计数器加1
  34. p->pc = pc + 1;
  35. //设置vdbe的栈顶指针,此时,栈中保存有结果
  36. p->pTos = pTos;
  37. /*注意:这里不是break,而是return; 向sqlite3_step()返回SQLITE_ROW.
  38. **当用户程序重新调用sqlite3_step()时,重新执行VDBE.
  39. */
  40. return SQLITE_ROW;
  41. }

2、Rewind指令

  1. /*移动当前游标到表或索引的第一条记录.
  2. **如果表为空且p2>0,则跳到p2处;如果p2为0且表不空,则执行下一条指令.
  3. */
  4. case OP_Rewind: { /* no-push */
  5. int i = pOp->p1;
  6. Cursor *pC;
  7. BtCursor *pCrsr;
  8. int res;
  9. assert( i>=0 && i<p->nCursor );
  10. //取得当前游标
  11. pC = p->apCsr[i];
  12. assert( pC!=0 );
  13. if( (pCrsr = pC->pCursor)!=0 ){
  14. //调用B-tree模块,移动游标到第一条记录
  15. rc = sqlite3BtreeFirst(pCrsr, &res);
  16. pC->atFirst = res==0;
  17. pC->deferredMoveto = 0;
  18. pC->cacheStatus = CACHE_STALE;
  19. }else{
  20. res = 1;
  21. }
  22. pC->nullRow = res;
  23. if( res && pOp->p2>0 ){
  24. pc = pOp->p2 - 1;
  25. }
  26. break;
  27. }

3、Column指令

  1. /*解析当前游标指定的记录的数据
  2. **p1为当前游标索引号,p2为列号
  3. */
  4. case OP_Column: {
  5. u32 payloadSize; /* Number of bytes in the record */
  6. int p1 = pOp->p1; /* P1 value of the opcode */
  7. //列号
  8. int p2 = pOp->p2; /* column number to retrieve */
  9. //VDBE游标
  10. Cursor *pC = 0; /* The VDBE cursor */
  11. char *zRec; /* Pointer to complete record-data */
  12. //btree游标
  13. BtCursor *pCrsr; /* The BTree cursor */
  14. u32 *aType; /* aType[i] holds the numeric type of the i-th column */
  15. u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */
  16. //列数
  17. u32 nField; /* number of fields in the record */
  18. int len; /* The length of the serialized data for the column */
  19. int i; /* Loop counter */
  20. char *zData; /* Part of the record being decoded */
  21. Mem sMem; /* For storing the record being decoded */
  22. sMem.flags = 0;
  23. assert( p1<p->nCursor );
  24. //栈顶指针上移
  25. pTos++;
  26. pTos->flags = MEM_Null;
  27. /* This block sets the variable payloadSize to be the total number of
  28. ** bytes in the record.
  29. **
  30. ** zRec is set to be the complete text of the record if it is available.
  31. ** The complete record text is always available for pseudo-tables
  32. ** If the record is stored in a cursor, the complete record text
  33. ** might be available in the pC->aRow cache. Or it might not be.
  34. ** If the data is unavailable, zRec is set to NULL.
  35. **
  36. ** We also compute the number of columns in the record. For cursors,
  37. ** the number of columns is stored in the Cursor.nField element. For
  38. ** records on the stack, the next entry down on the stack is an integer
  39. ** which is the number of records.
  40. */
  41. //设置游标
  42. pC = p->apCsr[p1];
  43. assert( pC!=0 );
  44. if( pC->pCursor!=0 ){
  45. /* The record is stored in a B-Tree */
  46. //移到当前游标
  47. rc = sqlite3VdbeCursorMoveto(pC);
  48. if( rc ) goto abort_due_to_error;
  49. zRec = 0;
  50. pCrsr = pC->pCursor;
  51. if( pC->nullRow ){
  52. payloadSize = 0;
  53. }else if( pC->cacheStatus==p->cacheCtr ){
  54. payloadSize = pC->payloadSize;
  55. zRec = (char*)pC->aRow;
  56. }else if( pC->isIndex ){
  57. i64 payloadSize64;
  58. sqlite3BtreeKeySize(pCrsr, &payloadSize64);
  59. payloadSize = payloadSize64;
  60. }else{
  61. //解析数据,payloadSize保存cell的数据字节数
  62. sqlite3BtreeDataSize(pCrsr, &payloadSize);
  63. }
  64. nField = pC->nField;
  65. }else if( pC->pseudoTable ){
  66. /* The record is the sole entry of a pseudo-table */
  67. payloadSize = pC->nData;
  68. zRec = pC->pData;
  69. pC->cacheStatus = CACHE_STALE;
  70. assert( payloadSize==0 || zRec!=0 );
  71. nField = pC->nField;
  72. pCrsr = 0;
  73. }else{
  74. zRec = 0;
  75. payloadSize = 0;
  76. pCrsr = 0;
  77. nField = 0;
  78. }
  79. /* If payloadSize is 0, then just push a NULL onto the stack. */
  80. if( payloadSize==0 ){
  81. assert( pTos->flags==MEM_Null );
  82. break;
  83. }
  84. assert( p2<nField );
  85. /* Read and parse the table header. Store the results of the parse
  86. ** into the record header cache fields of the cursor.
  87. */
  88. if( pC && pC->cacheStatus==p->cacheCtr ){
  89. aType = pC->aType;
  90. aOffset = pC->aOffset;
  91. }else{
  92. u8 *zIdx; /* Index into header */
  93. u8 *zEndHdr; /* Pointer to first byte after the header(指向header之后的第一个字节)*/
  94. u32 offset; /* Offset into the data */
  95. int szHdrSz; /* Size of the header size field at start of record */
  96. int avail; /* Number of bytes of available data */
  97. //数据类型数组
  98. aType = pC->aType;
  99. if( aType==0 ){
  100. //每个数据类型分配8字节---sizeof(aType)==4
  101. pC->aType = aType = sqliteMallocRaw( 2*nField*sizeof(aType) );
  102. }
  103. if( aType==0 ){
  104. goto no_mem;
  105. }
  106. //每列数据的偏移
  107. pC->aOffset = aOffset = &aType[nField];
  108. pC->payloadSize = payloadSize;
  109. pC->cacheStatus = p->cacheCtr;
  110. /* Figure out how many bytes are in the header */
  111. if( zRec ){
  112. zData = zRec;
  113. }else{
  114. if( pC->isIndex ){
  115. zData = (char*)sqlite3BtreeKeyFetch(pCrsr, &avail);
  116. }else{
  117. //获取数据
  118. zData = (char*)sqlite3BtreeDataFetch(pCrsr, &avail);
  119. }
  120. /* If KeyFetch()/DataFetch() managed to get the entire payload,
  121. ** save the payload in the pC->aRow cache. That will save us from
  122. ** having to make additional calls to fetch the content portion of
  123. ** the record.
  124. */
  125. if( avail>=payloadSize ){
  126. zRec = zData;
  127. pC->aRow = (u8*)zData;
  128. }else{
  129. pC->aRow = 0;
  130. }
  131. }
  132. assert( zRec!=0 || avail>=payloadSize || avail>=9 );
  133. //获得header size
  134. szHdrSz = GetVarint((u8*)zData, offset);
  135. /* The KeyFetch() or DataFetch() above are fast and will get the entire
  136. ** record header in most cases. But they will fail to get the complete
  137. ** record header if the record header does not fit on a single page
  138. ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to
  139. ** acquire the complete header text.
  140. */
  141. if( !zRec && avail<offset ){
  142. rc = sqlite3VdbeMemFromBtree(pCrsr, 0, offset, pC->isIndex, &sMem);
  143. if( rc!=SQLITE_OK ){
  144. goto op_column_out;
  145. }
  146. zData = sMem.z;
  147. }
  148. /* 一个记录的例子:
  149. ** 08 | 08 |04 00 13 01 | 63 61 74 01
  150. ** 08: nSize,payload总的大小——后面8个字节
  151. ** 08: 关键字大小,对于整型则为关键字本身
  152. ** 04: header size,包括本身共4个字节——04 00 13 01
  153. ** 00: 第一列的数据类型——空类型
  154. ** 13: 第二列的数据类型——字符串,长为(19-13)/2=3——“cat”
  155. ** 01: 第三列的数据类型——整型,占一个字节——1
  156. ** 对于这里的zData保存的数据为:04 00 13 01 63 61 74 01
  157. */
  158. //header之后的数据,对于上例为:63 61 74 01
  159. zEndHdr = (u8 *)&zData[offset];
  160. //header数据的索引号,对于上例为:00 13 01
  161. zIdx = (u8 *)&zData[szHdrSz];
  162. /* Scan the header and use it to fill in the aType[] and aOffset[]
  163. ** arrays. aType[i] will contain the type integer for the i-th
  164. ** column and aOffset[i] will contain the offset from the beginning
  165. ** of the record to the start of the data for the i-th column
  166. */
  167. /*扫描header,然后设置aType[]和aOffset[]数组; aType[i]为第i列的数据类型,
  168. **aOffset[i]为第i列数据相对于记录的开始的偏移.
  169. */
  170. for(i=0; i<nField; i++){
  171. if( zIdx<zEndHdr ){
  172. //计算每一列数据的偏移
  173. aOffset[i] = offset;
  174. //计算每一列的数据类型
  175. zIdx += GetVarint(zIdx, aType[i]);
  176. //offset指向下一列
  177. offset += sqlite3VdbeSerialTypeLen(aType[i]);
  178. }else{
  179. /* If i is less that nField, then there are less fields in this
  180. ** record than SetNumColumns indicated there are columns in the
  181. ** table. Set the offset for any extra columns not present in
  182. ** the record to 0. This tells code below to push a NULL onto the
  183. ** stack instead of deserializing a value from the record.
  184. */
  185. aOffset[i] = 0;
  186. }
  187. }
  188. Release(&sMem);
  189. sMem.flags = MEM_Null;
  190. /* If we have read more header data than was contained in the header,
  191. ** or if the end of the last field appears to be past the end of the
  192. ** record, then we must be dealing with a corrupt database.
  193. */
  194. if( zIdx>zEndHdr || offset>payloadSize ){
  195. rc = SQLITE_CORRUPT_BKPT;
  196. goto op_column_out;
  197. }
  198. }
  199. /* Get the column information. If aOffset[p2] is non-zero, then
  200. ** deserialize the value from the record. If aOffset[p2] is zero,
  201. ** then there are not enough fields in the record to satisfy the
  202. ** request. In this case, set the value NULL or to P3 if P3 is
  203. ** a pointer to a Mem object.
  204. */
  205. //获取P2指定的列的数据
  206. if( aOffset[p2] ){
  207. assert( rc==SQLITE_OK );
  208. if( zRec ){
  209. //取得该列的数据
  210. zData = &zRec[aOffset[p2]];
  211. }else{
  212. len = sqlite3VdbeSerialTypeLen(aType[p2]);
  213. rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex,&sMem);
  214. if( rc!=SQLITE_OK ){
  215. goto op_column_out;
  216. }
  217. zData = sMem.z;
  218. }
  219. //解析zData,并将结果保存在pTos中
  220. sqlite3VdbeSerialGet((u8*)zData, aType[p2], pTos);
  221. pTos->enc = encoding;
  222. }else{
  223. if( pOp->p3type==P3_MEM ){
  224. sqlite3VdbeMemShallowCopy(pTos, (Mem *)(pOp->p3), MEM_Static);
  225. }else{
  226. pTos->flags = MEM_Null;
  227. }
  228. }
  229. /* If we dynamically allocated space to hold the data (in the
  230. ** sqlite3VdbeMemFromBtree() call above) then transfer control of that
  231. ** dynamically allocated space over to the pTos structure.
  232. ** This prevents a memory copy.
  233. */
  234. if( (sMem.flags & MEM_Dyn)!=0 ){
  235. assert( pTos->flags & MEM_Ephem );
  236. assert( pTos->flags & (MEM_Str|MEM_Blob) );
  237. assert( pTos->z==sMem.z );
  238. assert( sMem.flags & MEM_Term );
  239. pTos->flags &= ~MEM_Ephem;
  240. pTos->flags |= MEM_Dyn|MEM_Term;
  241. }
  242. /* pTos->z might be pointing to sMem.zShort[]. Fix that so that we
  243. ** can abandon sMem */
  244. rc = sqlite3VdbeMemMakeWriteable(pTos);
  245. op_column_out:
  246. break;
  247. }

4、Next指令

  1. /*移动游标,使其指向表的下一个记录
  2. */
  3. case OP_Prev: /* no-push */
  4. case OP_Next: { /* no-push */
  5. Cursor *pC;
  6. BtCursor *pCrsr;
  7. CHECK_FOR_INTERRUPT;
  8. assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  9. pC = p->apCsr[pOp->p1];
  10. assert( pC!=0 );
  11. if( (pCrsr = pC->pCursor)!=0 ){
  12. int res;
  13. if( pC->nullRow ){
  14. res = 1;
  15. }else{
  16. assert( pC->deferredMoveto==0 );
  17. //调用B-tree模块,移动游标指向下一条记录
  18. rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) :
  19. sqlite3BtreePrevious(pCrsr, &res);
  20. pC->nullRow = res;
  21. pC->cacheStatus = CACHE_STALE;
  22. }
  23. if( res==0 ){
  24. pc = pOp->p2 - 1;
  25. sqlite3_search_count++;
  26. }
  27. }else{
  28. pC->nullRow = 1;
  29. }
  30. pC->rowidIsValid = 0;
  31. break;
  32. }