小巧、快速、可靠。
三者任选其二。

编者注:本文档描述了 SQLite 版本 2,该版本已于 2004 年被弃用并由 SQLite3 取代。本文档作为 SQLite 历史记录的一部分保留。现代程序员应参考本网站其他地方提供的关于 SQLite 的最新文档。

SQLite 版本 2 的 C 语言接口

SQLite 库的设计目的是非常易于从 C 或 C++ 程序中使用。本文档概述了 C/C++ 编程接口。

1.0 核心 API

SQLite 库的接口包含三个核心函数、一个不透明数据结构和一些用作返回值的常量。核心接口如下

typedef struct sqlite sqlite;
#define SQLITE_OK           0   /* Successful result */

sqlite *sqlite_open(const char *dbname, int mode, char **errmsg);

void sqlite_close(sqlite *db);

int sqlite_exec(
  sqlite *db,
  char *sql,
  int (*xCallback)(void*,int,char**,char**),
  void *pArg,
  char **errmsg
);

以上是您在 C 或 C++ 程序中使用 SQLite 所需了解的全部内容。还有其他接口函数可用(并在下面描述),但我们将首先描述上面显示的核心函数。

1.1 打开数据库

使用 sqlite_open 函数打开现有 SQLite 数据库或创建新的 SQLite 数据库。第一个参数是数据库名称。第二个参数旨在指示数据库将用于读写还是仅用于读取。但在当前实现中,sqlite_open 的第二个参数被忽略。第三个参数是指向字符串指针的指针。如果第三个参数不为 NULL,并且在尝试打开数据库时发生错误,则会将错误消息写入从 malloc() 获取的内存,并将 *errmsg 设置为指向此错误消息。调用函数负责在完成使用后释放内存。

SQLite 数据库的名称是包含数据库的文件的名称。如果文件不存在,SQLite 会尝试创建并初始化它。如果文件是只读的(由于权限位或因为它位于 CD-ROM 等只读介质上),则 SQLite 会以只读方式打开数据库。整个 SQL 数据库存储在磁盘上的单个文件中。但为了存储数据库回滚日志或查询的临时和中间结果,在执行 SQL 命令期间可能会创建额外的临时文件。

sqlite_open 函数的返回值是指向不透明 sqlite 结构的指针。此指针将成为所有后续处理同一数据库的 SQLite 函数调用的第一个参数。如果打开失败,则返回 NULL。

1.2 关闭数据库

要关闭 SQLite 数据库,请调用 sqlite_close 函数,并将从先前调用 sqlite_open 获取的 sqlite 结构指针传递给它。如果在关闭数据库时事务处于活动状态,则会回滚事务。

1.3 执行 SQL 语句

sqlite_exec 函数用于处理 SQL 语句和查询。此函数需要 5 个参数,如下所示

  1. 指向从先前调用 sqlite_open 获取的 sqlite 结构的指针。

  2. 包含一个或多个要处理的 SQL 语句和/或查询文本的以零结尾的字符串。

  3. 指向回调函数的指针,该函数会为查询结果中的每一行调用一次。此参数可以为 NULL,在这种情况下永远不会调用回调函数。

  4. 转发成为回调函数第一个参数的指针。

  5. 指向错误字符串的指针。错误消息将写入从 malloc() 获取的空间,并且错误字符串将设置为指向已分配的空间。调用函数负责在完成使用后释放此空间。此参数可以为 NULL,在这种情况下不会将错误消息报告回调用函数。

回调函数用于接收查询的结果。回调函数的原型如下所示

int Callback(void *pArg, int argc, char **argv, char **columnNames){
  return 0;
}

回调函数的第一个参数只是 sqlite_exec 的第四个参数的副本。此参数可用于将任意信息从客户端代码传递给回调函数。第二个参数是查询结果中的列数。第三个参数是字符串指针数组,其中每个字符串都是该记录结果的单个列。请注意,回调函数将数据库中的 NULL 值报告为 NULL 指针,这与空字符串有很大不同。如果第 i 个参数为空字符串,我们将得到

argv[i][0] == 0

但如果第 i 个参数为 NULL,我们将得到

argv[i] == 0

列的名称包含在第四个参数的前 argc 个条目中。如果 SHOW_DATATYPES pragma 处于开启状态(默认情况下处于关闭状态),则第四个参数中的第二个 argc 个条目是相应列的数据类型。

如果 EMPTY_RESULT_CALLBACKS pragma 设置为 ON,并且查询的结果为空集,则会调用回调函数一次,并将第三个参数 (argv) 设置为 0。换句话说

