概述
首先要弄明白自己学源码的目的是什么:
我的目的,就是弄清楚sql的执行原理,对整个代码有个大概的了解,可以写个很小的demo,或者参考别人的demo实现一个小型数据库。
如何学习sqlite源码? - 知乎
参考 https://huili.github.io/
架构如下
3进程模型
vdbe程序在形式上类似于汇编语言。一个数据库连接,可能拥有多个vdbe虚拟机,这些虚拟机被组织成一个双向链表
操作码添加
就是给vdbe程序添加某个操作码
int sqlite3VdbeAddOp3(Vdbe p, int op, int p1, int p2, int p3) //对应于操作码的操作数为3个的情况
参数p是指向vdbe的指针,op是需要添加的操作码,p1、p2、p3分别对应操作数1、操作数2、操作数3,
sqlite3VdbeMakeLabel(Vdbe p)实现。 该函数实现了为一个即将编码的指令创建一个新的符号标签的过程。我的理解,类似于symbol link,不过Label是个负数
添加操作码链函数
该功能主要由函数
int sqlite3VdbeAddOpList(Vdbe p, int nOp, VdbeOpList const aOp)
实现,就是一次加多个操作码
3.6.2.1 相关修改函数
void sqlite3VdbeChangeP2(Vdbe p, u32 addr, int val)
修改操作数P2的值为val
sqlite3VdbeJumpHere(Vdbe p, int addr)
jump修改P2的地址值为addr
3.6.2.2 配置函数
void sqlite3VdbeRunOnlyOnce(Vdbe p) //该函数保证vdbe只执行一次
int sqlite3VdbeCurrentAddr(Vdbe p) //该函数返回下一个指令地址
void sqlite3VdbeUsesBtree(Vdbe p, int i) //向vdbe声明使用db->aDb[i]指向的B树。
指向的B树也即要使用的数据库。
void freeEphemeralFunction(sqlite3 db, FuncDef pDef) 如果参数pDef所指向的FuncDef结构为临时结构,那么函数将释放其所占的空间,否则什么也不做。
sqlite3VdbeLinkSubProgram(Vdbe pVdbe, SubProgram p) 将子程序p传递给vdbe,并将其加入到vdbe的子程序链中,该链用于VM销毁时销毁所有与之有关的子程序。
void sqlite3VdbeLeave(Vdbe p) 该函数功能是解锁所有在sqlite3VdbeEnter加锁的B树。
看到3.6.3,没看
4 关系查询处理器
客户程序获取元组,通常一次一个元组或一小批元组. 元祖是啥?
其实查询,更新等,都会被优化器优化
先进行From,将表进行连接,查询出的是个“笛卡尔积”集,然后交给on子句连接过滤,再交给where进行条件过滤,再进行Group分组,再进行having过滤分组,再进行select输出,如果有order,limit,offset再对结果集进行排序,再返回限定行数和限定偏移量的结果集。
Select结构体,汇总各种语法树(比如from子句语法树,where语法树,group by语法树),然后sqlite3Select生成执行计划,所谓的执行计划,其实就是操作指令OpCode
struct Select {
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
LogEst nSelectRow; /* Estimated number of result rows */
u32 selFlags; /* Various SF_* values */
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
u32 selId; /* Unique identifier number for this SELECT */
int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */
ExprList *pEList; /* The fields of the result */
SrcList *pSrc; /* The FROM clause */
Expr *pWhere; /* The WHERE clause */
ExprList *pGroupBy; /* The GROUP BY clause */
Expr *pHaving; /* The HAVING clause */
ExprList *pOrderBy; /* The ORDER BY clause */
Select *pPrior; /* Prior select in a compound select statement */
Select *pNext; /* Next select to the left in a compound */
Expr *pLimit; /* LIMIT expression. NULL means not used. */
With *pWith; /* WITH clause attached to this select. Or NULL. */
#ifndef SQLITE_OMIT_WINDOWFUNC
Window *pWin; /* List of window functions */
Window *pWinDefn; /* List of named window definitions */
#endif
};
查询结果的处理,由SelectDect指定:
#define SRT_Output 9 /* Output each row of result */
#define SRT_Mem 10 /* Store result in a memory cell */
#define SRT_Set 11 /* Store results as keys in an index */
#define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */
#define SRT_Coroutine 13 /* Generate a single row of result */
#define SRT_Table 14 /* Store result as data with an automatic rowid */
#define SRT_Upfrom 15 /* Store result as data with rowid */
/*
** An instance of this object describes where to put of the results of
** a SELECT statement.
*/
struct SelectDest {
u8 eDest; /* How to dispose of the results. One of SRT_* above. */
int iSDParm; /* A parameter used by the eDest disposal method */
int iSDParm2; /* A second parameter for the eDest disposal method */
int iSdst; /* Base register where results are written */
int nSdst; /* Number of registers allocated */
char *zAffSdst; /* Affinity used when eDest==SRT_Set */
ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
};
4.1.5 看到selectInnerLoop
static void selectInnerLoop(
Parse *pParse, /*解析上下文*/
Select *p, /*声明select查询结构体*/
ExprList *pEList, /*被提取的值列表*/
int srcTab, /*提取数据的表*/
int nColumn, /*源表的行数*/
ExprList *pOrderBy, /*如果不是空,对结果排序使用这个关键词*/
SelectDest *pDest, /*处理结果集*/
int iContinue, /*跳过此处到下一行*/
)
4.1.7.1
优先级on,join,where;on会过滤出符合条件的行,然后 join,这样避免中间结果集太大;
where得先join,产生table1*table2的size结果集,然后在结果集选择where语句为true的,返回;所以where的中间结果集是蛮大的
4.1.7.2
where.c执行过程
流程图如上,where.c文件的主要功能就是生成VDBE编码来执行SQL命令中的WHERE子句:
例如:如果SQL是:
SELECT * FROM t1, t2, t3 WHERE ...;
那么会概念地生成以下代码:
foreach row1 in t1 do
foreach row2 in t2 do |-- 由sqlite3WhereBegin()生成
foreach row3 in t3 do /
...
end
end |-- 由sqlite3WhereEnd()生成
end /
比较重要的是WhereClause结构体
SqliteWhereBegin调用函数关系
SqliteWhereBegin是最核心的方法:
其中:
sqliteWhereBegin()函数是开始where子句的分析处理;
WhereClauseInit()函数是初始化whereClause结构体;
whereSplit()函数是把whereClause根据op分隔开来;
codeOneLoopStart()函数是为WHERE子句中实现的的第i级循环的代码的生成;
bestVirtualIndex()函数是用于计算虚拟表的最佳索引;
bestBtreeIndex()函数是用于选择最佳的Btree索引;
constructAutomatic()函数是用于创建自动索引;
exprAnalyzeAll()函数是循环调用exprAnalyze()分析where子句;
exprAnalyze()函数是分析分隔后的where子句;
allowedOp()函数是判断相应的运算符是否可以使用索引;
isLikeOrGlob()函数是判断like或glob语句是否能够进行优化;
isMatchOfColumn()函数是检查表达式是否是column MATCH expr形式;
exprAnalyzeOrTerm()函数是分析一个包含两个或更多OR子句连接的子句;
disableTerm()函数是在WHERE子句中禁用一个被分割的子句;
findTerm()函数是WHERE子句中查找一个被分割的子句;
打算阅读codeOneLoopStart,发现很多vdbe的基本方法,不知道用来干嘛的,所以需要回头看vdbe部分
5 文件系统
其实,数据库就是一个文件系统,比如里面回滚日志这些,都是和文件系统类似的,参考log_write
"SQLite 判断数据库文件是否完成了变更是依赖于回滚日志文件是否存在。"
MIT6 S.081 Lab9 file system_newbaby2012的专栏-CSDN博客
os interface主要是在os_win.c和os_unix里面,两者均定义了一些接口,比如osOpen,osWrite等,这些接口底层是调用open,write这些系统调用,在调用sqlite3_os_init(),也即初始化时声明了这些接口
挂载,其实可以理解为注册,系统调用接口的挂载相当于接口注册;设备挂载,相当于设备注册,以供操作系统使用,其实你想想游戏的注册,不也就是让这个账号能被使用吗;所以我怀疑,其实挂载,就是注册一个账号,而这个账号,其实就是对应某个内存地址,或者存储设备地址;其实就相当于在注册表里面加了一条映射记录,比如第几号映射哪块地址
SQLite有种4锁,前面三种是用户可以访问的,后面一种PENDING_LOCK是内部才能使用的过渡锁,比如NO_LOCK升级为SHARED_LOCK,或者SHARED_LOCK升级为RESERVED_LOCK,就需要先获取PENDING_LOCK
(1) SHARED_LOCK
(2) RESERVED_LOCK
(3) EXCLUSIVE_LOCK
(*) PENDING_LOCK
还有一个比较好奇的是,code generator生成虚拟机指令,非常好奇如何实现的
sqlite3WhereSplit
用来分割子表达式,如下:
The WhereClause structure
** is filled with pointers to subexpressions. For example:
**
** WHERE a=='hello' AND coalesce(b,11)<10 AND (c+12!=d OR c==22)
** ________/ _______________/ ________________/
** slot[0] slot[1] slot[2]
**
** The original WHERE clause in pExpr is unaltered. All this routine
** does is make slot[] entries point to substructure within pExpr.
sqlite3ExprSkipCollateAndLikely
在拆分where语句时,用来跳过Collate和Like关键字,Collate主要是和排序规则相关
sqlite3VdbeExplain
为操作添加一个操作码OP_Explain
sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0,
zMsg, P4_DYNAMIC);
whereClauseInsert
WhereTerm是个啥? where的语句吗
WhereClause *pWC; /* The clause this term is part of */
应该是where语句的一部分
All WhereTerms are collected into a single WhereClause structure.
A WhereTerm might also be two or more subterms connected by OR: ** ** (t1.X <op> <expr>) OR (t1.Y <op> <expr>) OR ....
所以whereTerm就是whereClause的一个小单元
代码风格
可以看到函数前面,一般都是Assert,进行判空处理
为了减少参数传递,一般传指针,java可以一般传一个比较大的对象
最后
以上就是幸福美女为你收集整理的SQLite源码学习笔记3进程模型4 关系查询处理器5 文件系统代码风格的全部内容,希望文章能够帮你解决SQLite源码学习笔记3进程模型4 关系查询处理器5 文件系统代码风格所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复