编者注: 本文档描述了 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_内部

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

SQLITE_错误

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

SQLITE_PERM

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

SQLITE_ABORT

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

SQLITE_BUSY

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

SQLITE_LOCKED

此返回码与 SQLITE_BUSY 类似,表示数据库已锁定。但是锁的来源是对sqlite_exec的递归调用。仅当您尝试从先前调用 sqlite_exec 的查询的回调例程中调用 sqlite_exec 时,才会发生此返回。允许对 sqlite_exec 的递归调用,只要它们不尝试写入同一个表。

SQLITE_NOMEM

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

SQLITE_READONLY

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

SQLITE_INTERRUPT

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

SQLITE_IOERR

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

SQLITE_损坏

如果 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_误用

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

SQLITE_NOLFS

此错误意味着您尝试在缺少大文件支持的旧版 Unix 机器上创建或访问大于 2GB 的文件数据库文件。

SQLITE_AUTH

此错误表明授权方回调不允许您尝试执行的 SQL。

SQLITE_ROW

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

SQLITE_DONE

这是 sqlite_step例程的返回码之一,它是非回调 API 的一部分。它表明 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,每行输出一次,最后在 SQL 执行完毕后调用sqlite_finalize 进行清理。

2.1 将SQL语句编译到虚拟机中

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

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

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

如果第二个参数实际上包含两条或多条SQL语句,则只编译第一条语句。(这与sqlite_exec执行其输入字符串中的所有 SQL 语句的行为不同。) sqlite_compile的第三个参数 指向输入中第一个 SQL 语句末尾之后的第一个字符。如果第二个参数只包含一个 SQL 语句,那么第三个参数将指向第二个参数末尾的 '\000' 终止符。

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

2.2 SQL语句的逐步执行

在使用sqlite_compile 生成虚拟机后 ,它通过一次或多次调用sqlite_step来执行。sqlite_step的每次调用(最后一次除外)都会返回一行结果。结果中的列数存储在第二个参数指向的整数中。第三个参数指定的指针指向列值的指针数组。第 4 个参数中的指针指向列名和数据类型的指针数组。使用sqlite_exec时, sqlite_step的第 2 到第 4 个参数传达与回调例程的第 2 到第 4 个参数相同的信息 界面。除了,对于sqlite_step ,列数据类型信息总是包含在第四个参数中,无论 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。行数据存储在一个字符串指针数组中,第二个参数指向这个数组。

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

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

如果您通过尝试不恰当地调用sqlite_step来滥用该库, 它将尝试返回 SQLITE_MISUSE。如果您同时从两个或多个线程在同一虚拟机上调用 sqlite_step(),或者在它返回 SQLITE_DONE 或 SQLITE_ERROR 后再次调用 sqlite_step(),或者如果您将无效的虚拟机指针传递给 sqlite_step( ). 您不应依赖 SQLITE_MISUSE 返回码来指示错误。界面的滥用可能不会被发现并导致程序崩溃。SQLITE_MISUSE 仅用作调试辅助 - 帮助您在发生事故之前检测不正确的使用。不保证滥用检测逻辑在所有情况下都有效。

2.3 删除虚拟机

sqlite_compile创建 的每个虚拟机最终都应该交给sqlite_finalizesqlite_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 列,则该键是一个唯一的整数。一行的键可以在 SELECT 语句中访问,或者在 WHERE 或 ORDER BY 子句中使用任何名称“ROWID”、“OID”或“_ROWID_”。

当您向没有 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"

请注意,“虚拟”记录的“主机”值为 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是安全的,例如如果结果集为空则返回。

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返回真,则调用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_UTF8SQLITE_ISO8859之一。

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

3.8 改变图书馆对锁定文件的反应

sqlite_busy_handler过程可用于向打开的 SQLite 数据库注册忙回调每当 SQLite 试图访问一个被锁定的数据库时,busy 回调就会被调用。回调通常会做一些其他有用的工作,或者可能会休眠,以便给锁一个清除的机会。如果回调返回非零值,则 SQLite 会再次尝试访问数据库并重复该循环。如果回调返回零,则 SQLite 中止当前操作并返回 SQLITE_BUSY。

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

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

3.9 使用_printf()包装函数

四个效用函数

实现与sqlite_execsqlite_get_table相同的查询功能但是,四个_printf 例程并没有将完整的 SQL 语句作为它们的第二个参数,而是采用了 printf 样式的格式字符串。要执行的 SQL 语句是从这个格式字符串和附加到函数调用末尾的任何附加参数生成的。

使用 SQLite printf 函数而不是sprintf有两个优点首先,使用 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 ()是同一例程的可变参数版本。这些例程返回的字符串指针应该通过将其传递给sqlite_freemem()来释放。

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

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

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

执行每个虚拟机操作所花费的时间可能因许多因素而异。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 函数的更多信息和示例,请查看文件 func.c中的 SQLite 源代码。

5.0 多线程和 SQLite

如果 SQLite 是在 THREADSAFE 预处理器宏设置为 1 的情况下编译的,那么同时从同一进程的两个或多个线程使用 SQLite 是安全的。但是每个线程都应该有自己的从sqlite_open返回的sqlite* 指针两个或多个线程同时访问同一个sqlite*指针 是不安全的。

在网站上提供的预编译 SQLite 库中,Unix 版本是在关闭 THREADSAFE 的情况下编译的,而 Windows 版本是在打开 THREADSAFE 的情况下编译的。如果您需要与此不同的东西,则必须重新编译。

在 Unix 下,不应 将sqlite*指针通过fork()系统调用携带到子进程中。子进程应该在fork()之后打开自己的数据库副本

6.0 使用示例

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