1、SQLite 入门与分析(一)-简介http:/ .接下来是第一章。 1 、SQLite 介绍 自几十年前出现的商业应用程序以来,数据库就成为软件应用程序的主要组成部分。正与数据库管理系统非常关键一样,它们也变得非常庞大,并占用了相当多的系统资源,增加了管理的复杂性。随着软件应用程序逐渐模块模块化,一种新型数据库会比大型复杂的传统数据库管理系统更适应。嵌入式数据库直接在应用程序进程中运行,提供了零配置(zero-configuration)运行模式,并且资源占用非常少。 SQLite是一个开源的嵌入式关系数据库,它在2000年由D. Richard Hipp发布,它的减少应用程序管理数据的开销
2、,SQLite可移植性好,很容易使用,很小,高效而且可靠。 SQLite嵌入到使用它的应用程序中,它们共用相同的进程空间,而不是单独的一个进程。从外部看,它并不像一个RDBMS ,但在进程内部,它却是完整的,自包含的数据库引擎。 嵌入式数据库的一大好处就是在你的程序内部不需要网络配置,也不需要管理。因为客户端和服务器在同一进程空间运行。SQLite 的数据库权限只依赖于文件系统,没有用户帐户的概念。SQLite 有数据库级锁定,没有网络服务器。它需要的内存,其它开销很小,适合用于嵌入式设备。你需要做的仅仅是把它正确的编译到你的程序。 2 、架构(architecture) SQLite采用了模
3、块的设计,它由三个子系统,包括8个独立的模块构成。 2.1、接口(Interface) 接口由SQLite C API组成,也就是说不管是程序、脚本语言还是库文件,最终都是通过它与SQLite交互的( 我们通常用得较多的ODBC/JDBC最后也会转化为相应C API的调用)。 2.2、编译器(Compiler) 在编译器中,分词器(Tokenizer)和分析器(Parser)对SQL进行语法检查,然后把它转化为底层能更方便处理的分层的数据结构-语法树,然后把语法树传给代码生成器(code generator)进行处理。而代码生成器根据它生成一种针对SQLite的汇编代码,最后由虚拟机(Virt
4、ual Machine)执行。 2.3、虚拟机(Virtual Machine) 架构中最核心的部分是虚拟机,或者叫做虚拟数据库引擎(Virtual Database Engine,VDBE)。它和Java虚拟机相似,解释执行字节代码。VDBE的字节代码由128个操作码(opcodes)构成,它们主要集中在数据库操作。它的每一条指令都用来完成特定的数据库操作(比如打开一个表的游标)或者为这些操作栈空间的准备( 比如压入参数)。总之,所有的这些指令都是为了满足SQL命令的要求(关于VM,后面会做详细介绍)。 2.4、后端(Back-End) 后端由 B-树(B-tree),页缓存(page ca
5、che,pager)和操作系统接口(即系统调用)构成。B-tree和page cache共同对数据进行管理。B-tree的主要功能就是索引,它维护着各个页面之间的复杂的关系,便于快速找到所需数据。而pager的主要作用就是通过OS接口在B-tree和Disk之间传递页面。3、SQLite的特点(SQLites Features and Philosophy) 3.1、零配置(Zero Configuration) 3.2、可移植(Portability): 它是运行在Windows,Linux,BSD,Mac OS X和一些商用Unix 系统,比如Sun的Solaris,IBM的AIX,同样,
6、它也可以工作在许多嵌入式操作系统下,比如QNX,VxWorks,Palm OS, Symbin和Windows CE。 3.3、Compactness: SQLite是被设计成轻量级,自包含的。one header file, one library, and youre relational, no external database server required 3.4、简单(Simplicity) 3.5、灵活(Flexibility)3.6、可靠(Reliability): SQLite的核心大约有 3万行标准C 代码,这些代码都是模块化的,很容易阅读。SQLite 入门与分析(二)
7、-设计与概念写在前面:谢谢各位的关注,没想到会有这么多人关注。高兴的同时,也感到压力,因为我接触SQLite也就几天,也没在实际开发中用过,只是最近项目的需求才来研究它,所以我很担心自己的文章是否会有错误,误导别人。但是我很想把自己的学习成果与大家分享,所以如果大家觉得我有不对的地方,望不吝赐教。 我原打算直接从VDBE入手的,因为它起着承上启下的作用,是整个SQLite 的核心,并分析源码,但考虑到这是一个系列的文章,我希望能把问题说全,所以还是从基本概念入手,对于初学者,如果没有这些概念,是很继续下去的。好了,下面开始第二章,由于这一章内容很多,我将分两部分讨论,下面开始第一部分。 1、
8、API 由两部分组成: 核心API(core API) 和扩展API(extension API) 核心API 的函数实现基本的数据库操作:连接数据库,处理SQL,遍历结果集。它也包括一些实用函数,比如字符串转换,操作控制,调试和错误处理。 扩展API 通过创建你自定义的SQL 函数去扩展 SQLite。 1.1、SQLite Version 3的一些新特点: (1)SQLite的API全部重新设计,由第二版的15 个函数增加到88个函数。这些函数包括支持 UTF-8和UTF-16编码的功能函数。 (2)改进并发性能。加锁子系统引进一种锁升级模型(lock escalation model),
9、解决了第二版的写进程饿死的问题(该问题是任何一个DBMS必须面对的问题)。这种模型保证写进程按照先来先服务的算法得到排斥锁(Exclusive Lock)。甚至,写进程通过把结果写入临时缓冲区(Temporary Buffer),可以在得到排斥锁之前就能开始工作。这对于写要求较高的应用,性能可提高400%(引自参考文献)。 (3)改进的B-树。对于表采用B+树,大大提高查询效率。 (4)SQLite 3最重要的改变是它的存储模型。由第二版只支持文本模型,扩展到支持5种本地数据类型。 总之,SQLite Version 3与SQLite Vertion 2有很大的不同,在灵活性,特点和性能方面有
10、很大的改进。1.2、主要的数据结构(The Principal Data Structures) SQLite由很多部分组成parser,tokenize,virtual machine等等。但是从程序员的角度,最需要知道的是:connection, statements, B-tree和pager 。它们之间的关系如下: 上图告诉我们在编程需要知道的三个主要方面:API,事务(Transaction) 和锁(Locks) 。从技术上来说,B-tree和pager不是API 的一部分。但是它们却在事务和锁上起着关键作用(稍后将讨论)。 1.3、Connections 和Statements C
11、onnection和statement 是执行SQL命令涉及的两个主要数据结构,几乎所有通过API进行的操作都要用到它们。一个连接(Connection)代表在一个独立的事务环境下的一个连接A (connection represents a single connection to a database as well as a single transaction context)。每一个statement都和一个connection关联,它通常表示一个编译过的SQL语句,在内部,它以VDBE字节码表示。Statement 包括执行一个命令所需要一切,包括保存VDBE程序执行状态所需的资源
12、,指向硬盘记录的B-树游标,以及参数等等。 1.4、 B-tree和pager 一个connection可以有多个 database对象- 一个主要的数据库以及附加的数据库,每一个数据库对象有一个B-tree对象,一个B-tree有一个pager对象(这里的对象不是面向对象的“对象 ”,只是为了说清楚问题)。Statement最终都是通过connection的B-tree和pager从数据库读或者写数据,通过B-tree的游标(cursor)遍历存储在页面(page)中的记录。游标在访问页面之前要把数所从disk加载到内存,而这就是pager的任务。任何时候,如果B-tree需要页面,它都会请
13、求pager从disk读取数据,然后把页面(page) 加载到页面缓冲区(page cache),之后,B-tree和与之关联的游标就可以访问位于page 中的记录了。 如果cursor 改变了page,为了防止事务回滚, pager必须采取特殊的方式保存原来的page。总的来说,pager负责读写数据库,管理内存缓存和页面(page),以及管理事务,锁和崩溃恢复 (这些在事务一节会详细介绍)。 总之,关于connection和transaction,你必须知道两件事: (1) 对数据库的任何操作,一个连接存在于一个事务下。(2) 一个连接决不会同时存在多个事务下。 whenever a co
14、nnection does anything with a database, it always operates under exactly one transaction, no more, no less. 1.5、核心API 核心API 主要与执行SQL命令有关,本质上有两种方法执行SQL语句:prepared query 和wrapped query。Prepared query由三个阶段构成:preparation,execution和finalization。其实wrapped query只是对prepared query的三个过程包装而已,最终也会转化为prepared qu
15、ery的执行。 1.5.1、连接的生命周期(The Connection Lifecycle) 和大多数据库连接相同,由三个过程构成: (1 ) 连接数据库(Connect to the database): 每一个SQLite数据库都存储在单独的操作系统文件中,连接,打开数据库的C API为:sqlite3_open(),它的实现位于main.c文件中,如下:1. int sqlite3_open(const char *zFilename, sqlite3 *ppDb)2. 3. return openDatabase(zFilename, ppDb, SQLITE_OPEN_READWR
16、ITE | SQLITE_OPEN_CREATE, 0);4. 复制代码当连接一个在磁盘上的数据库,如果数据库文件存在,SQLite打开一个文件;如果不存在,SQLite会假定你想创建一个新的数据库。在这种情况下,SQLite不会立即在磁盘上创建一个文件,只有当你向数据库写入数据时才会创建文件,比如:创建表、视图或者其它数据库对象。如果你打开一个数据,不做任何事,然后关闭它,SQLite会创建一个文件,只是一个空文件而已。 另外一个不立即创建一个新文件的原因是,一些数据库的参数,比如:编码,页面大小等,只在在数据库创建前设置。默认情况下,页面大小为1024字节,但是你可以选择512-32768
17、 字节之间为 2幂数的数字。有些时候,较大的页面能更有效的处理大量的数据。 (2 ) 执行事务(Perform transactions): all commands are executed within transactions。默认情况下,事务自动提交,也就是每一个SQL语句都在一个独立的事务下运行。当然也可以通过使用BEGINCOMMIT手动提交事务。 (3 ) 断开连接(Disconnect from the database): 主要是关闭数据库的文件。 1.5.2、执行Prepared Query 前面提到,预处理查询(Prepared Query)是SQLite执行所有SQL命
18、令的方式,包括以下三个过程: (1) Prepared Query: 分析器(parser),分词器(tokenizer)和代码生成器(code generator)把SQL Statement编译成 VDBE字节码,编译器会创建一个statement句柄(sqlite3_stmt),它包括字节码以及其它执行命令和遍历结果集的所有资源。 相应的C API为sqlite3_prepare(),位于prepare.c 文件中,如下:1. int sqlite3_prepare(2. sqlite3 *db, /* Database handle. */3. const char *zSql, /*
19、 UTF-8 encoded SQL statement. */4. int nBytes, /* Length of zSql in bytes. */5. sqlite3_stmt *ppStmt, /* OUT: A pointer to the prepared statement */6. const char *pzTail /* OUT: End of parsed string */7. )8. int rc;9. rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,ppStmt,pzTail);10. assert( rc=SQLITE_O
20、K | ppStmt=0 | *ppStmt=0 ); /* VERIFY: F13021 */11. return rc;12. 复制代码(2) Execution: 虚拟机执行字节码,执行过程是一个步进(stepwise)的过程,每一步(step)由sqlite3_step()启动,并由VDBE执行一段字节码。由sqlite3_prepare 编译字节代码,并由sqlite3_step() 启动虚拟机执行。在遍历结果集的过程中,它返回SQLITE_ROW,当到达结果末尾时,返回SQLITE_DONE 。 (3) Finalization: VDBE关闭statement,释放资源。相应的C
21、 API为sqlite3_finalize()。 通过下图可以更容易理解该过程: 最后以一个具体的例子结束本节,下节讨论事务。1. #include 2. #include 3. #include “sqlite3.h“4. 5. #include 6. 7. int main(int argc, char *argv)8. 9. int rc, i, ncols;10. sqlite3 *db;11. sqlite3_stmt *stmt;12. char *sql;13. const char *tail;14. /打开数据15. rc = sqlite3_open(“foods.db“,
22、 16. 17. if(rc) 18. fprintf(stderr, “Cant open database: %sn“, sqlite3_errmsg(db);19. sqlite3_close(db);20. exit(1);21. 22. 23. sql = “select * from episodes“;24. /预处理25. rc = sqlite3_prepare(db, sql, (int)strlen(sql), 26. 27. if(rc != SQLITE_OK) 28. fprintf(stderr, “SQL error: %sn“, sqlite3_errmsg(
23、db);29. 30. 31. rc = sqlite3_step(stmt);32. ncols = sqlite3_column_count(stmt);33. 34. while(rc = SQLITE_ROW) 35. 36. for(i=0; i 。Opcode为一定功能的操作码,为了理解,可以看成一个函数。P1是32位的有符号整数,p2是31 位的无符号整数,它通常是导致跳转(jump)的指令的目标地址(destination ),当然这了有其它用途;p3为一个以null结尾的字符串或者其它结构体的指针。和C API不同的是,VDBE操作码经常变化,所以不应该用字节码写程序。 下面
24、的几个C API直接和VDBE交互: sqlite3_bind_xxx() functions sqlite3_step() sqlite3_reset() sqlite3_column_xxx() functions sqlite3_finalize() 为了有个感性,下面看一个具体的字节码程序:sqlite .m col sqlite .h on sqlite .w 4 15 3 3 15 sqlite explain select * from episodes; addr opcode p1 p2 p3 - - - - - 0 Goto 0 12 1 Integer 0 0 2 Ope
25、nRead 0 2 # episodes 3 SetNumColumns 0 3 4 Rewind 0 10 5 Recno 0 0 6 Column 0 1 7 Column 0 2 8 Callback 3 0 9 Next 0 5 10 Close 0 0 11 Halt 0 0 12 Transaction 0 0 13 VerifyCookie 0 10 14 Goto 0 1 15 Noop 0 0 1.1、 栈(Stack) 一个VDBE程序通常由不同完成特定任务的段(section)构成,每一个段中,都有一些操作栈的指令。这是由于不同的指令有不同个数的参数,一些指令只有一个参数
26、;一些指令没有参数;一些指令有好几个参数,这种情况下,三个操作数就不能满足。 考虑到这些情况,指令采用栈来传递参数。(注:从汇编的角度来看,传递参数的方式有好几种,比如:寄存器,全局变量,而堆栈是现代语言常用的方式,它具有很大的灵活性)。而这些指令不会自己做这些事情,所以在它们之前,需要其它一些指令的帮助。VDBE把计算的中间结果保存到内存单元(memory cells)中,其实,堆栈和内存单元都是基于Mem(见vdbeInt.h)数据结构(注:这里的栈,内存单元都是虚拟的,记得一位计算机科学家说过:计算机科学中90%以上的科学都是虚拟化问题。一点不假,OS本质上也是虚拟机,而在这里SQLit
27、e ,我们也处处可见虚拟化的身影,到后面的OS Interface模块中再仔细讨论这个问题)。 1.2、程序体(Program Body) 这是一个打开episodes表的过程。 第一条指令:Integer 是为第二条指令作准备的,也就是把第二条指令执行需要的参数压入堆栈,OpenRead从堆栈中取出参数值然后执行。SQLite可以通过ATTACH命令在一个连接中打开多个数据库文件,每当SQLite打开一个数据,它就为之赋一个索引号(index),main database的索引为0,第一个数据库为 1,依次如此。Integer指令数据库索引的值压入栈,而OpenRead从中取出值,并决定打开
28、哪个数据,来看看SQLite文档中的解释: Open a read-only cursor for the database table whose root page is P2 in a database file. The database file is determined by an integer from the top of the stack. 0 means the main database and 1 means the database used for temporary tables. Give the new cursor an identifier of P
29、1. The P1 values need not be contiguous but all P1 values should be small integers. It is an error for P1 to be negative. If P2=0 then take the root page number from off of the stack. There will be a read lock on the database whenever there is an open cursor. If the data- base was unlocked prior to
30、this instruction then a read lock is acquired as part of this instruction. A read lock allows other processes to read the database but prohibits any other process from modifying the database. The read lock is released when all cursors are closed. If this instruction attempts to get a read lock but f
31、ails, the script terminates with an SQLITE_BUSY error code. The P3 value is a pointer to a KeyInfo structure that defines the content and collating sequence of indices. P3 is NULL for cursors that are not pointing to indices. 再来看看SetNumColumns指令设置游标将指向的列。P1 为游标的索引(这里为0,刚刚打开), P2为列的数目,episodes表有三列。 继
32、续Rewind 指令,它将游标重新设置到表的开始,它会检查表是否为空(即没有记录),如果没有记录,它会导致指令指针跳到P2指定的指令处。在这里,P2为10,即Close指令。一旦Rewind设置游标,接下就执行5-9这几条指令,它们的主要功能是遍历结果集,Recno把由游标P1指定的记录的关键字压入堆栈。 Column指令从由P1指定的游标,P2指定的列取值。5,6,7三条指令分别把id(primary key),season和name 字段的值压入栈。接下来,Callback指令从栈中取出三个值(P1),然后形成一个记录数组,存储在内存单元中(memory cell)。Callback会停止
33、VDBE 的操作,把控制权交给sqlite3_stemp(),该函数返回SQLITE_ROW。 一旦VDBE创建了记录结构,我们就可以通过sqlite3_column_xxx() functions从记录结构的域内取出值。当下次调用sqlite3_step() 时,指令指针会指向Next指令,而 Next指令会把游标向移向下一行,如果有其它的记录,它会跳到由P2指定的指令,在这里为指令5,创建一个新的记录结构,一直循环,直到结果集的最后。Close指令会关闭游标,然后执行Halt指令,结束 VDBE程序。 1.3、程序开始与停止 现在来看看其余的指令,Goto指令是一条跳转指令,跳到P2 处,
34、即第12条指令。指令12是Transaction,它开始一个新的事务;然后执行VerifyCookie,它的主要功能 VDBE程序编译后,数据库模式是否改变(即是否进行过更新操作)。这在SQLite中是一个很重要的概念,在SQL被sqlite3_prepare() 编译成VDBE 代码至程序调用sqlite3_step()执行字节码的这段时间,另一个 SQL命令可能会改变数据库模式(such as ALTER TABLE, DROP TABLE, or CREATE TABLE)。一旦发生这种情况,之前编译的statement就会变得无效,数据库模式信息记录在数据库文件的根页面中。类似,每一个
35、statement都有一份用来比较的在编译时刻该模式的备份,VerifyCookie的功能就是检查它们是否匹配,如果不匹配,将采取相关操作。 如果两者匹配,会执行下一条指令Goto;它会跳到程序的主要部分,即第一条指令,打开表读取记录。这里有两点值得注意: (1)Transaction指令自己不会获取锁( The Transaction instruction doesnt acquire any locks in itself)。它的功能相当于BEGIN,而实际是由OpenRead 指令获取share lock的。当事务关闭时释放锁,这取决于Halt指令,它会进行扫尾工作。 (2)state
36、ment对象(VDBE程序)所需的存储空间在程序执行前就已经确定。这有原于两个重要事实:首先,栈的深度不会比指令的数目还多(通常少得多)。其次,在执行VDBE程序之前,SQLite 可以计算出为分配资源所需要的内存。 1.4指令的类型(Instruction Types) 每条指令都完成特定的任务,而且通常和别的指令有关。大体上来说,指令可分为三类: (1)Value manipulation:这些指令通常完成算术运算,比如:add, subtract, divide;逻辑运算,比如: AND和OR;还有字符串操作。 (2)Data management:这些指令操作在内存和磁盘上的数据。内存
37、指令进行栈操作或者在内存单元之间传递数据。磁盘操作指令控制B-tree和pager打开或操作游标,开始或结束事务,等等。 (3)Control flow:控制指令主要是移动指令指针。 1.5、程序的执行(Program execution) 最后我们来看VM解释器是如何实现以及字节代码大致是如何执行的。在vdbe.c文件中有一个很关键的函数:1. /执行VDBE程序2. int sqlite3VdbeExec(3. Vdbe *p /* The VDBE */4. )复制代码该函数是执行VDBE程序的入口。来看看它的内部实现:1. /*从这里开始执行指令2. *pc为程序计数器 (int)3.
38、 */4. for(pc=p-pc; rc=SQLITE_OK; pc+)5. /取得操作码6. pOp = 7. switch( pOp-opcode )8. case OP_Goto: /* jump */9. CHECK_FOR_INTERRUPT;10. pc = pOp-p2 - 1;11. break;12. 13. 14. 15. 复制代码从这段代码,我们大致可以推出VM执行的原理:VM解释器实际上是一个包含大量switch语句的for循环,每一个switch语句实现一个特定的操作指令。写在前面:本节是前一节( SQLite入门与分析(三)-内核概述(1))内容的后续部分,这两节
39、都是从全局的角度SQLite内核各个模块的设计和功能。只有从全局上把握SQLite,才会更容易的理解SQLite 的实现。SQLite采用了层次化,模块化的设计,而这些使得它的可扩展性和可移植性非常强。而且SQLite的架构与通用DBMS的结构差别不是很大,所以它对于理解通用DBMS具有重要意义。好了,下面我们开始讨论SQLite剩余的两部分:Back-end(后端) 和compiler(编译器) 。 2、 B-tree和Pager B-Tree使得VDBE可以在O(logN)下查询,插入和删除数据,以及O(1) 下双向遍历结果集。B-Tree不会直接读写磁盘,它仅仅维护着页面(pages)之
40、间的关系。当B-TREE需要页面或者修改页面时,它就会调用Pager。当修改页面时,pager保证原始页面首先写入日志文件,当它完成写操作时,pager根据事务状态决定如何做。B-tree不直接读写文件,而是通过page cache这个缓冲模块读写文件对于性能是有重要意义的(注:这和操作系统读写文件类似,在Linux中,操作系统的上层模块并不直接调用设备驱动读写设备,而是通过一个高速缓冲模块调用设备驱动读写文件,并将结果存到高速缓冲区)。 2.1、数据库文件格式(Database File Format) 数据库中所有的页面都按从 1开始顺序标记。一个数据库由许多B-tree构成每一个表和索引
41、都有一个B-tree(注:索引采用B-tree,而表采用B+tree,这主要是表和索引的需求不同以及B-tree和B+tree的结构不同决定的:B+tree的所有叶子节点包含了全部关键字信息,而且可以有两种顺序查找具体参见数据结构,严蔚敏。而B-tree更适合用来作索引)。所有表和索引的根页面都存储在sqlite_master表中。 数据库中第一个页面(page 1)有点特殊,page 1的前100个字节包含一个描述数据库文件的特殊的文件头。它包括库的版本,模式的版本,页面大小,编码等所有创建数据库时设置的参数。这个特殊的文件头的内容在btree.c中定义,page 1也是sqlite_mas
42、ter表的根页面。 2.1、页面重用及回收(Page Reuse and Vacuum ) SQLite利用一个空闲列表(free list)进行页面回收。当一个页面的所有记录都被删除时,就被插入到该列表。当运行VACUUM命令时,会清除free list,所以数据库会缩小,本质上它是在新的文件重新建立数据库,而所有使用的页在都被拷贝过去,而free list却不会,结果就是一个新的,变小的数据库。当数据库的autovacuum开启时,SQLite不会使用free list,而且在每一次commit时自动压缩数据库。 2.2、B-Tree记录 B-tree中页面由B-tree记录组成,也叫做p
43、ayloads。每一个 B-tree记录,或者payload有两个域:关键字域(key field)和数据域(data field)。Key field就是ROWID的值,或者数据库中表的关键字的值。从B-tree的角度,data field可以是任何无结构的数据。数据库的记录就保存在这些data fields中。B-tree的任务就是排序和遍历,它最需要就是关键字。Payloads的大小是不定的,这与内部的关键字和数据域有关,当一个payload太大不能存在一个页面内进便保存到多个页面。B+Tree按关键字排序,所有的关键字必须唯一。表采用B+tree,内部页面不包含数据,如下: B+tre
44、e中根页面(root page)和内部页面(internal pages)都是用来导航的,这些页面的数据域都是指向下级页面的指针,仅仅包含关键字。所有的数据库记录都存储在叶子页面(leaf pages)内。在叶节点一级,记录和页面都是按照关键字的顺序的,所以B-tree可以水平方向遍历,时间复杂度为O(1)。 2.3、记录和域(Records and Fields) 位于叶节点页面的数据域的记录由VDBE管理,数据库记录以二进制的形式存储,但有一定的数据格式。记录格式包括一个逻辑头(logical header)和一个数据区(data segment),header segment包括head
45、er的大小和一个数据类型数组,数据类型用来在data segment的数据的类型,如下: 2.4、层次数据组织(Hierarchical Data Organization) 从上往下,数据越来越无序,从下向上,数据越来越结构化. 2.5、B-Tree API B-Tree模块有它自己的API ,它可以独立于C API使用。另一个特点就是它支持事务。由pager处理的事务,锁和日志都是为B-tree服务的。根据功能可以分为以下几类: 2.5.1 、访问和事务函数 sqlite3BtreeOpen: Opens a new database file. Returns a B-tree obje
46、ct. sqlite3BtreeClose: Closes a database. sqlite3BtreeBeginTrans: Starts a new transaction. sqlite3BtreeCommit: Commits the current transaction. sqlite3BtreeRollback: Rolls back the current transaction. sqlite3BtreeBeginStmt: Starts a statement transaction. sqlite3BtreeCommitStmt: Commits a statemen
47、t transaction. sqlite3BtreeRollbackStmt: Rolls back a statement transaction. 2.5.2、表函数 sqlite3BtreeCreateTable: Creates a new, empty B-tree in a database file. sqlite3BtreeDropTable: Destroys a B-tree in a database file. sqlite3BtreeClearTable: Removes all data from a B-tree, but keeps the B-tree in
48、tact. 2.5.3、游标函数(Cursor Functions) sqlite3BtreeCursor: Creates a new cursor pointing to a particular B-tree. sqlite3BtreeCloseCursor: Closes the B-tree cursor. sqlite3BtreeFirst: Moves the cursor to the first element in a B-tree. sqlite3BtreeLast: Moves the cursor to the last element in a B-tree. sqlite3BtreeNext: Moves the cursor to the next element after the