原文链接:http://www.jb51.cc/article/p-cyyznbtf-bnx.html
早期版本的 sqlite 很好学是因为他们只提供了 5 个 C/C++ 的接口。但是随着 sqlite 功能的增加,新的 C/C++ 接口加入,现在已经有超过 200 个不同的 API 了。这对新人可能是一种阻碍。幸运的是,大部分的 C/C++ 接口都是有特殊用途的,不需要了解。尽管有这么多的入口点,核心的 API 还是相当的简单而且容易使用。这篇文章旨在提供所有能使读者容易理解 sqlite 如何工作的背景信息。
一个独立的接口文档提供了 sqlite 不同的 C/C++ 接口的详细说明。一旦读者理解了操作 sqlite 的基本概念,该文档就可以成为参考手册了。这篇文章只是一个介绍,不会是 sqlite 的完整、权威的参考手册。
1.0 核心对象和接口
sql 数据库引擎的核心任务是计算 sql 语句的值(evaluate statements of sql)。为了完成这个目的,开发者需要了解两个对象:
数据库连接对象(The database connection object):sqlite3
预处理好的语句对象(The prepared statement object):sqlite3_stmt
严格来讲,对于一些封装好的便捷接口 prepared statement object不是必须的,比如sqlite3_exec还有sqlite3_get_table, 在内部求值时都会封装并隐藏这些 prepared statement object。然而,要想充分利用 sqlite,理解 prepared statements 还是有必要的。
Database connection 和 prepared statement 对象由下面列出的一组 C/C++ 接口(interface routine)控制。
上面列出的 6 个 C/C++ 接口还有两个对象组成了 sqlite 的核心功能。理解了这些的开发者在使用 sqlite 时就会很容易了。
注意上面列出的这些接口只是一类接口而不是实际的接口。这些接口都有许多不同的版本。比如,上面列出的一个叫做sqlite3_open()的接口,事实上是由 3 个完全不同的接口组成的:sqlite3_open(),sqlite3_open16()还有sqlite3_open_v2()。上面列表中提到的sqlite3_column()实际上并不存在。列表中提到的 “sqlite3_column()” 实际上是由一簇为了获取不同列类型的接口组成。
下面是核心接口的简要介绍:
sqlite3_open()这个接口打开一个到 sqlite 数据库文件的链接并返回一个数据库连接对象。这通常是应用程序调用的第一个 sqlite 的 API,并且也是其他大部分的 sqlite API 所需要的。大部分的 sqlite 接口需要一个指向 database connection object 的指针作为第一个参数,可以认为是数据库连接对象上的方法。这个接口是数据库连接对象的构造器。
sqlite3_prepare()这个接口将 sql 语句转换为一个 prepared statement 对象并返回指向这个对象的指针。这个接口需要之前使用sqlite3_open()返回的数据库连接对象,还有一个包含 sql 语句的文本字符串作为参数。这个 API 实际上并不执行 sql 语句。它仅仅只是准备用于执行的 sql 语句。把每个 sql 语句想象成一个小型的计算机程序。sqlite3_prepare()是为了把那个程序编译成对象代码(object code)。Prepared statement 就是这个对象代码。之后sqlite3_step()运行这个对象代码获取结果。
注意对于新的程序已经不推荐使用sqlite3_prepare()。新的应用推荐使用sqlite3_prepare_v2()接口。
sqlite3_step() 这个接口用于执行之前用sqlite3_prepare()创建的 prepared statement。这个接口只返回结果集的第一条结果。为了获取第二条结果,再调用一次sqlite3_step()。继续调用sqlite3_step()直到语句结束。不返回结果的语句(比如:INSERT,UPDATE,或者 DELETE 语句)调用一次sqlite3_step()就行了。
sqlite3_column()这个接口返回使用sqlite3_step()查询的 prepared statement 的结果集中当前行的某一列。每次调用完sqlite3_step()都会产生一个新的 result set row。这个接口可以被调用多次用于获取一行中的不同列。正如上面所提到的,事实上在 sqlite 的 API 中并没有 “sqlite3_column()” 这个函数。相反的,我们这里调用的 “sqlite3_column()” 是为了获取某列中不同类型值的一簇的接口占位符(a place-holder for an entire family of functions)。这类接口中有返回结果大小(如果是字符串或者 BLOB,译注:Binary Large OBject)还有结果集中列数的接口。
sqlite3_finalize()这个接口销毁之前由sqlite3_prepare()创建的 prepared statement。为了避免内存泄露,必须这个接口销毁之前创建的 prepared statement。
sqlite3_close()这个接口关闭之前由调用sqlite3_open()创建的数据库连接。在关闭数据库连接前所有与之关联的 prepared statements 都应该已经被销毁了。
1.1 核心接口和对象的使用方法
一个想要使用 sqlite 的应用程序通常在初始化时使用sqlite3_open()创建一个数据库连接。注意sqlite3_open()既可以用于打开一个现存的数据库,也可以创建并打开一个新的数据库。因为许多的应用程序只使用一个数据库连接,所以就没有理由让一个应用程序多次调用sqlite3_open()创建多个数据库连接 – 同一个数据库或者不同的数据库。有时一个多线程的应用会为不同的线程创建独立的数据库连接。注意,为了访问两个或者多个数据库也没必要创建多个独立的连接。一个单一的数据库连接可以使用ATTACHsql 语句同时访问两个或者多个数据库(译注:http://sqlite.awardspace.info/@R_404_156@/sqlitepg12.htm)。
许多应用在关闭时调用sqlite3_close()销毁它们的数据库连接。比如,一个使用 sqlite 作为它的应用程序文件格式(application file format)会在点击 文件/打开(File/Open) 菜单时创建一个数据库连接,在点击 文件/关闭 (File/Close) 菜单时关闭这个连接。
要执行 sql 语句,程序需要遵循以下几步:
上述都是为了高效的使用 sqlite 读者所需要知道的。其余的都只是补充和细节。
2.0 核心 API 的便捷封装
sqlite3_exec()是使用一次函数调用完成上述四种接口调用的便捷封装。sqlite3_exec()在处理结果集的每行时调用传入的回调函数。sqlite3_get_table()是另一个使用一次函数调用完成上述四种接口调用的便捷封装。sqlite3_get_table()与sqlite3_exec()的不同之处是将结果集保存在堆中而不是每次都调用回调函数。
需要注意的是sqlite3_exec()和sqlite3_get_table()都不能实现核心接口不能完成的事。事实上,这些封装都是完全由核心接口实现的。
3.0 参数绑定与重用 Prepared Statements
在之前的讨论中,都假设每个 sql 语句都只准备一次( prepared once),执行(evaluated),然后销毁。但是,sqlite 允许相同的 prepared statement 被执行多次。通过以下的方法实现:
sqlite3_step()执行一次或多次 prepared statement 后,通过sqlite3_reset()可以重置并重新执行(evaluated)。对现存的 prepared statement 使用sqlite3_reset()可以避免调用sqlite3_prepare()创建一个新的 prepared statement。对于部分的 sql 语句,调用sqlite3_prepare()的时间和调用sqlite3_step()一样。所以避免调用sqlite3_prepare()对性能提升有重大的影响。
通常不会对一个 sql 语句执行(evaluate)多次。更常见的是,执行一个相似的语句。例如,你想通过执行多次 INSERT 语句插入不同的值。为了实现类似的灵活性,sqlite 允许对一条 sql 语句每次“绑定”不同的值。这些值之后可以改变,并且同样的 prepared statement 使用新的值可以二次使用。(译注:说白了就是对一个表一次插入多条数据,每次插入的不同的值在 sql 语句中用通配符占位)
在 sqlite 中,任何地方都允许插入字符串字面量(string literal),可以使用以下几种形式:
在上面个的例子中,NNN 代表整型值( integer value ),AAA 是个标识符(identifier)(译注:字母数字组合)。参数的初始值为 NULL。在第一次调用sqlite3_step()之前或立即在调用sqlite3_reset()之后,应用程序可以调用某个sqlite3_bind()接口绑定值到参数上。每次调用sqlite3_bind()都会覆盖之前绑定到相同参数的值。
应用可以按需要提前准备多个 sql prepared statements 并按需要执行(evaluate)。没有严格的限制 prepared statements 的数量。
4.0 配置 sqlite
默认的 sqlite 配置对于大部分的应用工作的都挺好的。但有时开发者想要优化设置项去尝试压榨出更多的性能,或者利用一些隐蔽的特性。
sqlite3_config()可以设置 sqlite 全局的,进程级(process-wide)的配置项。sqlite3_config()只能在数据库连接建立后被调用。sqlite3_config()允许程序员做以下的事情:
在进程级的设置完成(configuration is complete)并且数据库连接已经创建后,不同的数据库连接可以通过调用sqlite3_limit()和sqlite3_db_config()配置。
5.0 扩展 sqlite
sqlite 包含可以扩展功能的接口。这些接口(routine)包括:
sqlite3_create_collation()接口用于创建排序文字用的新的校对队列(collating sequences)。sqlite3_create_module()接口用于注册新的虚表实现。sqlite3_vfs_register()创建一个新的 VFS(译注:Virtual File System,虚拟文件系统)。
sqlite3_create_function()接口用于创建新的 sql 函数(function)—— scalar 函数或者 aggregate 函数(译注:http://www.w3schools.com/sql/sql_functions.asp)。新的函数实现通常使用以下的额外接口:
所有的 sqlite 内建 sql 函数都使用的是这些接口创建的。可以参考 sqlite 的源文件,尤其是 date.c 和 func.c。
共享库或者 DLL 可以作为 sqlite 的可装卸扩展(loadable extensions)(译注:http://www.sqlite.org/cvstrac/wiki?p=LoadableExtensions)。
6.0 其他接口
这篇文章只提到了 sqlite 的一些基础接口。sqlite 库中还包含一些这里没有提到的有用的 API。可以在 sqlite 的 C/C++ 的接口说明中找到完整的函数列表(译注:http://sqlite.org/c3ref/intro.html)。参考此文档可以找到完整和权威的关于 sqlite 接口的信息。