2021SC@SDUSC SQLite源码分析(八)————SQLite虚拟机指令集


为了执行一个SQL语句,SQLite库首先解析SQL,分析该语句,然后生成简短的程序来执行该语句。产生的程序将由SQLite库实现的虚拟机来执行。虚拟机的源代码是vdbe.c文件。所有操作码的定义都包含在源文件注释中,下面是几个操作码指令实例分析

一、OP_Column

解析当前游标指定的记录的数据
p1为当前游标索引号,p2为列号


case OP_Column: {u32 payloadSize;   /* Number of bytes in the record */int p1 = pOp->p1;  /* P1 value of the opcode *///列号int p2 = pOp->p2;  /* column number to retrieve *///VDBE游标Cursor *pC = 0;    /* The VDBE cursor */char *zRec;        /* Pointer to complete record-data *///btree游标BtCursor *pCrsr;   /* The BTree cursor */u32 *aType;        /* aType[i] holds the numeric type of the i-th column */u32 *aOffset;      /* aOffset[i] is offset to start of data for i-th column *///列数u32 nField;        /* number of fields in the record */int len;           /* The length of the serialized data for the column */int i;             /* Loop counter */char *zData;       /* Part of the record being decoded */Mem sMem;          /* For storing the record being decoded */sMem.flags = 0;assert( p1<p->nCursor );//栈顶指针上移pTos++;pTos->flags = MEM_Null;/* This block sets the variable payloadSize to be the total number of** bytes in the record.**** zRec is set to be the complete text of the record if it is available.** The complete record text is always available for pseudo-tables** If the record is stored in a cursor, the complete record text** might be available in the  pC->aRow cache.  Or it might not be.** If the data is unavailable,  zRec is set to NULL.**** We also compute the number of columns in the record.  For cursors,** the number of columns is stored in the Cursor.nField element.  For** records on the stack, the next entry down on the stack is an integer** which is the number of records.*///设置游标pC = p->apCsr[p1];assert( pC!=0 );if( pC->pCursor!=0 ){/* The record is stored in a B-Tree *///移到当前游标rc = sqlite3VdbeCursorMoveto(pC);if( rc ) goto abort_due_to_error;zRec = 0;pCrsr = pC->pCursor;if( pC->nullRow ){payloadSize = 0;}else if( pC->cacheStatus==p->cacheCtr ){payloadSize = pC->payloadSize;zRec = (char*)pC->aRow;}else if( pC->isIndex ){i64 payloadSize64;sqlite3BtreeKeySize(pCrsr, &payloadSize64);payloadSize = payloadSize64;}else{//解析数据,payloadSize保存cell的数据字节数sqlite3BtreeDataSize(pCrsr, &payloadSize);}nField = pC->nField;}else if( pC->pseudoTable ){/* The record is the sole entry of a pseudo-table */payloadSize = pC->nData;zRec = pC->pData;pC->cacheStatus = CACHE_STALE;assert( payloadSize==0 || zRec!=0 );nField = pC->nField;pCrsr = 0;}else{zRec = 0;payloadSize = 0;pCrsr = 0;nField = 0;}/* If payloadSize is 0, then just push a NULL onto the stack. */if( payloadSize==0 ){assert( pTos->flags==MEM_Null );break;}assert( p2<nField );/* Read and parse the table header.  Store the results of the parse** into the record header cache fields of the cursor.*/if( pC && pC->cacheStatus==p->cacheCtr ){aType = pC->aType;aOffset = pC->aOffset;}else{u8 *zIdx;        /* Index into header */u8 *zEndHdr;     /* Pointer to first byte after the header(指向header之后的第一个字节)*/u32 offset;      /* Offset into the data */int szHdrSz;     /* Size of the header size field at start of record */int avail;       /* Number of bytes of available data *///数据类型数组aType = pC->aType;if( aType==0 ){//每个数据类型分配8字节---sizeof(aType)==4pC->aType = aType = sqliteMallocRaw( 2*nField*sizeof(aType) );}if( aType==0 ){goto no_mem;}//每列数据的偏移pC->aOffset = aOffset = &aType[nField];pC->payloadSize = payloadSize;pC->cacheStatus = p->cacheCtr;/* Figure out how many bytes are in the header */if( zRec ){zData = zRec;}else{if( pC->isIndex ){zData = (char*)sqlite3BtreeKeyFetch(pCrsr, &avail);}else{//获取数据zData = (char*)sqlite3BtreeDataFetch(pCrsr, &avail);}/* If KeyFetch()/DataFetch() managed to get the entire payload,** save the payload in the pC->aRow cache.  That will save us from** having to make additional calls to fetch the content portion of** the record.*/if( avail>=payloadSize ){zRec = zData;pC->aRow = (u8*)zData;}else{pC->aRow = 0;}}assert( zRec!=0 || avail>=payloadSize || avail>=9 );//获得header sizeszHdrSz = GetVarint((u8*)zData, offset);/* The KeyFetch() or DataFetch() above are fast and will get the entire** record header in most cases.  But they will fail to get the complete** record header if the record header does not fit on a single page** in the B-Tree.  When that happens, use sqlite3VdbeMemFromBtree() to** acquire the complete header text.*/if( !zRec && avail<offset ){rc = sqlite3VdbeMemFromBtree(pCrsr, 0, offset, pC->isIndex, &sMem);if( rc!=SQLITE_OK ){goto op_column_out;}zData = sMem.z;}
/* 一个记录的例子:
** 08 | 08 |04 00 13 01 | 63 61 74 01
** 08: nSize,payload总的大小——后面8个字节
** 08: 关键字大小,对于整型则为关键字本身
** 04: header size,包括本身共4个字节——04 00 13 01
** 00: 第一列的数据类型——空类型
** 13: 第二列的数据类型——字符串,长为(19-13)/2=3——“cat”
** 01: 第三列的数据类型——整型,占一个字节——1
** 对于这里的zData保存的数据为:04 00 13 01 63 61 74 01
*///header之后的数据,对于上例为:63 61 74 01zEndHdr = (u8 *)&zData[offset];//header数据的索引号,对于上例为:00 13 01zIdx = (u8 *)&zData[szHdrSz];/* Scan the header and use it to fill in the aType[] and aOffset[]** arrays.  aType[i] will contain the type integer for the i-th** column and aOffset[i] will contain the offset from the beginning** of the record to the start of the data for the i-th column*//*扫描header,然后设置aType[]和aOffset[]数组; aType[i]为第i列的数据类型,aOffset[i]为第i列数据相对于记录的开始的偏移.*/for(i=0; i<nField; i++){if( zIdx<zEndHdr ){//计算每一列数据的偏移aOffset[i] = offset;//计算每一列的数据类型zIdx += GetVarint(zIdx, aType[i]);//offset指向下一列offset += sqlite3VdbeSerialTypeLen(aType[i]);}else{/* If i is less that nField, then there are less fields in this** record than SetNumColumns indicated there are columns in the** table. Set the offset for any extra columns not present in** the record to 0. This tells code below to push a NULL onto the** stack instead of deserializing a value from the record.*/aOffset[i] = 0;}}Release(&sMem);sMem.flags = MEM_Null;/* If we have read more header data than was contained in the header,** or if the end of the last field appears to be past the end of the** record, then we must be dealing with a corrupt database.*/if( zIdx>zEndHdr || offset>payloadSize ){rc = SQLITE_CORRUPT_BKPT;goto op_column_out;}}/* Get the column information. If aOffset[p2] is non-zero, then ** deserialize the value from the record. If aOffset[p2] is zero,** then there are not enough fields in the record to satisfy the** request.  In this case, set the value NULL or to P3 if P3 is** a pointer to a Mem object.*///获取P2指定的列的数据if( aOffset[p2] ){assert( rc==SQLITE_OK );if( zRec ){//取得该列的数据zData = &zRec[aOffset[p2]];}else{len = sqlite3VdbeSerialTypeLen(aType[p2]);rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex,&sMem);if( rc!=SQLITE_OK ){goto op_column_out;}zData = sMem.z;}//解析zData,并将结果保存在pTos中sqlite3VdbeSerialGet((u8*)zData, aType[p2], pTos);pTos->enc = encoding;}else{if( pOp->p3type==P3_MEM ){sqlite3VdbeMemShallowCopy(pTos, (Mem *)(pOp->p3), MEM_Static);}else{pTos->flags = MEM_Null;}}/* If we dynamically allocated space to hold the data (in the** sqlite3VdbeMemFromBtree() call above) then transfer control of that** dynamically allocated space over to the pTos structure.** This prevents a memory copy.*/if( (sMem.flags & MEM_Dyn)!=0 ){assert( pTos->flags & MEM_Ephem );assert( pTos->flags & (MEM_Str|MEM_Blob) );assert( pTos->z==sMem.z );assert( sMem.flags & MEM_Term );pTos->flags &= ~MEM_Ephem;pTos->flags |= MEM_Dyn|MEM_Term;}/* pTos->z might be pointing to sMem.zShort[].  Fix that so that we** can abandon sMem */rc = sqlite3VdbeMemMakeWriteable(pTos);op_column_out:break;
}