argv == 0
第二个参数 (argc) 和第四个参数 (columnNames) 仍然有效,如果存在结果,则可以用于确定结果列的数量和名称。默认行为是在结果集为空时根本不调用回调函数。

回调函数通常应返回 0。如果回调函数返回非零值,则会立即中止查询,sqlite_exec 将返回 SQLITE_ABORT。

1.4 错误代码

sqlite_exec 函数通常返回 SQLITE_OK。但如果出现问题,它可能会返回不同的值以指示错误类型。以下是返回值的完整列表

#define SQLITE_OK           0   /* Successful result */
#define SQLITE_ERROR        1   /* SQL error or missing database */
#define SQLITE_INTERNAL     2   /* An internal logic error in SQLite */
#define SQLITE_PERM         3   /* Access permission denied */
#define SQLITE_ABORT        4   /* Callback routine requested an abort */
#define SQLITE_BUSY         5   /* The database file is locked */
#define SQLITE_LOCKED       6   /* A table in the database is locked */
#define SQLITE_NOMEM        7   /* A malloc() failed */
#define SQLITE_READONLY     8   /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite_interrupt() */
#define SQLITE_IOERR       10   /* Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT     11   /* The database disk image is malformed */
#define SQLITE_NOTFOUND    12   /* (Internal Only) Table or record not found */
#define SQLITE_FULL        13   /* Insertion failed because database is full */
#define SQLITE_CANTOPEN    14   /* Unable to open the database file */
#define SQLITE_PROTOCOL    15   /* Database lock protocol error */
#define SQLITE_EMPTY       16   /* (Internal Only) Database table is empty */
#define SQLITE_SCHEMA      17   /* The database schema changed */
#define SQLITE_TOOBIG      18   /* Too much data for one row of a table */
#define SQLITE_CONSTRAINT  19   /* Abort due to constraint violation */
#define SQLITE_MISMATCH    20   /* Data type mismatch */
#define SQLITE_MISUSE      21   /* Library used incorrectly */
#define SQLITE_NOLFS       22   /* Uses OS features not supported on host */
#define SQLITE_AUTH        23   /* Authorization denied */
#define SQLITE_ROW         100  /* sqlite_step() has another row ready */
#define SQLITE_DONE        101  /* sqlite_step() has finished executing */

这些不同返回值的含义如下

SQLITE_OK

如果一切正常且没有错误,则返回此值。

SQLITE_INTERNAL

此值表示 SQLite 库内的内部一致性检查失败。这只有在 SQLite 库中存在错误时才会发生。如果您从 sqlite_exec 调用中获得 SQLITE_INTERNAL 回复,请在 SQLite 邮件列表中报告该问题。

SQLITE_ERROR

此返回值表示传递给 sqlite_exec 的 SQL 语句中存在错误。

SQLITE_PERM

此返回值表示数据库文件的访问权限使得无法打开该文件。

SQLITE_ABORT

如果回调函数返回非零值,则返回此值。

SQLITE_BUSY

此返回代码表示另一个程序或线程已锁定数据库。SQLite 允许两个或多个线程同时读取数据库,但一次只能有一个线程将数据库打开以写入。SQLite 中的锁定是对整个数据库进行的。

SQLITE_LOCKED

此返回代码类似于 SQLITE_BUSY,因为它表示数据库已锁定。但锁定的来源是对 sqlite_exec 的递归调用。此返回值只有在您尝试从先前调用 sqlite_exec 的查询的回调例程中调用 sqlite_exec 时才会出现。只要递归调用 sqlite_exec 不尝试写入同一表,就允许递归调用 sqlite_exec。

SQLITE_NOMEM

如果对 malloc 的调用失败,则返回此值。

SQLITE_READONLY

此返回代码表示已尝试写入以只读方式打开的数据库文件。

SQLITE_INTERRUPT

如果对 sqlite_interrupt 的调用中断了正在进行的数据库操作,则返回此值。

SQLITE_IOERR

如果操作系统告知 SQLite 无法执行某些磁盘 I/O 操作,则返回此值。这可能意味着磁盘上没有剩余空间。

SQLITE_CORRUPT

如果 SQLite 检测到它正在使用的数据库已损坏,则返回此值。损坏可能是由于恶意进程写入数据库文件造成的,也可能是由于 SQLite 中先前未检测到的逻辑错误造成的。如果磁盘 I/O 错误以使 SQLite 被迫将数据库文件置于损坏状态的方式发生,也会返回此值。后者应该只发生在硬件或操作系统故障的情况下。

SQLITE_FULL

如果插入失败,因为磁盘上没有剩余空间,或者数据库太大,无法容纳更多信息,则返回此值。后者只应该发生在大小超过 2GB 的数据库中。

