SQLite源码学习笔记

首先要弄明白自己学源码的目的是什么:

我的目的,就是弄清楚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可以一般传一个比较大的对象

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: CSDN是一个知识分享平台,其中包含了大量关于Qt和SQLite源码分享和教程。Qt是一个跨平台的应用程序开发框架,它提供了一套丰富的图形界面和基础库,方便开发者进行跨平台的软件开发。SQLite是一种嵌入式数据库,它提供了一个轻量级、速度快、易于使用的数据库引擎。 在CSDN上可以找到很多关于使用Qt和SQLite开发应用程序的示例代码和教程。比如,可以找到如何使用Qt进行GUI设计和编写数据库操作的代码示例。通过这些源码和教程,开发者可以学习如何在Qt中使用SQLite进行数据库操作,比如创建数据库、执行SQL语句、插入、删除、修改和查询数据等。这对于想要开发跨平台应用程序并具有持久化数据存储需求的开发者来说非常有帮助。 此外,CSDN上还有很多关于Qt和SQLite深入原理的文章和讨论。比如,可以找到有关SQLite数据库连接、事务处理、性能优化等方面的讨论和源码分析。通过阅读这些文章,开发者可以更深入地了解Qt和SQLite的工作原理,从而更好地应用于实际项目中。 总的来说,CSDN上关于Qt和SQLite源码和教程非常丰富,可以为开发者提供很好的学习和参考资料。无论是想要入门学习还是深入研究,CSDN都是一个不错的选择。 ### 回答2: CSDN是一个知名的技术社区,提供各种技术相关的文章、教程和资源。QT是一种跨平台的应用程序开发框架,可以用于开发各种类型的应用程序。SQLite是一种轻量级的嵌入式数据库,可以在各种平台上使用。 CSDN上有很多关于QT和SQLite的文章和分享,其中包括相关的源码和教程。我们可以通过搜索关键词"QT SQLite",在CSDN上找到很多与此相关的内容。 在这些源码中,主要涉及到QT如何使用SQLite数据库进行数据存储和管理。我们可以学习如何建立数据库连接、创建表格、插入和读取数据等操作。同时,还可以了解如何使用QT提供的各种功能和类来简化数据库操作的步骤,例如利用QT的模型/视图架构进行数据的展示和修改。 CSDN上的源码分享可能是以示例程序或者案例的形式存在的,我们可以通过运行这些程序,理解和学习QT和SQLite的整合使用方法。通过阅读源码和教程,我们可以学习到如何在QT中使用SQLite进行数据持久化存储,从而应对各种应用程序的需求。 总之,CSDN上关于QT和SQLite源码分享是非常有价值的资源,可以帮助我们学习如何在QT应用程序中使用SQLite数据库。 ### 回答3: CSDN是一个知名的技术社区,提供了大量的技术文章和资源供开发者参考学习。其中涉及到的内容包括Qt和SQLite源码。 Qt是一款跨平台的应用程序开发框架,它提供了丰富的工具和组件,能够帮助开发者快速构建高效、可靠的应用程序。Qt的源码可以在CSDN上找到,开发者可以通过阅读源码了解Qt的实现原理,学习Qt框架的使用方法和技巧。同时,CSDN上也有很多关于Qt的博客和教程,可以帮助开发者更加深入地理解和应用Qt。 SQLite是一种轻型的嵌入式数据库引擎,它以其小巧、高效和易用的特点而广受欢迎。SQLite源码也可以在CSDN上找到,开发者可以通过研究源码了解SQLite的内部机制和算法,从而更好地理解和应用SQLite。CSDN上还有关于SQLite的文章和教程,可以帮助开发者学习如何使用SQLite进行数据存储和处理。 总的来说,CSDN是一个宝贵的资源,提供了Qt和SQLite源码和相关教程,供开发者学习和参考。通过研究源码,开发者可以深入理解这两个技术的底层原理,提升自己的技术能力和开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值