二、OP_Next

移动游标使它指向表的下一个记录

case OP_Next: {        /* no-push */Cursor *pC;BtCursor *pCrsr;CHECK_FOR_INTERRUPT;assert( pOp->p1>=0 && pOp->p1<p->nCursor );pC = p->apCsr[pOp->p1];assert( pC!=0 );if( (pCrsr = pC->pCursor)!=0 ){int res;if( pC->nullRow ){res = 1;}else{assert( pC->deferredMoveto==0 );//调用B-tree模块,移动游标指向下一条记录rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) :sqlite3BtreePrevious(pCrsr, &res);pC->nullRow = res;pC->cacheStatus = CACHE_STALE;}if( res==0 ){pc = pOp->p2 - 1;sqlite3_search_count++;}}else{pC->nullRow = 1;}pC->rowidIsValid = 0;break;
}

三、OP_Callback

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

case OP_Callback: {            /* no-push */Mem *pMem;Mem *pFirstColumn;assert( p->nResColumn==pOp->p1 );/* Data in the pager might be moved or changed out from under us** in between the return from this sqlite3_step() call and the** next call to sqlite3_step().  So deephermeralize everything on ** the stack.  Note that ephemeral data is never stored in memory ** cells so we do not have to worry about them.*/pFirstColumn = &pTos[0-pOp->p1];for(pMem = p->aStack; pMem<pFirstColumn; pMem++){Deephemeralize(pMem);}/* 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.  The results are deephemeralized as** as side effect.*/for(; pMem<=pTos; pMem++ ){sqlite3VdbeMemNulTerminate(pMem);//设置结果集中的数据类型storeTypeInfo(pMem, encoding);}/* Set up the statement structure so that it will pop the current** results from the stack when the statement returns.*/p->resOnStack = 1; //栈上有结果p->nCallback++;  //回调次数加1//出栈的数据个数,在下次执行VDBE时,会先进行出栈操作p->popStack = pOp->p1;//程序计数器加1p->pc = pc + 1;//设置vdbe的栈顶指针,此时,栈中保存有结果p->pTos = pTos;return SQLITE_ROW;
}

2021SC@SDUSC SQLite源码分析(八)————SQLite虚拟机指令集相关推荐

  1. Spring Security源码分析八:Spring Security 退出

    为什么80%的码农都做不了架构师?>>> Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spr ...

  2. 【转】ABP源码分析八:Logger集成

    ABP使用Castle日志记录工具,并且可以使用不同的日志类库,比如:Log4Net, NLog, Serilog... 等等.对于所有的日志类库,Castle提供了一个通用的接口来实现,我们可以很方 ...

  3. Spring源码分析八:Mybatis ORM映射框架原理

    文章目录 (一)Mybatis单独操作数据库程序 1.1.数据库表 1.2.建立PO 1.3.建立mapper接口映射 1.4.建立Mybatis配置文件 1.5.建立mapper映射文件 1.6.测 ...

  4. mybatis的使用及源码分析(八) mybatis的rowbounds分析

    Mybatis提供了一个简单的逻辑分页类RowBounds,其原理类似于在内存中做了一个分页,不是数据库层面的分页,性能不算好,谨慎使用 一. RowBounds源码分析 1 RowBounds源码: ...

  5. python3.5源码分析-启动与虚拟机

    Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3启动流程概述 本文基于python3分析其基本的 ...

  6. MyBatis框架的使用及源码分析(八) MapperMethod

    从 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我们知道Mapper,通过MapperProxy ...

  7. tcp/ip 协议栈Linux内核源码分析八 路由子系统分析三 路由表

    内核版本:3.4.39 Linux路由子系统代码量虽说不是很多,但是难度还是有的,最近在分析路由子系统这一块,对它的框架有了基本的了解,如果要想掌握的话估计还得再花点时间阅读代码,先把框架记录下来.路 ...

  8. ernel 3.10内核源码分析--KVM相关--虚拟机运行

    1.基本原理 KVM虚拟机通过字符设备/dev/kvm的ioctl接口创建和运行,相关原理见之前的文章说明. 虚拟机的运行通过/dev/kvm设备ioctl VCPU接口的KVM_RUN指令实现, 在 ...

  9. storm源码分析研究(五)

    2021SC@SDUSC spout源码分析(四) 2021SC@SDUSC spout: ack机制 为保证无数据丢失,Storm/JStorm使用了非常漂亮的可靠性处理机制,当定义Topology ...

  10. 5-LVI-SAM源码分析_imageProjection初步分析

    2021SC@SDUSC LVI-SAM源码分析_imageProjection.cpp初步分析 文章目录 LVI-SAM源码分析_imageProjection.cpp初步分析 一.点云结构体 二. ...

最新文章

  1. 华为鸿蒙系统2.0发布了!AI人工智能大有可为!
  2. git常用基本简单命令
  3. python【蓝桥杯vip练习题库】ADV-234字符串跳步
  4. Dynamic LAN-to-LAN ××× 之 Router-to-Router
  5. python---memcache使用操作
  6. ps抠头发插件_「福利」PS抠图神级插件——VertusFluid Mask
  7. node-webkit File Dialog
  8. Shell break和continue命令
  9. 32款iOS开发插件和工具介绍[效率]
  10. pom.xml的配置详解
  11. 个人站立会议-----20181216
  12. STM8学习笔记---NTC热敏电阻的使用
  13. 【BOI2007】【BZOJ1176】Mokia
  14. day 39 mycql 数据库之约束
  15. 大神带你实现 NLP 从入门到获奖,还有免费算力可以薅
  16. js中this的用法
  17. Web项目部署到阿里云
  18. 3d胆码计算机方法,3D---定胆方法大全(转)
  19. 原来互联网公司想裁员还能这样玩?
  20. [内核内存] [arm64] 内存回收2---快速内存回收和直接内存回收

热门文章

  1. cmake multiple definition 问题解决方案
  2. 宁用ios不用鸿蒙,鸿蒙撕开谷歌安卓和苹果IOS垄断天幕,不能让华为单打独斗
  3. pip安装python库速度慢、失败及超时报错解决办法
  4. uni-app使用总结(1)
  5. leetcode 2
  6. .Net开发,屏蔽浏览器弹出广告的方法。
  7. 【Unity学习笔记】斜向走路变快的问题
  8. 安全篇-AES/RSA加密机制
  9. Spring DI Demo
  10. “大数据”要这样用才赚钱!