SQLITE_CANTOPEN

如果由于某种原因无法打开数据库文件,则返回此值。

SQLITE_PROTOCOL

如果某些其他进程正在处理文件锁,并且违反了 SQLite 在其回滚日志文件上使用的文件锁定协议,则返回此值。

SQLITE_SCHEMA

当数据库首次打开时,SQLite 会将数据库模式读入内存,并使用该模式来解析新的 SQL 语句。如果另一个进程更改了模式,则当前正在处理的命令将中止,因为生成的虚拟机代码假设了旧的模式。这是此类情况的返回代码。重试命令通常会解决问题。

SQLITE_TOOBIG

SQLite 不会在单个表的单个行中存储超过大约 1 兆字节的数据。如果您尝试在单个行中存储超过 1 兆字节的数据,则会收到此返回代码。

SQLITE_CONSTRAINT

如果 SQL 语句违反了数据库约束,则返回此常量。

SQLITE_MISMATCH

当尝试将非整数数据插入标记为 INTEGER PRIMARY KEY 的列时,会发生此错误。对于大多数列,SQLite 会忽略数据类型,并允许存储任何类型的数据。但 INTEGER PRIMARY KEY 列只能存储整数数据。

SQLITE_MISUSE

如果错误使用了一个或多个 SQLite API 例程,则可能会发生此错误。错误使用示例包括在使用 sqlite_close 关闭数据库后调用 sqlite_exec,或者从两个独立的线程同时使用相同的数据库指针调用 sqlite_exec

SQLITE_NOLFS

此错误表示您尝试创建或访问在缺少大文件支持的旧版 Unix 机器上大小超过 2GB 的文件数据库文件。

SQLITE_AUTH

此错误表示授权者回调已拒绝您尝试执行的 SQL 语句。

SQLITE_ROW

这是非回调 API 部分的 sqlite_step 例程的返回代码之一。它表示有另一行结果数据可用。

SQLITE_DONE

这是非回调 API 部分的 sqlite_step 例程的返回代码之一。它表示 SQL 语句已完全执行,并且可以调用 sqlite_finalize 例程进行清理。

2.0 不使用回调函数访问数据

上面描述的 sqlite_exec 例程曾经是检索 SQLite 数据库数据的唯一方法。但许多程序员发现使用回调函数获取结果很不方便。因此,从 SQLite 版本 2.7.7 开始,提供了一个不使用回调的第二个访问接口。

新接口使用三个单独的函数来代替单个 sqlite_exec 函数。

typedef struct sqlite_vm sqlite_vm;

int sqlite_compile(
  sqlite *db,              /* The open database */
  const char *zSql,        /* SQL statement to be compiled */
  const char **pzTail,     /* OUT: uncompiled tail of zSql */
  sqlite_vm **ppVm,        /* OUT: the virtual machine to execute zSql */
  char **pzErrmsg          /* OUT: Error message. */
);

int sqlite_step(
  sqlite_vm *pVm,          /* The virtual machine to execute */
  int *pN,                 /* OUT: Number of columns in result */
  const char ***pazValue,  /* OUT: Column data */
  const char ***pazColName /* OUT: Column names and datatypes */
);

int sqlite_finalize(
  sqlite_vm *pVm,          /* The virtual machine to be finalized */
  char **pzErrMsg          /* OUT: Error message */
);

策略是使用 sqlite_compile 编译单个 SQL 语句,然后多次调用 sqlite_step(每次调用对应于一行输出),最后调用 sqlite_finalize 来清理 SQL 完成执行后的内容。

2.1 将 SQL 语句编译成虚拟机

sqlite_compile 将单个 SQL 语句(由第二个参数指定)“编译”并生成能够执行该语句的虚拟机。与大多数接口例程一样,第一个参数必须是指向从先前调用 sqlite_open 获取的 sqlite 结构的指针。

指向虚拟机的指针存储在一个指针中,该指针作为第 4 个参数传入。用于保存虚拟机的空间是动态分配的。为了避免内存泄漏,调用函数在完成对虚拟机的操作后必须调用 **sqlite_finalize**。如果在编译过程中遇到错误,则第 4 个参数可以设置为 NULL。

如果在编译过程中遇到任何错误,错误消息将写入从 **malloc** 获得的内存中,并且第 5 个参数将指向该内存。如果第 5 个参数为 NULL,则不会生成错误消息。如果第 5 个参数不为 NULL,则调用函数应通过调用 **sqlite_freemem** 来释放包含错误消息的内存。

