SQLite3 有八个函数用于实际处理连接、处理查询以及断开数据库连接的。其余都是为了完成特定的任务
通过封装查询对数据库进行连接、断开、以及查询。
1.连接和断开
执行SQL命令之前,首先要连接数据库(打开数据库),断开连接时候断开数据库。
打开数据库函数:sqlite_open_v2()、sqlite3_open()、sqlite3_open16()。
函数:int sqlite_open_v2(const char *filename, sqlite3 **ppdb, int flag, const char *zvfs); 参数:filename:数据库名,如果为null,则创建一个临时文件,断开后就删除 ppdb:SQLite数据库句柄,代表数据库的连接。 flag:标志:SQLITE_OPEN_READONLY:只读模式打开数据库,文件不存在则返回错误。 SQLITE_OPEN_READWRITE:读写模式打开数据库,文件不存在则返回错误。 SQLITE_OPEN_CREAT:创建数据库。 zvfs:要使用vfs模块的名称 函数:int 、sqlite3_open(const char *filename, sqlite3 **ppdb); 参数:filename:数据库名,如果为null,则创建一个临时文件,断开后就删除 ppdb:SQLite数据库句柄 函数:int sqlite_open_v2(const char *filename, sqlite3 **ppdb); 参数:filename:数据库名,如果为null,则创建一个临时文件,断开后就删除 ppdb:SQLite数据库句柄
关闭数据库函数:sqlite3_close()
函数:int 、sqlite3_close(sqlite3 *ppdb); 参数:ppdb:SQLite数据库句柄
执行sqlite3_close函数必须要完成与连接关联的所有操作,如果有一个未完成,则返回SQLITE_BUSY。无法关闭连接。
2.执行查询
执行查询函数:sqlite3_exec()
函数:int sqlite3_exec(sqlite3 *ppdb, const char *sql, sqlite_callback, void *data, char **errmsg); 参数:ppdb:打开的数据库 sql:sql语句,可以包含多条SQL命令 sqlite_callback:回调函数 data:回调函数的第一个参数 errmsg:指向错误消息字符串的指针,发生错误时,将错误信息写入该字符串中。
demo:
int main() { sqlite3 *db = NULL; int rc; char *sql = NULL; rc = sqlite3_open("test.db", &db); if (rc != SQLITE_OK) { printf("[%s:%d] open database failed,rc=%d\n", __func__, __LINE__, rc); } sql = "create table stu(id int, name text)"; rc = sqlite3_exec(db, sql, NULL, NULL, NULL); if (rc != SQLITE_OK) { printf("[%s:%d] sqlite3 exec:%d\n", __func__, __LINE__, rc); sqlite3_close(db); } sqlite3_close(db); return 0; }
通过参数sqlite_callback和data实现了对sqlite3_exec获得select语句结果的回调机制,回调函数申明如下:
typedef int (*sqlite3_callback) (void*, int, char**, char**); 参数:void*:sqlite3_exec()函数第四个参数提供的数据 int:行中字段数目 char**:代表行中字段名称的字符串数组 char**:代表字段名称的字符串数组 返回值:当返回非0的时候,sqlite3_exec()函数就会终止。
3.获取表查询
函数sqlite3_get_table()函数返回一个命令的所有结果集,sqlite3_get_table函数封装了sqlite3_exec函数并尽可能地返回数据,从而更容易获取数据。
函数: int sqlite3_get_table(sqlite3 *db, const char *sql, char ***resultp, int*nrow, int *ncolumn, char **errmsg); 参数:db:数据库句柄 sql:SQL语句 resultp:用来指向sql执行结果的指针 nrow:满足条件的记录的数目(行) ncolumn:每条记录包含的字段数目(列) errmsg:错误信息指针的地址 返回值:成功返回0,失败返回错误码
此函数接收sql命令返回的所有记录,并且存储在 resultp中, resultp使用sqlite3_malloc()申请内存,必须使用sqlite3_free_table()释放内存。
二、准备查询
封装函数将所有步骤封装在一个函数中调用使得某些情况下运行命令会很方便,每个查询函数都提供了自己的行和列的获取方式,但是如果方法封装的越多,那么对执行和结果控制的越少,因此,准备查询提供了更多的功能,更好的控制和更多的信息。
准备查询使用了特殊的函数来访问记录字段和列的信息,列值可以使用函数sqlite3_column_xxx()获取,xxx表示返回值的类型(int、double、blob、char)。因此可以根据自己的需要来检索数据。
在实际应用中,我们通常使用sqlite3_exec()函数对数据库进行修改(创建、丢弃、修改、删除、插入)。而准备查询则用于select语句。因为它提供更多的信息,而且是线性编程,以及使用游标遍历,可以更好地控制结果集。
准备查询执行有三个步骤:编译、执行、完成。使用sqlite3_prepare_v2()或者sqlite3_prepare()函数编译查询,sqlite3_step()函数分布执行查询,使用函数sqlite3_finalize()关闭查询,可以使用sqlite3_reset()函数重用编译。
1.编译
编译或准备SQL语句,并将其编译为虚拟数据库引擎(VDBE)可读的字节码,由函数sqlite3_prepare_v2()或者sqlite3_prepare()完成。
函数:sqlite3_prepare_v2(sqlite3 *db, const char *sql, int nbytes, sqlite3_stmt **ppstmt, const char **pztail) 参数:db:数据库句柄 sql:SQL语句文本,UTF-8编码 nbytes:sql语句的长度 ppstmt:为sqlite3_step()执行的编译好的准备语句的指针 pztail:Sql在遇见终止符或者是达到设定的nByte之后结束,假如zSql还有剩余的内容,那么这些剩余的内容被存放到pZTail中,不包括终止符。可以填NULL。
sqlite3_prepare_v2编译sql字符串地第一条SQL语句,分配执行该语句所需的所有资源,并将其字节代码关联到单个语句句柄,由参数ppstmt指定,参数ppstmt是sqlite3_stmt结构体。该数据结构包含了命令的字节码、绑定的参数,B-Tree游标、执行上下文以及sqlite3_step()在执行过程中管理查询状态所需的其他数据。
此函数不会对连接和数据库产生影响,也不会启动事务获取锁,它可以直接通过编译器工作,为执行准备查询。
2.执行
一旦查询语句准备就绪,下一步就是用sqlite3_setp()执行,该函数接收语句句柄并直接与VDBE通信,生成执行SQL语句的字节码指令,第一次调用sqlite3_setp()时候,VDBE获得执行该命令所需的必要的数据库锁,如果不能获得锁,并且木有指派繁忙处理程序,则返回SQLITE_BUSY。如果由则执行繁忙处理程序。
对于不返回数据的SQL语句,sqlite3_setp()第一次调用就执行完SQL语句,并且返回一个指示结果的代码。对于返回数据的SQL语句,例如select语句,sqlite3_setp()第一次调用将语句定位在第一个记录的B-Tree光标上,后续调用sqlite3_setp()将光标定位在结果集内的后续记录。在到达结尾之前,sqlite3_setp()为结果集中的每个记录返回SQLITE_ROW,当到达结尾时候返回SQLIET_DONE。
函数:int sqlite3_setp(sqlite3_stmt *pstmt) 参数:pstmt:qlite3_prepare_v2函数中ppstmt参数指针。
3.完成与重置
一旦语句接收,必须终止,可以使用以下函数之一完成结束或者重置
函数:int sqlite3_finalize(sqlite3_stmt *pstmt); 功能:关闭语句,释放资源并提交或回滚任何隐式事务,清除日志文件并释放相关的锁。 参数:pstmt:qlite3_prepare_v2函数中ppstmt参数指针。 函数:int sqlite3_reset(sqlite3_stmt *pstmt); 功能:保持以编辑的SQL语句,但是会将当前语句相关联的变化提交到数据库,如果启动了自动提交,还会释放锁清除日志文件。 参数:pstmt:qlite3_prepare_v2函数中ppstmt参数指针。
二者区别:sqlite3_reset会保留语句关联的资源,使他可以重复执行,避免了再次调用sqlite3_prepare()编译命令。
除了sqlite3_exec()和sqlite3_get_table()函数获取记录和列之外数据库还提供了其他函数
1.获取字段信息
函数:int sqlite3_column_count(sqlite3_stmt *pstmt); 功能:返回语句句柄关联的字段数,如果不是select语句则返回0. 函数:int sqlite3_data_count(sqlite3_stmt *pstmt); 功能:返回当前记录的列数。 函数:const char *sqlite3_column_name(sqlite3_stmt*, int icol); 功能:获取当前记录中的所有列 参数:sqlite3_stmt*:语句句柄 icol:字段的次序。 函数:int sqlite3_column_name(sqlite3_stmt*, int icol); 功能:获取每个字段关联的存储类 参数:sqlite3_stmt*:语句句柄 icol:字段的次序。 返回值:1:SQLITE_INTEGER 2:SQLITE_FLOAT 3:SQLITE_TEXT 4:SQLITE_BLOB 5:SQLITE_NULL 函数:int sqlite3_column_bytes(sqlite3_stmt*, int icol); 功能:获取实际数据长度 参数:sqlite3_stmt*:语句句柄 icol:返回信息字段的索引。
2.获取字段值
可以使用sqlite3_column_xxx函数获取当前记录中所有字段值,该函数的通用形式如下:
xxx sqlite3_column_xxx(sqlite3_stmt*, int icol);
下面是常用的sqlite3_column_xxx()函数:
int sqlite3_column_int(sqlite3_stmt*, int icol); double sqlite3_column_double(sqlite3_stmt*, int icol); long long int sqlite3_column_int64(sqlite3_stmt*, int icol); const void *sqlite3_column_blob(sqlite3_stmt*, int icol); const unsigned char *sqlite3_column_text(sqlite3_stmt*, int icol); const void *sqlite3_column_text16(sqlite3_stmt*, int icol);
3.demo
int main() { int rc, i, ncols, id, cid; char *name, *sql; sqlite3 *db; sqlite3_stmt *stmt; sql = "select id, name from stu"; sqlite3_open("test.db", &db); sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL); ncols = sqlite3_column_count(stmt); rc = sqlite3_setp(stmt); for(i = 0; i < ncols; i++) { printf("column: name = %s\n", sqlite3_column_name(stmt, i)); } while(rc == SQLIET_ROW) { id = sqlite3_column_int(stmt, 0); name = sqlite3_column_name(stmt, 1); printf("column: name = %s\n id = %d", name, id); rc = sqlite3_step(stmt); } sqlite3_finalize(stmt); sqlite3_close(db); return 0; }
API运行SQL语句中指定参数,为后面参数提供值,绑定的参数与sqlite3_prepare()一起使用。
例子:第一个?可以绑定值2,第二个?绑定字符串“pi”,第三个绑定值3.14
const char *sql = "insert into foo value(?,?,?)"; sqlite3_prepare(db, sql, strlen(sql), &stmt, NULL); sqlite3_bind_int(stmt, 1, 2); sqlite3_bind_text(stmt, 2, "pi"); sqlite3_bind_double(stmt, 3, 3.14); sqlite3_step(stmt); sqlite3_finalize(stmt);
当sqlite3_prepare()函数识别到SQL语句中有参数时候,在内部为每一个参数分配一个可标识的编号,位置参数从1开始,以此类推顺序整数值。他在生成语句句柄(sqlite3_stmt结构体)中存储这些值。然后期待特定值在执行前绑定到对应参数。
在准备语句之后,可以使用sqlite_bind_xxx()函数绑定参数值
函数:int sqlite3_bind_xxx(sqlite3_stmt*, int i, xxx value); int sqlite3_bind_blob(sqlite3_stmt*, int, const void *, int n, void(*)(void *)); 参数:sqlite3_stmt*:语句句柄 i: 参数个数 value:绑定的值 int:次序 void *:指向blob数据 n:数据的字节长度 void(*)(void *)):清理处理程序,一般为NULL
常用绑定函数:
int sqlite3_bind_int(sqlite3_stmt*, int, int); int sqlite3_bind_double(sqlite3_stmt*, int, double); int sqlite3_bind_int64(sqlite3_stmt*, int, long long int); int sqlite3_bind_null(sqlite3_stmt*, int); int sqlite3_bind_blob(sqlite3_stmt*, int, const void *, int n, void(*)(void *)); int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); int sqlite3_bind_text(sqlite3_stmt*, int, const void *, int n, void(*)(void *)); int sqlite3_bind_text16(sqlite3_stmt*, int, const void *, int n, void(*)(void *));
一旦绑定完所有参数,就可以执行sqlite3_step()函数,sqlite3_step()将接收绑定值,替换SQL语句中的对应参数,然后开始执行命令。
参数编号:允许为参数指定编号,而不是必须使用内部的序列,语法是在?后面加一个数字。
例子:
const char *sql = "insert into foo value(?10,?100,?1000)"; sqlite3_prepare(db, sql, strlen(sql), &stmt, NULL); sqlite3_bind_int(stmt, 10, 2); sqlite3_bind_text(stmt, 100, "pi"); sqlite3_bind_double(stmt, 1000, 3.14);
1.错误处理
可以使用sqlite3_errmsg()获取给定错误的详细信息
函数:const char *sqlite3_errmsg(sqlite3 *);
参数:sqlite3 *:连接句柄
返回值:返回最近连接上API调用产生的错误,如果木有错误则返回 “not an error”
下图是所有SQLite返回的结果
2.繁忙情况处理
有关处理查询的两个重要函数是sqlite3_busy_handler()和sqlite_busy_timeout(),如果所使用的数据库有其他连接的操作,可能会发生冲突,最终需要等待锁返回SQLITE_BUSY。此时需要处理SQLITE_BUSY,每当调用需要锁的SQLite的API函数并且SQLite无法获得锁,函数j就会返回SQLITE_BUSY。处理情况有以下三种:
自己出路SQLITE_BUSY,通过重新运行该语句,或采取一些其他操作
让SQLite调用繁忙处理程序
让SQLite等待一段锁接触的时间。
第三种情况会需要sqlite3_busy_timeout()函数,该函数可以告诉SQLite在返回SQLITE_BUSY前需要等待多长时间锁可以清除。
第二种情况需要使用sqlite3_busy_handler()。该函数提供了调用用户定义函数的方法而不是阻塞等待或返回SQLITE_BUSY.
函数:int sqlite3_busy_handler(sqlite3*, int(*)(void*, int), void*); 参数:sqlite3 *:连接句柄 int(*)(void*, int):指向繁忙处理程序的函数指针 void*:指向应用程序特定数据的指针,作为繁忙处理程序的第一个参数。 注意:在繁忙处理程序中关闭数据库会导致数据库崩溃。
3.模式改变处理
当连接更改了数据库模式,所有在改变发生前编译的准备语句都会失效。这些语句在第一个sqlite_step()调用将尝试重新编译有关SQL并尽可能正常进行,如果重新编译无法实现,则函数sqlite_step()返回SQLITE_SCHEMA。这种情况就是处理改变并重新开始。以下情况下会导致SQLITE_SCHEMA错误
分离数据库
修改或安装用户自定义的函数或聚合
修改或安装用户自定义的排序规则
修改或安装授权函数
清理数据库k空间
QLITE_SCHEMA错误的最终原因是与VDBE有关,当连接更改模式时,其他已编译查询的VDBE代码可能会指向一个不存在或数据库位置已经改变的对象。为了避免产生错误,SQLite将已编译但未执行的所有语句无效化。他们必须重新编译。
API提供了一些函数,可以在编译时和运行时监视和管理SQL命令,这些函数可以安装用来监视数据库的回调函数,通常在各种数据库事件发生时控制这些事件。
1.提交钩子
函数:void *sqlite3_commit_hook(sqlite3 *cnx, int(&xcallback)(void *data), void *data); 功能:监视给定连接上的事务提交事件 参数:cnx:数据库句柄,当连接cnx上提交事务时将触发xcallback回调函数。 int(&xcallback)(void *data):回调函数,如果回调函数返回值非0,提交将转换为回滚。如果回调函数中指针传入NULL,则可以禁止当前注册函数。 data:应用程序数据,传递给回调函数的参数。 返回值:在给定连接一次只能注册一个回调函数,如果之前没有注册过sqlite3_commit_hook则返回NULL,如果之前注册过则返回以前的参数data值。
2.回滚钩子
函数:void *sqlite3_rollback_hook(sqlite3 *cnx, void(&xcallback)(void *data), void *data); 功能:给定连接上的回滚事件。注册回调函数xcallback,连接cnx上的回滚事件触发回调函数,不管是通过显示的回滚命令、隐式错误还是为犯错误导致的回滚。如果是因为数据库关闭连接导致的回滚则无法触发回调函数。 参数:cnx:数据库句柄,当连接cnx上提交事务时将触发xcallback回调函数。 void(&xcallback)(void *data):回调函数。 data:应用程序数据,传递给回调函数的参数。
3.更新钩子
函数:void *sqlite3_update_hook(sqlite3 *cnx, void(*)(void *, int, char const*, char const*, sqlite_int64), void *data); 功能:用于监视给定连接上对行的所有更新、插入和删除操作。 参数:cnx:数据库句柄 回调函数具体形式: void callback(void *data, int operation_code, char const *db_name, char const *table_name, sqlite3_int64 rowid); 参数:data:指向特定应用程序的数据指针。由sqlite3_update_hook函数第三个参数data提供。 operation_code:SQLITE_INSERT:插入操作 SQLITE_UPDATE:更新操作 SQLITE_DELETE:删除操作 db_name:对应数据库名称 table_name:操作发生表的名称 rowid:受影响的行 返回值:如果之前存在回调函数,返回值是指向之前回调函数数据的指针。
4.授权函数
函数:int sqlite3_set_authorizer(sqlite3*, int(*xAuth)(void*, int, const char*, const char*, const char*, const char*), void *puserdata); 功能:监控或控制查询语句的编译,最强大的事件过滤器 参数:回调函数:SQLite在语句编译时为各种数据库事件调用回调函数,使程序可以安全地执行用户提供的SQL, 它提供了一种限制某种SQL操作或否定对数据库中特定表或字段的访问方法。 int auth(void*, //用户数据,sqlite3_set_authorizer函数第四个参数提供 int, //事件代码 授予什么类型的操作权限 const char*, //事件具体相关的参数 const char*, //事件具体相关的参数 const char*, //数据库名称 const char*); //触发器或视图名称,如果为NULL。直接访问SQL语句。
SQLite3是支持多线程的,但是不允许fork()调用将连接传递给子进程,否则无法工作。
1.共享缓存模型
SQLite3支持共享缓存模式,如下图,允许一个进程中多个连接使用共同的页缓存,该功能使用单个线程能够代表其他线程有效管理多个数据库连接的嵌入式服务器。这种情况下,连接共享单个页缓存以及不同的并发模型,与单个线程管理自己的连接相比较,共享缓存使服务器大大减内存的使用。通常情况下共享缓存模式与其他线程共享一个页缓存,不必每个线程都消耗那么多缓存。
下图共享缓存模式
共享缓存模式中,线程依赖于服务器线程帮助他们管理数据库连接。线程通过一种通信机制来向服务器发生SQL语句,服务器使用线程分配的连接执行他们,然后返回结果。线程可以继续发生命令并且控制自己的事务,实际连接存在于主线程由其管理。
默认情况下,共享缓存模式使用表锁分别保持读连接和写连接,表锁和数据库锁不同,他们在共享缓存下的连接中存在,当连接读表时,SQLite试图获取表上的读锁定,写表也是如此,连接不可以向另一个已经有读锁定的连接表写入。表锁绑定到各自的连接并且只在其事务内有效。