首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

The C language interface to SQLite Version 2

| 编辑注意:本文档描述了SQLite版本2,该版本在2004年被弃用并被SQLite3取代。本文档作为SQLite历史记录的一部分保留。现代程序员应该参考更多关于SQLite的最新文档,可以在本网站的其他地方找到。|

|:----|

  1. 一个指向从之前调用sqlite_open获得的sqlite结构的指针。
  1. 包含一个或多个SQL语句和/或要处理的查询的文本的空终止字符串。
  1. 指向回调函数的指针,该函数在查询结果中为每行调用一次。该参数可能为NULL,在这种情况下,将不会调用任何回调。
  1. 被转发成为回调函数的第一个参数的指针。
  1. 指向错误字符串的指针。错误消息被写入从malloc()获得的空间,并且错误字符串被指向malloced空间。调用函数负责在完成该空间时释放该空间。该参数可能为NULL,在这种情况下,错误消息不会报告给调用函数。

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

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

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

argvi == 0

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

argvi == 0

列的名称包含在第四个参数的第一个argc条目中。如果SHOW_DATATYPES编译指示打开(默认情况下它是关闭的),则第四个参数中的第二个argc条目是相应列的数据类型。

如果EMPTY_RESULT_CALLBACKS编译指示被设置为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_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这是来自sqlite_step例程的返回代码之一,它是非回调API的一部分。它表示另一行结果数据可用。SQLITE_DONE这是来自sqlite_step例程的返回代码之一,它是非回调API的一部分。

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。如果在编译期间遇到错误,则第四个参数可能会设置为NULL。

如果在编译期间遇到任何错误,则将错误消息写入从malloc获取的内存中,并使第5个参数指向该内存。如果第五个参数为NULL,则不会生成错误消息。如果第五个参数不是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参数2日至sqlite_step传达了相同的信息,通过的第四参数的第二个回调例程使用时sqlite_exec接口。除sqlite_step外,列数据类型信息始终包含在第四个参数中,而不管SHOW_DATATYPES附注是打开还是关闭。

每调用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_finalize。sqlite_finalize()过程将取消分配虚拟机使用的内存和其他资源。未能调用sqlite_finalize()将导致程序中的资源泄漏。

所述sqlite_finalize程序也返回结果代码,指示该虚拟机执行的SQL操作的成功或失败。)由sqlite_finalize(返回的值将是相同的将有相同的SQL被执行已返回sqlite_exec。返回的错误信息也是一样的。

这是可以接受的调用sqlite_finalize在虚拟机上之前sqlite_step返回SQLITE_DONE。这样做会中断正在进行的操作。部分完成的变化将被回滚,并且数据库将被恢复到其原始状态(除非替代恢复算法是使用正在执行的SQL的ON CONFLICT子句选择。)的效果是一样的,如果的回调函数sqlite_exec有返回非零。

在一个从未传过sqlite_step的虚拟机 上调用sqlite_finalize也是可以的。

3.0扩展API

只有1.0节中描述的三个核心例程才能使用SQLite。但是还有许多其他功能提供了有用的界面。这些扩展例程如下所示:

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语句中访问,或者在使用任何名称“ROWID”,“OID”或“_ROWID_”的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完成的虚拟机。在常见用法中,sqlite_changes返回最近的sqlite_exec调用或自最近的sqlite_compile以来插入,删除或修改的行数。但是,如果你有嵌套调用sqlite_exec(也就是说,如果一个sqlite_exec的回调例程调用另一个sqlite_exec),或者如果您调用sqlite_compile来创建新的虚拟机,同时还有另一个虚拟机,那么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结果由空指针表示。除常规数据外,数组的开头还有一行添加了行,其中包含结果每列的名称。

作为示例,请考虑以下查询:

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

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

nrow = 2

ncolumn = 3

result0 = "employee_name"

result1 = "login"

result2 = "host"

result3 = "dummy"

result4 = "No such user"

result5 = 0

result6 = "D. Richard Hipp"

result7 = "drh"

result8 = "zadok"

请注意,“虚拟”记录的“主机”值为NULL,因此结果[]数组在该槽中包含一个NULL指针。

如果查询的结果集为空,则默认情况下,sqlite_get_table将nrow设置为0,并将其结果参数设置为NULL。但是,如果EMPTY_RESULT_CALLBACKS编译指示为ON,那么结果参数仅被初始化为列的名称。例如,考虑具有空结果集的这个查询:

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

默认行为给出了这个结果:

nrow = 0

ncolumn = 0

result = 0

但是,如果EMPTY_RESULT_CALLBACKS编译指示为ON,则返回以下内容:

nrow = 0

ncolumn = 3

result0 = "employee_name"

result1 = "login"

result2 = "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返回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_UTF8SQLITE_ISO8859中的一个。

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

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

所述sqlite_busy_handler过程可用于注册一个忙回调以开放SQLite数据库。每当SQLite尝试访问被锁定的数据库时,都会调用busy回调。回调通常会做一些其他有用的工作,或者睡觉,以便给锁定一个清除机会。如果回调函数返回非零值,那么SQLite会再次尝试访问数据库并重复循环。如果回调函数返回0,则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_exec_printf()
  • sqlite_exec_vprintf()
  • sqlite_get_table_printf()
  • sqlite_get_table_vprintf()

实现与sqlite_execsqlite_get_table相同的查询功能。但是,不要将完整的SQL语句作为第二个参数,这四个_printf例程采用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”这样的文本,那么这个语句就可以正常工作。但是,假设用户输入一个字符串,如“你好,你好!”。生成的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)

All of the _printf() routines above are built around the following two functions:

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

所述sqlite_mprintf()例行程序的作用与标准库的sprintf()不同之处在于它的结果写入从获得的malloc存储器(),并返回一个指针malloced缓冲器。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()的第三个和第四个参数是指向要调用的例程的指针,以及作为第一个参数传递给它的void指针。

执行每个虚拟机操作所用的时间可能因许多因素而异。根据查询,1 GHz PC的典型值在每秒钟300万到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()用于创建新的聚合函数。在这两种情况下,数据库参数上的功能的应注册一个开放的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

如果在THREADSAFE预处理器宏设置为1的情况下编译SQLite,那么同时使用来自同一进程的两个或多个线程的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接口的源代码。

扫码关注腾讯云开发者

领取腾讯云代金券