2021SC@SDUSC SQLite源码分析(八)————SQLite虚拟机指令集
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虚拟机指令集相关推荐
- Spring Security源码分析八:Spring Security 退出
为什么80%的码农都做不了架构师?>>> Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spr ...
- 【转】ABP源码分析八:Logger集成
ABP使用Castle日志记录工具,并且可以使用不同的日志类库,比如:Log4Net, NLog, Serilog... 等等.对于所有的日志类库,Castle提供了一个通用的接口来实现,我们可以很方 ...
- Spring源码分析八:Mybatis ORM映射框架原理
文章目录 (一)Mybatis单独操作数据库程序 1.1.数据库表 1.2.建立PO 1.3.建立mapper接口映射 1.4.建立Mybatis配置文件 1.5.建立mapper映射文件 1.6.测 ...
- mybatis的使用及源码分析(八) mybatis的rowbounds分析
Mybatis提供了一个简单的逻辑分页类RowBounds,其原理类似于在内存中做了一个分页,不是数据库层面的分页,性能不算好,谨慎使用 一. RowBounds源码分析 1 RowBounds源码: ...
- python3.5源码分析-启动与虚拟机
Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3启动流程概述 本文基于python3分析其基本的 ...
- MyBatis框架的使用及源码分析(八) MapperMethod
从 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我们知道Mapper,通过MapperProxy ...
- tcp/ip 协议栈Linux内核源码分析八 路由子系统分析三 路由表
内核版本:3.4.39 Linux路由子系统代码量虽说不是很多,但是难度还是有的,最近在分析路由子系统这一块,对它的框架有了基本的了解,如果要想掌握的话估计还得再花点时间阅读代码,先把框架记录下来.路 ...
- ernel 3.10内核源码分析--KVM相关--虚拟机运行
1.基本原理 KVM虚拟机通过字符设备/dev/kvm的ioctl接口创建和运行,相关原理见之前的文章说明. 虚拟机的运行通过/dev/kvm设备ioctl VCPU接口的KVM_RUN指令实现, 在 ...
- storm源码分析研究(五)
2021SC@SDUSC spout源码分析(四) 2021SC@SDUSC spout: ack机制 为保证无数据丢失,Storm/JStorm使用了非常漂亮的可靠性处理机制,当定义Topology ...
- 5-LVI-SAM源码分析_imageProjection初步分析
2021SC@SDUSC LVI-SAM源码分析_imageProjection.cpp初步分析 文章目录 LVI-SAM源码分析_imageProjection.cpp初步分析 一.点云结构体 二. ...
最新文章
- 华为鸿蒙系统2.0发布了!AI人工智能大有可为!
- git常用基本简单命令
- python【蓝桥杯vip练习题库】ADV-234字符串跳步
- Dynamic LAN-to-LAN ××× 之 Router-to-Router
- python---memcache使用操作
- ps抠头发插件_「福利」PS抠图神级插件——VertusFluid Mask
- node-webkit File Dialog
- Shell break和continue命令
- 32款iOS开发插件和工具介绍[效率]
- pom.xml的配置详解
- 个人站立会议-----20181216
- STM8学习笔记---NTC热敏电阻的使用
- 【BOI2007】【BZOJ1176】Mokia
- day 39 mycql 数据库之约束
- 大神带你实现 NLP 从入门到获奖,还有免费算力可以薅
- js中this的用法
- Web项目部署到阿里云
- 3d胆码计算机方法,3D---定胆方法大全(转)
- 原来互联网公司想裁员还能这样玩?
- [内核内存] [arm64] 内存回收2---快速内存回收和直接内存回收