如果第 2 个参数实际上包含两个或多个 SQL 语句,则仅编译第一个语句。(这与 **sqlite_exec** 的行为不同,**sqlite_exec** 会执行其输入字符串中的所有 SQL 语句。)**sqlite_compile** 的第 3 个参数将指向输入中第一个 SQL 语句结尾后的第一个字符。如果第 2 个参数仅包含单个 SQL 语句,则第 3 个参数将指向第 2 个参数末尾的 '\000' 终止符。

成功时,**sqlite_compile** 返回 SQLITE_OK。否则返回错误代码。

2.2 SQL 语句的逐步执行

使用 **sqlite_compile** 生成虚拟机后,可以通过一次或多次调用 **sqlite_step** 来执行它。每次调用 **sqlite_step**(除了最后一次调用)都返回结果集的一行。结果集中的列数存储在第 2 个参数指向的整数中。第 3 个参数指定的指针将指向一个指向列值指针的数组。第 4 个参数中的指针将指向一个指向列名和数据类型的指针数组。**sqlite_step** 的第 2 到第 4 个参数与使用 **sqlite_exec** 接口时 **callback** 例程的第 2 到第 4 个参数传递相同的信息。不同的是,使用 **sqlite_step** 时,列数据类型信息始终包含在第 4 个参数中,无论 SHOW_DATATYPES pragma 是开启还是关闭。

每次调用 **sqlite_step** 都返回一个整数代码,指示该步骤期间发生了什么。此代码可能是 SQLITE_BUSY、SQLITE_ROW、SQLITE_DONE、SQLITE_ERROR 或 SQLITE_MISUSE。

如果虚拟机无法打开数据库文件,因为它被另一个线程或进程锁定,**sqlite_step** 将返回 SQLITE_BUSY。调用函数应该执行一些其他操作,或者睡眠一小段时间,让锁有机会解除,然后再次调用 **sqlite_step**。这可以重复任意次数。

只要有另一行结果数据可用,**sqlite_step** 就会返回 SQLITE_ROW。行数据存储在一个指向字符串的指针数组中,第 2 个参数将指向该数组。

当所有处理完成后,**sqlite_step** 将返回 SQLITE_DONE 或 SQLITE_ERROR。SQLITE_DONE 表示语句成功完成,SQLITE_ERROR 表示发生了运行时错误。(错误详细信息从 **sqlite_finalize** 获得。)在 **sqlite_step** 返回 SQLITE_DONE 或 SQLITE_ERROR 后尝试再次调用 **sqlite_step** 是对库的错误使用。

当 **sqlite_step** 返回 SQLITE_DONE 或 SQLITE_ERROR 时,*pN 和 *pazColName 值将设置为结果集中的列数和列名,就像它们是用于 SQLITE_ROW 返回一样。这允许调用代码即使结果集为空也能找到结果列的数量以及列名和数据类型。*pazValue 参数在返回代码为 SQLITE_DONE 或 SQLITE_ERROR 时始终设置为 NULL。如果正在执行的 SQL 语句是不返回结果的语句(例如 INSERT 或 UPDATE),则 *pN 将被设置为 0,*pazColName 将被设置为 NULL。

如果你通过尝试不恰当地调用 **sqlite_step** 来滥用库,它将尝试返回 SQLITE_MISUSE。这可能发生在你在两个或多个线程中同时对同一个虚拟机调用 sqlite_step(),或者在 sqlite_step() 返回 SQLITE_DONE 或 SQLITE_ERROR 后再次调用 sqlite_step(),或者将无效的虚拟机指针传递给 sqlite_step() 时。你不应该依赖 SQLITE_MISUSE 返回代码来指示错误。接口的错误使用可能会被忽略,导致程序崩溃。SQLITE_MISUSE 仅作为调试辅助工具 - 帮助你在发生意外之前检测错误用法。错误检测逻辑不能保证在所有情况下都能正常工作。

2.3 删除虚拟机

每个由 **sqlite_compile** 创建的虚拟机最终都应该被传递给 **sqlite_finalize**。sqlite_finalize() 过程会释放虚拟机使用的内存和其他资源。不调用 sqlite_finalize() 将导致程序中出现资源泄漏。

**sqlite_finalize** 例程还会返回指示 SQL 操作成功或失败的结果代码。sqlite_finalize() 返回的值与使用 **sqlite_exec** 执行相同 SQL 时返回的值相同。返回的错误消息也将相同。

在 **sqlite_step** 返回 SQLITE_DONE 之前,可以对虚拟机调用 **sqlite_finalize**。这样做会中断正在进行的操作。部分完成的更改将回滚,数据库将恢复到其原始状态(除非使用正在执行的 SQL 中的 ON CONFLICT 子句选择了备用恢复算法)。效果与 **sqlite_exec** 的回调函数返回非零值相同。

