2015-05-27
这篇文章简要的介绍了 SQLite 的 C/C++ 接口。
早期版本的 SQLite 很好学是因为他们只提供了 5 个 C/C++ 的接口。但是随着 SQLite 功能的增加,新的 C/C++ 接口加入,现在已经有超过 200 个不同的 API 了。这对新人可能是一种阻碍。幸运的是,大部分的 C/C++ 接口都是有特殊用途的,不需要了解。尽管有这么多的入口点,核心的 API 还是相当的简单而且容易使用。这篇文章旨在提供所有能使读者容易理解 SQLite 如何工作的背景信息。
一个独立的 接口文档 提供了 SQLite 不同的 C/C++ 接口的详细说明。一旦读者理解了操作 SQLite 的基本概念,该文档就可以成为参考手册了。这篇文章只是一个介绍,不会是 SQLite 的完整、权威的参考手册。
SQL 数据库引擎的核心任务是计算 SQL 语句的值(evaluate statements of SQL)。为了完成这个目的,开发者需要了解两个对象:
数据库连接对象(The database connection object): sqlite3
预处理好的语句对象(The prepared statement object): sqlite3_stmt
严格来讲,对于一些封装好的便捷接口 prepared statement object不是必须的,比如 sqlite3 exec 还有 sqlite3get_table , 在内部求值时都会封装并隐藏这些 prepared statement object。然而,要想充分利用 SQLite, 理解 prepared statements 还是有必要的。
Database connection 和 prepared statement 对象由下面列出的一组 C/C++ 接口(interface routine)控制。
sqlite3_open() sqlite3_prepare() sqlite3_step() sqlite3_column() sqlite3_finalize() sqlite3_close()
上面列出的 6 个 C/C++ 接口还有两个对象组成了 SQLite 的核心功能。理解了这些的开发者在使用 SQLite 时就会很容易了。
注意上面列出的这些接口只是一类接口而不是实际的接口。这些接口都有许多不同的版本。比如,上面列出的一个叫做 sqlite3 open() 的接口,事实上是由 3 个完全不同的接口组成的: sqlite3open() , sqlite3 open16() 还有 sqlite3open v2() 。上面列表中提到的 sqlite3column() 实际上并不存在。列表中提到的 “ sqlite3_column() ” 实际上是由一簇为了获取不同列类型的接口组成。
下面是核心接口的简要介绍:
sqlite3_open()这个接口打开一个到 SQLite 数据库文件的链接并返回一个数据库连接对象。这通常是应用程序调用的第一个 SQLite 的 API,并且也是其他大部分的 SQLite API 所需要的。大部分的 SQLite 接口需要一个指向 database connection object 的指针作为第一个参数,可以认为是数据库连接对象上的方法。这个接口是数据库连接对象的构造器。
sqlite3 prepare() 这个接口将 SQL 语句转换为一个 prepared statement 对象并返回指向这个对象的指针。这个接口需要之前使用 sqlite3open() 返回的数据库连接对象,还有一个包含 SQL 语句的文本字符串作为参数。这个 API 实际上并不执行 SQL 语句。它仅仅只是准备用于执行的 SQL 语句。把每个 SQL 语句想象成一个小型的计算机程序。 sqlite3 prepare() 是为了把那个程序编译成对象代码(object code)。Prepared statement 就是这个对象代码。之后 sqlite3step() 运行这个对象代码获取结果。
注意对于新的程序已经不推荐使用 sqlite3 prepare() 。新的应用推荐使用 sqlite3prepare_v2() 接口。
sqlite3 step() 这个接口用于执行之前用 sqlite3prepare() 创建的 prepared statement。这个接口只返回结果集的第一条结果。为了获取第二条结果,再调用一次 sqlite3 step() 。继续调用 sqlite3step() 直到语句结束。不返回结果的语句(比如:INSERT, UPDATE, 或者 DELETE 语句)调用一次 sqlite3_step() 就行了。
sqlite3 column() 这个接口返回使用 sqlite3step() *查询的 prepared statement 的结果集中当前行的某一列。每次调用完 * sqlite3 step() 都会产生一个新的 result set row。这个接口可以被调用多次用于获取一行中的不同列。正如上面所提到的,事实上在 SQLite 的 API 中并没有 " sqlite3column() " 这个函数。相反的,我们这里调用的 " sqlite3_column() " 是为了获取某列中不同类型值的一簇的接口占位符(a place-holder for an entire family of functions)。这类接口中有返回结果大小(如果是字符串或者 BLOB,译注:Binary Large OBject)还有结果集中列数的接口。
sqlite3_column_blob() sqlite3_column_bytes() sqlite3_column_bytes16() sqlite3_column_count() sqlite3_column_double() sqlite3_column_int() sqlite3_column_int64() sqlite3_column_text() sqlite3_column_text16() sqlite3_column_type() sqlite3_column_value()
sqlite3 finalize() 这个接口销毁之前由 sqlite3prepare() 创建的 prepared statement。为了避免内存泄露,必须这个接口销毁之前创建的 prepared statement。
sqlite3 close() 这个接口关闭之前由调用 sqlite3open() 创建的数据库连接。在关闭数据库连接前所有与之关联的 prepared statements 都应该已经被销毁了。
一个想要使用 SQLite 的应用程序通常在初始化时使用 sqlite3 open() 创建一个数据库连接。注意 sqlite3open() 既可以用于打开一个现存的数据库,也可以创建并打开一个新的数据库。因为许多的应用程序只使用一个数据库连接,所以就没有理由让一个应用程序多次调用 sqlite3_open() 创建多个数据库连接 - 同一个数据库或者不同的数据库。有时一个多线程的应用会为不同的线程创建独立的数据库连接。注意,为了访问两个或者多个数据库也没必要创建多个独立的连接。一个单一的数据库连接可以使用 ATTACH SQL 语句同时访问两个或者多个数据库(译注: http://sqlite.awardspace.info/syntax/sqlitepg12.htm)。
许多应用在关闭时调用 sqlite3_close() 销毁它们的数据库连接。比如,一个使用 SQLite 作为它的应用程序文件格式(application file format)会在点击 文件/打开(File/Open) 菜单时创建一个数据库连接,在点击 文件/关闭 (File/Close) 菜单时关闭这个连接。
要执行 SQL 语句,程序需要遵循以下几步:
使用 **sqlite3_prepare()** 创建一个 prepared statement。 通过一次或多次调用 **sqlite3_step()** 查询 prepared statement 的结果。 对于查询来说,在调用 **sqlite3_step()** 后通过调用 **sqlite3_column()** 获取结果。 使用 **sqlite3_finalize()** 销毁 prepared statement。
上述都是为了高效的使用 SQLite 读者所需要知道的。其余的都只是补充和细节。
sqlite3 exec() 是使用一次函数调用完成上述四种接口调用的便捷封装。 sqlite3exec() 在处理结果集的每行时调用传入的回调函数。 sqlite3 get table() 是另一个使用一次函数调用完成上述四种接口调用的便捷封装。 sqlite3 get table() 与 sqlite3_exec() 的不同之处是将结果集保存在堆中而不是每次都调用回调函数。
需要注意的是 sqlite3 exec() 和 sqlite3get_table() 都不能实现核心接口不能完成的事。事实上,这些封装都是完全由核心接口实现的。
在之前的讨论中,都假设每个 SQL 语句都只准备一次( prepared once),执行(evaluated),然后销毁。但是,SQLite 允许相同的 prepared statement 被执行多次。通过以下的方法实现:
sqlite3_reset() sqlite3_bind()
sqlite3 step() 执行一次或多次 prepared statement 后,通过 sqlite3reset() 可以重置并重新执行(evaluated)。对现存的 prepared statement 使用 sqlite3 reset() 可以避免调用 sqlite3prepare() 创建一个新的 prepared statement。对于部分的 SQL 语句,调用 sqlite3 prepare() 的时间和调用 sqlite3step() 一样。所以避免调用 sqlite3_prepare() 对性能提升有重大的影响。
通常不会对一个 SQL 语句执行(evaluate)多次。更常见的是,执行一个相似的语句。例如,你想通过执行多次 INSERT 语句插入不同的值。为了实现类似的灵活性,SQLite 允许对一条 SQL 语句每次“绑定”不同的值。这些值之后可以改变,并且同样的 prepared statement 使用新的值可以二次使用。(译注:说白了就是对一个表一次插入多条数据,每次插入的不同的值在 SQL 语句中用通配符占位)
在 SQLite 中,任何地方都允许插入字符串字面量(string literal),可以使用以下几种形式:
? ?NNN :AAA $AAA @AAA
在上面个的例子中,NNN 代表整型值( integer value ),AAA 是个标识符(identifier)(译注:字母数字组合)。参数的初始值为 NULL。在第一次调用 sqlite3 step() 之前或立即在调用 sqlite3reset() 之后,应用程序可以调用某个 sqlite3 bind() 接口绑定值到参数上。每次调用 sqlite3bind() 都会覆盖之前绑定到相同参数的值。
应用可以按需要提前准备多个 SQL prepared statements 并按需要执行(evaluate)。没有严格的限制 prepared statements 的数量。
默认的 SQLite 配置对于大部分的应用工作的都挺好的。但有时开发者想要优化设置项去尝试压榨出更多的性能,或者利用一些隐蔽的特性。
sqlite3 config() 可以设置 SQLite 全局的,进程级(process-wide)的配置项。 sqlite3config() 只能在数据库连接建立后被调用。 sqlite3_config() 允许程序员做以下的事情:
调整 SQLite 如何分配内存,包括为安全敏感(safety-critical)的实时的嵌入式系统和应用程序定义的(application-defined)内存分配器设置为可选的(alternative)内存分配器(memory allocators)。 设置一个进程级的错误日志。 指定一个应用程序定义的页缓存(page cache)。 调整 mutex 的使用,这样就可以使用多种线程模型,或者替换一个程序定义的 mutex 系统。
在进程级的设置完成(configuration is complete)并且数据库连接已经创建后,不同的数据库连接可以通过调用 sqlite3 limit() 和 sqlite3db_config() 配置。
SQLite 包含可以扩展功能的接口。这些接口(routine)包括:
sqlite3_create_collation() sqlite3_create_function() sqlite3_create_module() sqlite3_vfs_register()
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)。新的函数实现通常使用以下的额外接口:
sqlite3_aggregate_context() sqlite3_result() sqlite3_user_data() sqlite3_value()
所有的 SQLite 内建 SQL 函数都使用的是这些接口创建的。可以参考 SQLite 的源文件,尤其是 date.c 和 func.c。
共享库或者 DLL 可以作为 SQLite 的可装卸扩展(loadable extensions)(译注: http://www.sqlite.org/cvstrac/wiki?p=LoadableExtensions)。
这篇文章只提到了 SQLite 的一些基础接口。SQLite 库中还包含一些这里没有提到的有用的 API。可以在 SQLite 的 C/C++ 的接口说明中找到完整的函数列表(译注: http://sqlite.org/c3ref/intro.html)。参考此文档可以找到完整和权威的关于 SQLite 接口的信息。(