Architecture of SQLite
Architecture of SQLite
介绍
本文档描述了SQLite库的体系结构。这里的信息对那些想要理解或修改SQLite内部工作的人很有用。
附近的图表显示了SQLite的主要组件以及它们如何互操作。以下文本解释了各个组件的作用。
概观
SQLite的工作原理是将SQL文本编译为字节码,然后使用虚拟机运行该字节码。
sqlite3_prepare_v2()和相关接口充当用于将SQL文本转换为字节码的编译器。sqlite3_stmt对象是用于实现单个SQL语句的单个字节码程序的容器。sqlite3_step()接口将一个字节码程序传递给虚拟机,并运行该程序直到它完成,或者形成一行结果返回,或者发生致命错误或中断。
接口
许多C语言接口可以在源文件main.c
,legacy.c
和vdbeapi.c中找到,
尽管一些例程分散在其他文件中,在这些文件中它们可以访问具有文件范围的数据结构。sqlite3_get_table()例程在table.c中
实现。sqlite3_mprintf()例程在printf.c中
找到。sqlite3_complete()接口位于tokenize.c中
。TCL接口由tclsqlite.c
实现。
为避免名称冲突,SQLite库中的所有外部符号都以前缀sqlite3
开头。那些用于外部使用的符号(换句话说,那些构成SQLite API的符号)会添加一个下划线,因此以sqlite3_
开头。扩展API有时会在下划线之前添加扩展名; 例如:sqlite3rbu_
或sqlite3session_
。
标记生成器
当包含SQL语句的字符串要被评估时,它首先被发送到标记器。标记器将SQL文本分解为标记并将这些标记逐个传递给解析器。标记器是在文件tokenize.c中
手动编码的。
请注意,在此设计中,标记器调用解析器。熟悉YACC和BISON的人可能习惯于以相反的方式做事 - 让解析器调用标记器。不过,令牌分析器调用分析器会更好,因为它可以做成线程安全的并且运行速度更快。
分析器
解析器根据其上下文为令牌分配含义。SQLite的解析器是使用Lemon LALR(1)解析器生成器生成的。柠檬和YACC / BISON一样工作,但它使用了不太容易出错的输入语法。柠檬还生成一个可重入且线程安全的解析器。柠檬定义了非终端析构函数的概念,以便在遇到语法错误时不会泄漏内存。驱动Lemon并定义SQLite可理解的SQL语言的语法文件可在parse.y中
找到。
因为Lemon是一个通常在开发机器上找不到的程序,Lemon的完整源代码(只有一个C文件)包含在SQLite分发的“tool”子目录中。
代码生成器
解析器将令牌组装成解析树之后,代码生成器运行以分析解析器树并生成执行SQL语句工作的字节码。准备好的语句对象是这个字节码的容器。有许多文件中的代码生成器,包括:attach.c
,auth.c
,build.c
,delete.c
,expr.c
,insert.c
,pragma.c
,select.c
,trigger.c
,update.c
,vacuum.c
,where.c
,wherecode.c
和whereexpr.c
。在这些文件中,大部分严重的魔法都是在这里发生的。expr.c
处理表达式的代码生成。其中* .c
处理SELECT,UPDATE和DELETE语句中WHERE子句的代码生成。该文件attach.c
,delete.c
,insert.c
,select.c
,trigger.c
update.c
和vacuum.c
处理代码生成具有相同名称的SQL语句。(这些文件中的每一个都根据需要调用expr.c
和where.c中的
例程。)所有其他SQL语句都由build.c
编码。该auth.c
文件实现了sqlite3_set_authorizer()的功能。
代码生成器,特别是* .c
和select.c中
的逻辑有时称为查询规划器。对于任何特定的SQL语句,可能有数百,数千或数百万种不同的算法来计算答案。查询计划员是一个AI,致力于从数百万个选择中选择最佳算法。
字节码引擎
由代码生成器创建的字节码程序由虚拟机运行。
虚拟机本身完全包含在单个源文件vdbe.c中
。所述vdbe.h
头文件定义了虚拟机和SQLite库的其余部分之间的界面vdbeInt.h
其定义了私人虚拟机本身的结构和接口。其他各种vdbe * .c
文件都是虚拟机的助手。所述vdbeaux.c
文件包含由所用的库的其余部分来构造VM程序在虚拟机和接口模块中使用的工具。该vdbeapi.c
文件包含虚拟机的外部接口,如sqlite3_bind_int()和sqlite3_step()。各个值(字符串,整数,浮点数和BLOB)存储在由vdbemem.c
实现的名为“Mem”的内部对象中。
SQLite使用C语言例程的回调来实现SQL函数。即使内置的SQL函数也是这样实现的。大多数内置的SQL函数(例如:abs(),count(),substr()等)都可以在func.c
源文件中找到。日期和时间转换函数可在date.c
中找到。一些函数如coalesce()和typeof()直接由代码生成器实现为字节码。
B树
SQLite数据库使用btree.c
源文件中的B-tree实现在磁盘上进行维护。数据库中的每个表和索引都使用单独的B树。所有B树都存储在同一个磁盘文件中。文件格式细节稳定且定义明确,并保证向前兼容。
B树子系统和SQLite库的其余部分的接口由头
文件btree.h
定义。
页面缓存
B树模块以固定大小的页面从磁盘请求信息。默认的page_size是4096字节,但可以是512到65536字节之间的任意两个幂。页面缓存负责读取,写入和缓存这些页面。页面缓存还提供了回滚和原子提交抽象,并负责锁定数据库文件。B树驱动程序请求页面缓存中的特定页面,并在需要修改页面或提交或回滚更改时通知页面缓存。页面缓存处理了确保请求被快速,安全和有效地处理的所有细节。
主页面缓存实现在pager.c
文件中。WAL模式逻辑位于单独的wal.c中
。内存中缓存由pcache.c
和pcache1.c
文件实现。页面缓存子系统和SQLite其余部分之间的接口由头
文件pager.h
定义。
OS界面
为了提供跨操作系统的可移植性,SQLite使用称为VFS的抽象对象。每个VFS都提供了打开,读取,写入和关闭磁盘上的文件以及执行其他特定于操作系统的任务(如查找当前时间或获取随机性以初始化内置伪随机数生成器的方法)。SQLite当前为unix(在os_unix.c
文件中)和Windows(在os_win.c
文件中)提供了VFSes
。
公用事业
内存分配,无格式字符串比较例程,可移植的文本到数字转换例程以及其他实用程序位于util.c中
。解析器使用的符号表由hash.c中的
哈希表维护。该utf.c
源文件包含Unicode转换子程序。SQLite的有()自己的私人执行的printf(有一些扩展)在printf.c
并在其自己的伪随机数生成器(PRNG)random.c
。
测试代码
源代码树的“src /”文件夹中名称以test
开头的文件仅用于测试,不包含在库的标准版本中。
SQLite在公共领域。