也可以对从未传递给 **sqlite_step** 的虚拟机调用 **sqlite_finalize**。

3.0 扩展 API

要使用 SQLite,只需要第 1.0 节中描述的三个核心例程。但还有许多其他函数提供有用的接口。这些扩展例程如下

int sqlite_last_insert_rowid(sqlite*);

int sqlite_changes(sqlite*);

int sqlite_get_table(
  sqlite*,
  char *sql,
  char ***result,
  int *nrow,
  int *ncolumn,
  char **errmsg
);

void sqlite_free_table(char**);

void sqlite_interrupt(sqlite*);

int sqlite_complete(const char *sql);

void sqlite_busy_handler(sqlite*, int (*)(void*,const char*,int), void*);

void sqlite_busy_timeout(sqlite*, int ms);

const char sqlite_version[];

const char sqlite_encoding[];

int sqlite_exec_printf(
  sqlite*,
  char *sql,
  int (*)(void*,int,char**,char**),
  void*,
  char **errmsg,
  ...
);

int sqlite_exec_vprintf(
  sqlite*,
  char *sql,
  int (*)(void*,int,char**,char**),
  void*,
  char **errmsg,
  va_list
);

int sqlite_get_table_printf(
  sqlite*,
  char *sql,
  char ***result,
  int *nrow,
  int *ncolumn,
  char **errmsg,
  ...
);

int sqlite_get_table_vprintf(
  sqlite*,
  char *sql,
  char ***result,
  int *nrow,
  int *ncolumn,
  char **errmsg,
  va_list
);

char *sqlite_mprintf(const char *zFormat, ...);

char *sqlite_vmprintf(const char *zFormat, va_list);

void sqlite_freemem(char*);

void sqlite_progress_handler(sqlite*, int, int (*)(void*), void*);

所有上述定义都包含在源代码树中提供的 "sqlite.h" 头文件中。

3.1 最近插入的 ROWID

SQLite 表的每一行都有一个唯一的整数键。如果表有一个名为 INTEGER PRIMARY KEY 的列,则该列用作键。如果没有 INTEGER PRIMARY KEY 列,则键是一个唯一的整数。可以使用 "ROWID"、"OID" 或 "_ROWID_" 中的任何名称在 SELECT 语句中访问行的键,或者在 WHERE 或 ORDER BY 子句中使用它。

当你插入一个没有 INTEGER PRIMARY KEY 列的表,或者表有 INTEGER PRIMARY KEY 但该列的值未在插入的 VALUES 子句中指定时,键将自动生成。可以使用 **sqlite_last_insert_rowid** API 函数查找最近 INSERT 语句的键值。

3.2 更改的行数

**sqlite_changes** API 函数返回自数据库上次处于静默状态以来插入、删除或修改的行数。"静默" 数据库是指没有未完成的 **sqlite_exec** 调用,也没有由 **sqlite_compile** 创建的尚未被 **sqlite_finalize** 终结的 VM 的数据库。在一般情况下,**sqlite_changes** 返回最近的 **sqlite_exec** 调用插入、删除或修改的行数,或者自最近的 **sqlite_compile** 调用以来插入、删除或修改的行数。但如果你嵌套调用 **sqlite_exec**(即一个 **sqlite_exec** 的回调例程调用另一个 **sqlite_exec**),或者在你调用 **sqlite_compile** 创建一个新的 VM 时,仍然存在另一个 VM,则 **sqlite_changes** 返回的数字的含义会更加复杂。报告的数字包括任何后来被 ROLLBACK 或 ABORT 取消的更改。但由于 DROP TABLE 而删除的行 *不会* 被计算在内。

SQLite 通过删除表然后重新创建表来实现 "**DELETE FROM table**" 命令(没有 WHERE 子句)。这比逐个删除表的元素快得多。但这也意味着,无论表中最初有多少元素,**sqlite_changes** 返回的值都将为零。如果需要准确统计被删除的元素数量,请使用 "**DELETE FROM table WHERE 1**" 代替。

3.3 查询到从 malloc() 获得的内存

**sqlite_get_table** 函数是对 **sqlite_exec** 的一个包装器,它会收集来自连续回调的所有信息,并将它们写入从 malloc() 获得的内存中。这是一个方便函数,允许应用程序通过单个函数调用获取数据库查询的完整结果。

**sqlite_get_table** 的主要结果是一个指向字符串的指针数组。该数组中每个结果集的每一列都包含一个元素。NULL 结果由 NULL 指针表示。除了常规数据之外,数组开头还增加了一行,包含结果集每一列的名称。

