SQLite 剖析之 C /C++ 接口
前言
SQLite3是 SQLite 一个全新的版本,它虽然是在 SQLite2 的代码基础之上开发的,但是使用了和之前的版本不兼容的数据库格式和 API。SQLite3 是为了满足以下的需求而开发的:支持 UTF-16 编码、用户自定义的文本比较方法、可以对 BLOBs 字段建立索引。SQLite 3.X 版的和 SQLite 2.X 版的 API 非常相似,但是有一些重要的改变需要注意。3.X 版的 API 增加到超过 185 个,所有 API 接口函数和数据结构的前缀都由 ”sqlite_” 改为了 ”sqlite3_”,这是为了避免同时使用 SQLite 2.X 和 SQLite 3.X 这两个版本的时候发生链接冲突。这里概要地介绍一下 SQLite 的核心 API,详细的 API 指南参考 http://sqlite.com/capi3ref.html。
由于对于 C 语言应用什么数据类型来存放 UTF-16 编码的字符串并没有一致的规范,因此 SQLite 使用了普通的 void类型来指向 UTF-16 编码的字符串。客户端使用过程中可以把 void映射成适合他们的系统的任何数据类型。
sqlite3 的使用
一个 SQL 数据库引擎的首要任务是执行 SQL 语句以获得我们想要的数据。为了完成这个任务,开发需要知道 两个对象 : 数据库连接对象sqlite3 和 **SQL 预处理语句对象.sqlite3_stmt,定义如下:
typedef struct sqlite3 sqlite3;
typedef struct sqlite3_stmt sqlite3_stmt;
严格地说,SQL 预处理语句对象不是必需的,因为有使用方便的包装函数 sqlite3_exec 或 sqlite3_get_table,它们封装并且隐藏了 SQL 语句对象。不过理解 SQL 语句对象能更好地使用 SQLite。
数据库连接对象和 SQL 语句对象由下面几个核心的 C /C++ 接口来控制:sqlite3_open()、sqlite3_prepare()、sqlite3_step()、sqlite3_column()、sqlite3_finalize()、sqlite3_close()。
使用 SQLite3 时根据以上函数大概分为几个过程,这几个过程是概念上的说法,而不完全是程序运行的过程,如 sqlite3_column()表示的是对查询获得一行里面的数据的列的各个操作统称,实际上在 sqlite 中并不存在这个函数。在 SQLite 提供的 C /C++ 接口中,其中 5 个 API 属于核心接口。相比于其它数据库引擎提供的 API,如 OCI、MySQL API 等,SQLite 提供的接口易于理解和掌握。以上六个 C /C++ 接口及上面的两个对象构成 SQLite 的核心功能。注意这些接口有些有多个版本,例如 sqlite3_open()有三个独立的版本:sqlite3_open(), sqlite3_open16()和 sqlite3_open_v2(),它们以稍微不同的方式完成同样的事情。sqlite3_column()代表一个家族系列:sqlite_column_int(), sqlite_column_blob()等等,用于提取结果集中各种类型的列数据。
核心对象和接口:
核心对象:
在 SQLite 中最主要的两个对象是:database_connection 和 prepared_statement。database_connection 对象是由 sqlite3_open()接口函数创建并返回的,在应用程序使用任何其他 SQLite 接口函数之前,必须先调用该函数以便获得 database_connnection 对象,在随后的其他 API 调用中,都需要该对象作为输入参数以完成相应的工作。至于 prepare_statement,我们可以简单地将它视为编译后的 SQL 语句,因此,所有和 SQL 语句执行相关的函数也都需要该对象作为输入参数以完成指定的 SQL 操作。
核心接口:
- sqlite3_open.
上面已经提到过这个函数了,它是操作 SQLite 数据库的入口函数。该函数返回的 database_connection 对象是很多其他 SQLite API 的句柄参数。注意,我们通过该函数既可以打开已经存在的数据库文件,也可以创建新的数据库文件。对于该函数返回的 database_connection 对象,我们可以在多个线程之间共享该对象的指针,以便完成和数据库相关的任意操作。然而在多线程情况下,更为推荐的使用方式是,为每个线程创建独立的 database_connection 对象。对于该函数还有一点也需额外说明,我们没有必要为了访问多个数据库而创建多个数据库连接对象,因为通过 SQLite 自带的 ATTACH 命令可以在一个连接中方便的访问多个数据库。 - sqlite3_prepare.
该函数将 SQL 文本转换为 prepared_statement 对象,并在函数执行后返回该对象的指针。事实上,该函数并不会评估参数指定 SQL 语句,它仅仅是将 SQL 文本初始化为待执行的状态。最后需要指出的,对于新的应用程序,可以使用 sqlite3_prepare_v2 接口函数来替代该函数以完成相同的工作。 - sqlite3_step.
该函数用于评估 sqlite3_prepare 函数返回的
prepared_statement 对象,在执行完该函数之后,prepared_statement 对象的内部指针将指向其返回的结果集的第一行。如果打算进一步迭代其后的数据行,就需要不断的调用该函数,直到所有的数据行都遍历完毕。然而对于 INSERT、UPDATE 和 DELETE 等 DML 语句,该函数执行一次即可完成。 - sqlite3_column.
该函数用于获取当前行指定列的数据,然而严格意义上讲,此函数在 SQLite 的接口函数中并不存在,而是由一组相关的接口函数来完成该功能,其中每个函数都返回不同类型的数据,如:
sqlite3_column_blob
sqlite3_column_bytes
sqlite3_column_bytes16
sqlite3_column_double
sqlite3_column_int
sqlite3_column_int64
sqlite3_column_text
sqlite3_column_text16
sqlite3_column_type
sqlite3_column_value
sqlite3_column_count
其中,sqlite3_column_count 函数用于获取当前结果集中的字段数目。下面是使用 sqlite3_step 和 sqlite3_column 函数迭代结果集中每行数据的伪代码(注意这里作为示例代码简化了对字段类型的判断):
int fieldCount = sqlite3_column_count(...);
while (sqlite3_step(...) <> EOF) {for (int i = 0; i < fieldCount; ++i) {int v = sqlite3_column_int(...,i);
}
}
- sqlite3_finalize.
该函数用于销毁 prepared statement 对象,否则将会造成内存泄露。 - sqlite3_close.
该函数用于关闭之前打开的 database_connection 对象,其中所有和该对象相关的 prepared_statements 对象都必须在此之前先被销毁。
参数绑定:
和大多数关系型数据库一样,SQLite 的 SQL 文本也支持变量绑定,以便减少 SQL 语句被动态解析的次数,从而提高数据查询和数据操作的效率。要完成该操作,我们需要使用 SQLite 提供的另外两个接口 API,sqlite3_reset 和 sqlite3_bind。见如下示例:
void test_parameter_binding() {
//1. 不带参数绑定的情况下插入多条数据。char strSQL[128];
for (int i = 0; i < MAX_ROWS; ++i) {sprintf(strSQL,"insert into testtable values(%d)",i);
sqlite3_prepare_v2(..., strSQL);
sqlite3_step(prepared_stmt);
sqlite3_finalize(prepared_stmt);
}
//2. 参数绑定的情况下插入多条数据。string strSQLWithParameter = "insert into testtable values(?)";
sqlite3_prepare_v2(..., strSQLWithParameter);
for (int i = 0; i < MAX_ROWS; ++i) {sqlite3_bind(...,i);
sqlite3_step(prepared_stmt);
sqlite3_reset(prepared_stmt);
}
sqlite3_finalize(prepared_stmt);
}
这里首先需要说明的是,SQL 语句 ”insert into testtable values(?)” 中的问号 (?) 表示 参数变量的占位符,该规则在很多关系型数据库中都是一致的,因此这对于数据库移植操作还是比较方便的。
通过上面的示例代码,明显可以看出参数绑定写法的执行效率要高于每次生成不同的 SQL 语句的写法,即 (2) 在效率上要明显优于(1),下面是针对这两种写法的具体比较:
- 单单从程序表面来看,前者在 for 循环中执行了更多的任务,比如字符串的填充、SQL 语句的 prepare,以及 prepared_statement 对象的释放。
- 在 SQLite 的官方文档中明确的指出,sqlite3_prepare_v2 的执行效率往往要低于 sqlite3_step 的效率。
- 当插入的数据量较大时,后者带来的效率提升还是相当可观的。
一、打开和关闭数据库连接
int sqlite3_open(const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
int sqlite3_open16(const void *filename, /* Database filename (UTF-16) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
int sqlite3_open_v2(const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb, /* OUT: SQLite db handle */
int flags, /* Flags */
const char *zVfs /* Name of VFS module to use */
);
int sqlite3_close(sqlite3*);
int sqlite3_close_v2(sqlite3*);
int sqlite3_errcode(sqlite3 *db);
int sqlite3_extended_errcode(sqlite3 *db);
const char *sqlite3_errmsg(sqlite3*);
const void *sqlite3_errmsg16(sqlite3*);
建立到一个 SQLite 数据库文件的连接,返回连接对象。如果数据库文件不存在,则创建这个文件,函数返回一个整数错误代码。许多 SQLite 接口需要一个指向连接对象的指针作为第一个参数,这个函数用来创建一个数据库连接对象。sqlite3_open()和 sqlite3_open16()的不同之处在于 sqlite3_open16()使用 UTF-16 编码(使用本地主机字节顺序)传递数据库文件名。如果要创建新数据库,sqlite3_open16()将内部文本转换为 UTF-16 编码,反之 sqlite3_open()将文本转换为 UTF- 8 编码。打开或者创建数据库的命令会被缓存,直到这个数据库真正被调用的时候才会被执行。而且允许使用 PRAGMA 声明来设置如本地文本编码或默认内存页面大小等选项和参数。
sqlite3_close()关闭数据库连接,在关闭之前所有准备好的 SQL 语句对象都要被销毁。
sqlite3_errcode()通常用来获取最近调用的 API 接口返回的错误代码。sqlite3_errmsg()则用来得到这些错误代码所对应的文字说明。这些错误信息将以 UTF- 8 的编码返回,并且在下一次调用任何 SQLiteAPI 函数的时候被清除。sqlite3_errmsg16()和 sqlite3_errmsg()大体上相同,除了返回的错误信息将以 UTF-16 本机字节顺序编码。
SQLite 的返回码定义如下:
#define SQLITE_OK 0 /* Successful result */
/* beginning-of-error-codes */
#define SQLITE_ERROR 1 /* SQL error or missing database */
#define SQLITE_INTERNAL 2 /* 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 sqlite3_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 /* Unknown opcode in sqlite3_file_control() */
#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 /* Database is empty */
#define SQLITE_SCHEMA 17 /* The database schema changed */
#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */
#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_FORMAT 24 /* Auxiliary database format error */
#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
#define SQLITE_NOTADB 26 /* File opened that is not a database file */
#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
/* end-of-error-codes */
二、编译 SQL 语句
int sqlite3_prepare(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
int sqlite3_prepare_v2(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
int sqlite3_prepare16(
sqlite3 *db, /* Database handle */
const void *zSql, /* SQL statement, UTF-16 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const void **pzTail /* OUT: Pointer to unused portion of zSql */
);
int sqlite3_prepare16_v2(
sqlite3 *db, /* Database handle */
const void *zSql, /* SQL statement, UTF-16 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const void **pzTail /* OUT: Pointer to unused portion of zSql */
);
把 SQL 文本编译成一个 SQL 语句对象并返回这个对象的指针。它只是把含有 SQL 语句的字符串编译成字节码,并不执行 SQL 语句。sqlite3_prepare()处理的 SQL 语句应该是 UTF- 8 编码的,而 sqlite3_prepare16()则要求是 UTF-16 编码的。输入的参数中只有第一个 SQL 语句会被编译。第四个参数 则用来指向输入参数中 下一个需要编译的 SQL 语句 存放的 SQLite statement 对象的指针。任何时候如果调用 sqlite3_finalize()将销毁一个准备好的 SQL 声明。在数据库关闭之前,所有准备好的声明都必须被释放销毁。sqlite3_reset()函数用来重置一个 SQL 声明的状态,使得它可以被再次执行。
注意现在 sqlite3_prepare()已经不被推荐使用了,在新的应用中推荐使用 sqlite3_prepare_v2()。
三、执行 SQL 语句
int sqlite3_step(sqlite3_stmt*);
在 SQL 声明准备好之后,就可以调用 sqlite3_step()来执行这个 SQL 声明。如果 SQL 返回了一个单行结果集,sqlite3_step()函数将返回 SQLITE_ROW,若要得到结果集的第二行、第三行 …,则要继续调用 sqlite3_step()。如果 SQL 语句执行成功或者正常将返回 SQLITE_DONE,否则将返回错误代码。如果不能打开数据库文件则会返回 SQLITE_BUSY。
执行 SQL 语句 还可以直接 用便捷的 包装函数 ,这样就 无需预先编译 SQL 语句。如下:
typedef int (*sqlite3_callback)(void*,int,char**, char**);
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
sqlite3_exec 函数依然像它在 SQLite 2 中一样承担着很多的工作。该函数的第二个参数中可以指定零个或多个 SQL 语句,查询的结果返回给回调函数,回调函数会作用在结果集的每条记录上。sqlite3_exec 函数实际上封装了 sqlite3_prepare_v2(),sqlite3_step()和 sqlite3_finalize(),因此可以通过一个调用直接执行多条 SQL 语句,让应用程序省略大量代码,因此在实际应用中一般使用这个函数。
四、获取结果集数据
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
const char *sqlite3_column_decltype(sqlite3_stmt *, int iCol);
const void *sqlite3_column_decltype16(sqlite3_stmt *, int iCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
int sqlite3_column_int(sqlite3_stmt*, int iCol);
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
int sqlite3_column_type(sqlite3_stmt*, int iCol);
sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
const char *sqlite3_column_name(sqlite3_stmt*, int N);
const void *sqlite3_column_name16(sqlite3_stmt*, int N);
int sqlite3_column_count(sqlite3_stmt *pStmt);
int sqlite3_data_count(sqlite3_stmt *pStmt);
如果函数 sqlite3_step()的返回值是 SQLITE_ROW,那么用以上方法可以获得记录集中的数据。
sqlite3_column_count()函数返回结果集中包含的列数,sqlite3_column_count()可以在执行了 sqlite3_prepare()之后的任何时刻调用。sqlite3_data_count()除了必须在 sqlite3_step()之后调用之外,其他跟 sqlite3_column_count()大同小异。如果调用 sqlite3_step()返回值是 SQLITE_DONE 或者一个错误代码,则此时调用 sqlite3_data_count()将返回 0,然而 sqlite3_column_count()仍然会返回结果集中包含的列数。
返回的记录集通过使用其它几个 sqlite3_column_xx()函数来提取,所有的这些函数都把列的编号作为第二个参数。列编号从左到右以零起始,和之前那些从 1 起始的参数不同。
sqlite3_column_type()函数返回第 N 列的值的数据类型,具体的返回值如下:
#define SQLITE_INTEGER 1
#define SQLITE_FLOAT 2
#define SQLITE_TEXT 3
#define SQLITE_BLOB 4
#define SQLITE_NULL 5
sqlite3_column_decltype()则用来返回该列在 CREATE TABLE 语句中声明的类型,它可以用在当返回类型是空字符串的时候。
sqlite3_column_name()返回第 N 列的字段名。
sqlite3_column_bytes()用来返回 UTF- 8 编码的 BLOB 列的字节数或者 TEXT 字符串的字节数。
sqlite3_column_bytes16()对于 BLOB 列返回同样的结果,但是对于 TEXT 字符串则按 UTF-16 的编码来计算字节数。
sqlite3_column_blob()返回 BLOB 数据。
sqlite3_column_text()返回 UTF- 8 编码的 TEXT 数据。sqlite3_column_text16()返回 UTF-16 编码的 TEXT 数据。
sqlite3_column_int()以本地主机的整数格式返回一个整数值。sqlite3_column_int64()返回一个 64 位的整数。
sqlite3_column_double()返回浮点数。
不一定非要按照 sqlite3_column_type()接口返回的数据类型来获取数据,数据类型不同时软件将自动转换。
五、SQL 声明对象的销毁和重用
int sqlite3_finalize(sqlite3_stmt*);
int sqlite3_reset(sqlite3_stmt*);
函数 sqlite3_finalize() 销毁由 sqlite3_prepare()创建的 SQL 声明对象。在数据库关闭之前每个准备好的声明都必须被销毁,以避免内存泄露。sqlite3_reset()则用来重置一个 SQL 声明的状态,使得它可以被再次执行。例如用 sqlite3_step()执行完编译好的 SQL 声明后,还想再执行它,则可用 sqlite3_reset()重置它即可,而无需用 sqlite3_prepare()再来编译一个新 SQL 声明,因为很多 SQL 声明的编译时间甚至超过执行时间。
六、给 SQl 语句绑定参数
SQL 语句声明中可以包含如下形式的参数:
?
?NNN
:AAA
$AAA
@AAA
其中 ”NNN” 是一个整数,”AAA” 是一个字符串,这些标记代表一些不确定的字符值(或者说是通配符)。在首次调用 sqlite3_step()之前或者刚调用 sqlite3_reset()之后,应用程序可以用 sqlite3_bind 接口来 填充 这些 参数。每一个通配符都被分配了一个编号(由它在 SQL 声明中的位置决定,从 1 开始),此外也可以用 ”NNN” 来表示 ”?NNN” 这种情况。允许相同的通配符在同一个 SQL 声明中出现多次,在这种情况下所有相同的通配符都会被替换成相同的值。没有被绑定的通配符将自动取 NULL 值。
int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
int sqlite3_bind_double(sqlite3_stmt*, int, double);
int sqlite3_bind_int(sqlite3_stmt*, int, int);
int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
int sqlite3_bind_null(sqlite3_stmt*, int);
int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
以上是 sqlite3_bind 所包含的全部接口,用来给 SQL 声明中的通配符赋值。没有绑定的通配符则被认为是空值。已绑定的值不会被 sqlite3_reset()函数重置。但是在调用了 sqlite3_reset()之后所有的通配符都可以被重新赋值。注意绑定操作是可选的。
七、扩展 SQL
(1)创建自定义的比较序列:
int sqlite3_create_collation(
sqlite3*,
const char *zName,
int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*)
);
int sqlite3_create_collation_v2(
sqlite3*,
const char *zName,
int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*),
void(*xDestroy)(void*)
);
int sqlite3_create_collation16(
sqlite3*,
const void *zName,
int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*)
);
int sqlite3_collation_needed(
sqlite3*,
void*,
void(*)(void*,sqlite3*,int eTextRep,const char*)
);
int sqlite3_collation_needed16(
sqlite3*,
void*,
void(*)(void*,sqlite3*,int eTextRep,const void*)
);
这些函数在数据库连接上,为要比较的文本添加、删除或者修改自定义比较规则。第三个参数 eTextRep 表示 SQLite 支持的字符编码类型,必须是以下常量之一:
#define SQLITE_UTF8 1
#define SQLITE_UTF16LE 2
#define SQLITE_UTF16BE 3
#define SQLITE_UTF16 4 /* Use native byte order */
#define SQLITE_ANY 5 /* sqlite3_create_function only */
#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */
sqlite3_create_collation()函数用来声明一个比较序列和实现它的比较函数,比较函数只能用来做文本的比较。同一个自定义的比较规则的同一个比较函数可以有 UTF-8、UTF-16LE 和 UTF-16BE 等多个编码的版本。sqlite3_create_collation16()和 sqlite3_create_collation()的区别也仅仅在于比较名称的编码是 UTF-16 还是 UTF-8。
可以使用 sqlite3_collation_needed()函数来注册一个回调函数,当数据库引擎遇到未知的比较规则时会自动调用该函数。在回调函数中可以查找一个相似的比较函数,并激活相应的 sqlite_3_create_collation()函数。回调函数的第四个参数是比较规则的名称。同样 sqlite3_collation_needed 采用 UTF- 8 编码,sqlite3_collation_need16()采用 UTF-16 编码。
(2)创建自定义的 SQL 函数:
int sqlite3_create_function(
sqlite3 *db,
const char *zFunctionName,
int nArg,
int eTextRep,
void *pApp,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*)
);
int sqlite3_create_function16(
sqlite3 *db,
const void *zFunctionName,
int nArg,
int eTextRep,
void *pApp,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*)
);
int sqlite3_create_function_v2(
sqlite3 *db,
const char *zFunctionName,
int nArg,
int eTextRep,
void *pApp,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*)
);
nArg 参数用来表明自定义函数的参数个数。如果参数值为 0,则表示接受任意个数的参数。用 eTextRep 参数来表明传入参数的编码形式。SQLite 3 允许同一个自定义函数有多种不同的编码参数的版本。数据库引擎会自动选择转换参数编码个数最少的版本使用。
普通的函数只需要设置 xFunc 参数,而把 xStep 和 xFinal 设为 NULL。聚合函数则需要设置 xStep 和 xFinal 参数,然后把 xFunc 设为 NULL。该方法和使用 sqlite3_create_aggregate() API 一样。
sqlite3_create_function16()和 sqlite_create_function()的不同就在于自定义的函数名一个要求是 UTF-16 编码,而另一个则要求是 UTF-8。
自定函数的参数目前使用 sqlite3_value 结构体指针替代了 SQLite version 2.X 中的字符串指针。
下面的函数用来从 sqlite3_value 结构体中提取数据,以获得 SQL 函数的参数值:
const void *sqlite3_value_blob(sqlite3_value*);
int sqlite3_value_bytes(sqlite3_value*);
int sqlite3_value_bytes16(sqlite3_value*);
double sqlite3_value_double(sqlite3_value*);
int sqlite3_value_int(sqlite3_value*);
sqlite3_int64 sqlite3_value_int64(sqlite3_value*);
const unsigned char *sqlite3_value_text(sqlite3_value*);
const void *sqlite3_value_text16(sqlite3_value*);
const void *sqlite3_value_text16le(sqlite3_value*);
const void *sqlite3_value_text16be(sqlite3_value*);
int sqlite3_value_type(sqlite3_value*);
int sqlite3_value_numeric_type(sqlite3_value*);
上面的函数调用以下的 API 来获得上下文内容和返回结果:
void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
void sqlite3_result_double(sqlite3_context*, double);
void sqlite3_result_error(sqlite3_context*, const char*, int);
void sqlite3_result_error16(sqlite3_context*, const void*, int);
void sqlite3_result_error_toobig(sqlite3_context*);
void sqlite3_result_error_nomem(sqlite3_context*);
void sqlite3_result_error_code(sqlite3_context*, int);
void sqlite3_result_int(sqlite3_context*, int);
void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
void sqlite3_result_null(sqlite3_context*);
void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));
void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
void sqlite3_result_zeroblob(sqlite3_context*, int n);
void *sqlite3_user_data(sqlite3_context*);
void *sqlite3_get_auxdata(sqlite3_context*, int N);
void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
(3)注册自定义的虚拟表模块:
int sqlite3_create_module(
sqlite3 *db, /* SQLite connection to register module with */
const char *zName, /* Name of the module */
const sqlite3_module *p, /* Methods for the module */
void *pClientData /* Client data for xCreate/xConnect */
);
int sqlite3_create_module_v2(
sqlite3 *db, /* SQLite connection to register module with */
const char *zName, /* Name of the module */
const sqlite3_module *p, /* Methods for the module */
void *pClientData, /* Client data for xCreate/xConnect */
void(*xDestroy)(void*) /* Module destructor function */
);
其中第二个参数指定虚拟表模块名称,第三个参数指向虚拟表模块,第四个参数为传给虚拟表模块 xCreate/xConnect 方法的客户数据。sqlite3_create_module_v2()还有第五个参数,指定对 pClientData 数据进行析构的函数。若指定析构函数为 NULL,则该函数与 sqlite3_create_module()等价。
SQLite 的所有内建 SQL 函数都使用上面这些接口来创建,特别是 date.c 和 func.c 中的 SQL 函数代码。
程序示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sqlite3.h>
int main(int argc, char **argv)
{
sqlite3 *db;
sqlite3_stmt * stmt;
const char *zTail;
// 打开数据库
int r = sqlite3_open("mysqlite.db",&db);
if(r){printf("%s",sqlite3_errmsg(db));
}
else
printf("open db sccess!\n");
// 创建 Table
//sqlite3_prepare()将 SQL 语句编译为 sqlite 内部一个结构体(sqlite3_stmt),// 该结构体中包含了将要执行的 SQL 语句的信息
// 第四个参数用来指向输入参数中下一个需要编译的 SQL 语句存放的 SQLite statement 对象的指针
sqlite3_prepare(db,"CREATE TABLE players ( ID INTEGER PRIMARY KEY, name TEXT, age INTEGER);",-1,&stmt,&zTail);
printf("prepared has done\n");
// 调用 sqlite3_step(),此时 SQL 语句才真正执行,执行成功,返回 SQLITE_DONE 或 SQLITE_ROW.
// 每次调用 sqlite3_step(),只返回一行数据,使用 sqlite3_column_XXX()函数来取出这些数据
// 要取出全部的数据,则需要反复调用 sqlite3_step()
sqlite3_step(stmt);
// 调用 sqlite3_finalize(),释放 stmt 占用的内存,该内存是在 sqlite3_prepare()时分配的
// 如果 SQL 语句要重复使用,可以调用 sqlite3_reset()来清除已经绑定的参数
sqlite3_finalize(stmt);
printf("create table success!\n");
// 插入数据
sqlite3_prepare(db,"INSERT INTO players (name,age) VALUES(?,?);",-1,&stmt,&zTail);
char str[] = "Kevin";
int n = 23;
//sqlite3_bind_xxx 的第四个参数为负,则字符串长度由第一个 0 终止的位数决定
//SQLITE_STATIC 表示命令执行完后的信息为 static 类型,不能被改动, 而且不需要被 free
sqlite3_bind_text(stmt,1,str,-1,SQLITE_STATIC);
sqlite3_bind_int(stmt,2,n);
r = sqlite3_step(stmt);
if(r!=SQLITE_DONE){printf("%s",sqlite3_errmsg(db));
}
// 清除已经绑定的参数
sqlite3_reset(stmt);
// 插入第二个数据
char str2[] = "Jack";
int n2 = 16;
sqlite3_bind_text(stmt,1,str2,-1,SQLITE_STATIC);
sqlite3_bind_int(stmt,2,n2);
r = sqlite3_step(stmt);
if(r!=SQLITE_DONE){printf("%s",sqlite3_errmsg(db));
}
sqlite3_finalize(stmt); // 释放 stmt 所占的内存
// 查询所有数据
sqlite3_prepare(db,"SELECT ID, name, age FROM players ORDER BY age",-1,&stmt,&zTail);
r = sqlite3_step(stmt);
int number;
int id;
const unsigned char * name;
while(r == SQLITE_ROW){id = sqlite3_column_int( stmt, 0);
name = sqlite3_column_text(stmt,1);
number = sqlite3_column_int(stmt, 2);
printf("ID: %d Name: %s Age: %d \n",id,name,number);
sleep(1);
r = sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
// 关闭数据库
sqlite3_close(db);
return 0;
}
编译运行:$ gcc sqlite.c –o sqlite –lsqlite3
$ ./sqlite