例如,考虑以下查询

SELECT employee_name, login, host FROM users WHERE login LIKE 'd%';

此查询将返回每个以字母 "d" 开头的登录名的员工的姓名、登录名和主机名。如果此查询提交给 **sqlite_get_table**,则结果可能如下所示

nrow = 2
ncolumn = 3
result[0] = "employee_name"
result[1] = "login"
result[2] = "host"
result[3] = "dummy"
result[4] = "No such user"
result[5] = 0
result[6] = "D. Richard Hipp"
result[7] = "drh"
result[8] = "zadok"

请注意,"dummy" 记录的 "host" 值为 NULL,因此 result[] 数组在该插槽中包含一个 NULL 指针。

如果查询的结果集为空,则默认情况下 **sqlite_get_table** 将 nrow 设置为 0,并将其结果参数设置为 NULL。但如果 EMPTY_RESULT_CALLBACKS pragma 为 ON,则结果参数将初始化为仅包含列名。例如,考虑以下查询,它的结果集为空

SELECT employee_name, login, host FROM users WHERE employee_name IS NULL;

默认行为给出以下结果

nrow = 0
ncolumn = 0
result = 0

但如果 EMPTY_RESULT_CALLBACKS pragma 为 ON,则返回以下结果

nrow = 0
ncolumn = 3
result[0] = "employee_name"
result[1] = "login"
result[2] = "host"

用于保存 **sqlite_get_table** 返回信息的内存是从 malloc() 获得的。但调用函数不应尝试直接释放此信息。相反,当不再需要表时,将完整的表传递给 **sqlite_free_table**。可以使用 NULL 指针调用 **sqlite_free_table**,例如,如果结果集为空,则返回 NULL 指针。

**sqlite_get_table** 例程返回与 **sqlite_exec** 相同的整数结果代码。

3.4 中断 SQLite 操作

可以从另一个线程或信号处理程序调用 **sqlite_interrupt** 函数,以便在第一个机会时使当前数据库操作退出。当这种情况发生时,启动数据库操作的 **sqlite_exec** 例程(或等效例程)将返回 SQLITE_INTERRUPT。

3.5 测试完整的 SQL 语句

SQLite 的下一个接口例程是一个方便的函数,用于测试字符串是否构成完整的 SQL 语句。如果 **sqlite_complete** 函数在其输入为字符串时返回 true,则参数构成完整的 SQL 语句。虽然不能保证该语句的语法正确,但至少我们知道语句是完整的。如果 **sqlite_complete** 返回 false,则需要更多文本才能完成 SQL 语句。

为了 **sqlite_complete** 函数的目的,如果 SQL 语句以分号结尾,则该语句是完整的。

**sqlite** 命令行工具使用 **sqlite_complete** 函数来确定何时需要调用 **sqlite_exec**。在接收到每行输入后,**sqlite** 会对缓冲区中的所有输入调用 **sqlite_complete**。如果 **sqlite_complete** 返回 true,则调用 **sqlite_exec** 并重置输入缓冲区。如果 **sqlite_complete** 返回 false,则提示符将更改为延续提示符,并读取另一行文本并将其添加到输入缓冲区。

3.6 库版本字符串

SQLite 库导出名为 **sqlite_version** 的字符串常量,其中包含库的版本号。头文件包含一个具有相同信息的宏 SQLITE_VERSION。如果需要,程序可以将 SQLITE_VERSION 宏与 **sqlite_version** 字符串常量进行比较,以验证头文件和库的版本号是否匹配。

3.7 库字符编码

默认情况下,SQLite 假设所有数据都使用固定大小的 8 位字符 (iso8859)。但是,如果您在配置脚本中提供 --enable-utf8 选项,则库将假设 UTF-8 可变大小字符。这会影响 LIKE 和 GLOB 运算符以及 LENGTH() 和 SUBSTR() 函数。静态字符串 **sqlite_encoding** 将设置为 "UTF-8" 或 "iso8859",以指示库的编译方式。此外,**sqlite.h** 头文件将根据需要定义 **SQLITE_UTF8** 或 **SQLITE_ISO8859** 宏之一。

请注意,SQLite 使用的字符编码机制不能在运行时更改。这只是一个编译时选项。**sqlite_encoding** 字符串只是告诉您库是如何编译的。

3.8 更改库对锁定文件的响应

**sqlite_busy_handler** 过程可用于向打开的 SQLite 数据库注册繁忙回调。每当 SQLite 尝试访问已锁定的数据库时,都会调用繁忙回调。回调通常会执行一些其他有用的操作,或者休眠,以便让锁有机会清除。如果回调返回非零值,则 SQLite 再次尝试访问数据库,循环重复。如果回调返回零,则 SQLite 将中止当前操作并返回 SQLITE_BUSY。

**sqlite_busy_handler** 的参数是 **sqlite_open** 返回的不透明结构、指向繁忙回调函数的指针以及将在繁忙回调中作为第一个参数传递的泛型指针。当 SQLite 调用繁忙回调时,它会向其发送三个参数:作为 **sqlite_busy_handler** 的第三个参数传入的泛型指针、库尝试访问的数据库表或索引的名称以及库尝试访问数据库表或索引的次数。

对于我们希望繁忙回调休眠的常见情况,SQLite 库提供了一个方便的例程 **sqlite_busy_timeout**。**sqlite_busy_timeout** 的第一个参数是指向打开的 SQLite 数据库的指针,第二个参数是毫秒数。执行 **sqlite_busy_timeout** 后,SQLite 库将至少等待指定毫秒数的时间来清除锁,然后返回 SQLITE_BUSY。将超时指定为零毫秒将恢复默认行为。

3.9 使用_printf()包装函数

四个实用函数

实现与 **sqlite_exec** 和 **sqlite_get_table** 相同的查询功能。但它们不以完整的 SQL 语句作为其第二个参数,而是以 printf 样式的格式字符串作为参数。要执行的 SQL 语句是从此格式字符串和附加到函数调用末尾的任何其他参数生成的。

与 **sprintf** 相比,使用 SQLite printf 函数有两个优点。首先,使用 SQLite printf 例程时,永远不会像使用 **sprintf** 那样有溢出静态缓冲区的危险。SQLite printf 例程会自动分配(并随后释放)保存生成的 SQL 语句所需的尽可能多的内存。

SQLite printf 例程相对于 **sprintf** 的第二个优点是两个专门用于支持 SQL 中字符串文字的新格式选项。在格式字符串中,%q 格式选项与 %s 非常相似,因为它从参数列表中读取一个以 null 结尾的字符串并将其插入结果中。但 %q 通过将插入的字符串中每个单引号 (') 字符复制两次来转换插入的字符串。这样就相当于在字符串文字中转义单引号的字符串结束含义。%Q 格式选项的工作方式类似;它像 %q 那样转换单引号,并且还会将结果字符串括在单引号中。如果 %Q 格式选项的参数是 NULL 指针,则结果字符串将是 NULL,没有单引号。

考虑一个例子。假设您正在尝试将一个字符串值插入到数据库表中,该字符串值是从用户输入中获取的。假设要插入的字符串存储在一个名为 zString 的变量中。用于执行插入的代码可能如下所示

sqlite_exec_printf(db,
  "INSERT INTO table1 VALUES('%s')",
  0, 0, 0, zString);

如果 zString 变量包含像 "Hello" 这样的文本,那么此语句将正常工作。但是,假设用户输入的字符串是 "Hi y'all!"。生成的 SQL 语句如下所示

INSERT INTO table1 VALUES('Hi y'all')

由于 "y'all" 中的撇号,这不是有效的 SQL。但如果使用 %q 格式选项而不是 %s,如下所示

sqlite_exec_printf(db,
  "INSERT INTO table1 VALUES('%q')",
  0, 0, 0, zString);

那么生成的 SQL 将如下所示

INSERT INTO table1 VALUES('Hi y''all')

这里撇号已转义,SQL 语句格式正确。当从可能包含单引号字符 (') 的数据中动态生成 SQL 时,始终建议使用 SQLite printf 例程和 %q 格式选项而不是 **sprintf**。

如果使用 %Q 格式选项而不是 %q,如下所示

sqlite_exec_printf(db,
  "INSERT INTO table1 VALUES(%Q)",
  0, 0, 0, zString);

那么生成的 SQL 将如下所示

INSERT INTO table1 VALUES('Hi y''all')

如果 zString 变量的值为 NULL,则生成的 SQL 将如下所示

INSERT INTO table1 VALUES(NULL)

上面所有 _printf() 例程都是围绕以下两个函数构建的

char *sqlite_mprintf(const char *zFormat, ...);
char *sqlite_vmprintf(const char *zFormat, va_list);

**sqlite_mprintf()** 例程的工作方式类似于标准库 **sprintf()**,只是它将结果写入从 malloc() 获取的内存中,并返回指向已分配内存缓冲区的指针。**sqlite_mprintf()** 也理解上面描述的 %q 和 %Q 扩展。**sqlite_vmprintf()** 是同一个例程的 varargs 版本。这些例程返回的字符串指针应通过将其传递给 **sqlite_freemem()** 来释放。

3.10 在大型查询期间执行后台作业

**sqlite_progress_handler()** 例程可用于向 SQLite 数据库注册回调例程,以便在长时间运行的 **sqlite_exec()**、**sqlite_step()** 和各种包装函数调用期间定期调用该回调例程。

回调每 N 个虚拟机操作调用一次,其中 N 是作为 **sqlite_progress_handler()** 的第二个参数提供的。**sqlite_progress_handler()** 的第三个和第四个参数是指向要调用的例程的指针以及作为第一个参数传递给它的 void 指针。

执行每个虚拟机操作所需的时间会根据许多因素而异。对于 1 GHz PC,典型值在每秒 50 万到 300 万之间,但可能会更高或更低,具体取决于查询。因此,很难根据虚拟机操作来安排后台操作。相反,建议相对频繁地安排回调(例如每 1000 个指令),并使用外部计时器例程来确定是否需要运行后台作业。

4.0 添加新的 SQL 函数

从版本 2.4.0 开始,SQLite 允许使用作为 C 代码实现的新函数扩展 SQL 语言。使用以下接口

typedef struct sqlite_func sqlite_func;

int sqlite_create_function(
  sqlite *db,
  const char *zName,
  int nArg,
  void (*xFunc)(sqlite_func*,int,const char**),
  void *pUserData
);
int sqlite_create_aggregate(
  sqlite *db,
  const char *zName,
  int nArg,
  void (*xStep)(sqlite_func*,int,const char**),
  void (*xFinalize)(sqlite_func*),
  void *pUserData
);

char *sqlite_set_result_string(sqlite_func*,const char*,int);
void sqlite_set_result_int(sqlite_func*,int);
void sqlite_set_result_double(sqlite_func*,double);
void sqlite_set_result_error(sqlite_func*,const char*,int);

void *sqlite_user_data(sqlite_func*);
void *sqlite_aggregate_context(sqlite_func*, int nBytes);
int sqlite_aggregate_count(sqlite_func*);

**sqlite_create_function()** 接口用于创建常规函数,**sqlite_create_aggregate()** 用于创建新的聚合函数。在这两种情况下,**db** 参数都是一个开放的 SQLite 数据库,函数应该在其上注册,**zName** 是新函数的名称,**nArg** 是参数的个数,**pUserData** 是一个指针,它会原封不动地传递给函数的 C 实现。这两个例程在成功时返回 0,如果出现任何错误,则返回非零值。

函数名称的长度不能超过 255 个字符。任何尝试创建名称长度超过 255 个字符的函数都会导致错误。

对于常规函数,**xFunc** 回调会为每个函数调用调用一次。xFunc 的实现应调用 **sqlite_set_result_...** 接口之一来返回其结果。**sqlite_user_data()** 例程可用于检索在注册函数时传入的 **pUserData** 指针。

对于聚合函数,**xStep** 回调会为结果中的每一行调用一次,然后在结束时调用 **xFinalize** 来计算最终答案。xStep 例程可以使用 **sqlite_aggregate_context()** 接口来分配特定于该 SQL 函数特定实例的内存。此内存将在调用 xFinalize 后自动删除。**sqlite_aggregate_count()** 例程可用于找出传递给聚合的数据行数。xFinalize 回调应调用 **sqlite_set_result_...** 接口之一来设置聚合的最终结果。

SQLite 现在使用此接口实现其所有内置函数。有关如何创建新的 SQL 函数的更多信息和示例,请查看 SQLite 源代码文件 **func.c** 中的 SQLite 源代码。

5.0 多线程和 SQLite

如果 SQLite 使用 THREADSAFE 预处理器宏编译为 1,则可以安全地从同一进程的两个或多个线程同时使用 SQLite。但每个线程应该拥有自己的 **sqlite*** 指针,该指针从 **sqlite_open** 返回。两个或多个线程绝不能同时访问相同的 **sqlite*** 指针。

在网站上提供的预编译 SQLite 库中,Unix 版本使用 THREADSAFE 关闭编译,而 Windows 版本使用 THREADSAFE 启用编译。如果您需要与之不同的内容,您将需要重新编译。

在 Unix 下,**sqlite*** 指针不应跨越 **fork()** 系统调用传递到子进程中。子进程应在 **fork()** 后打开其自己的数据库副本。

6.0 使用示例

有关如何使用 SQLite C/C++ 接口的示例,请参阅源代码树中文件 src/shell.c 中 **sqlite** 程序的源代码。有关 sqlite 的更多信息,请访问 cli.html。另请参阅 SQLite Tcl 接口的源代码,该接口位于源文件 src/tclsqlite.c 中。