sqlite blob 数据检索(基于sqlite3_get_table的优化
sqlite数据库blob数据插入与检索
sqlite数据库共有 SQLITE_INTEGER SQLITE_FLOAT SQLITE_BLOB SQLITE_NULL SQLITE_TEXT 五中数据类型,分别为整形、浮点、块类型、空类型和文本。Blob类型是一种块状的二进制数据,在实际应用时会保存一些我们用到的二进制数据。比如有一个
struct student{
int score;
char name[64];
};
结构体数据需要保存,在实际应用时会将该保存该结构体数据的内存地址和结构体长度传给数据库接口,调用数据库接口时会将内存中的数据拷贝进数据库文件中。
blob数据的插入
sqlite数据库往表中插入一条数据的sql语句如下:
INSERT INTO TABLE_NAME [(column1, column2, column3,...columnN)] VALUES (value1, value2, value3,...valueN);
其中 TABLE_NAME是表名称,column1,column2,… ,columnN 是表中需要插入的列名称,value1,value2,… ,valueN是插入的值。如果要插入bolb数据,则不能用简单的insert语句。
#include <sqlite3.h>
#include <stdio.h>
int main() {
sqlite3 *db;
sqlite3_stmt *stmt;
int rc;
rc = sqlite3_open("example.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
const char *sql = "INSERT INTO blob_table (blob_column) VALUES (?)";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
// 假设我们有一个名为data和size的BLOB数据和大小
// 你需要替换为实际的数据和大小
unsigned char *data = ...;
int size = ...;
rc = sqlite3_bind_blob(stmt, 1, data, size, SQLITE_TRANSIENT);
if (rc != SQLITE_OK) {
fprintf(stderr, "Failed to bind blob: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return 0;
}
blob 数据的插入需要 sqlite3_prepare_v2、sqlite3_bind_blob、sqlite3_step、sqlite3_finalize 等函数配合使用
blob数据的检索
sqlite获取检索结果采用 sqlite3_get_table 函数:
int sqlite3_get_table(
sqlite3 *db, /* An open database */
const char *zSql, /* SQL to be evaluated */
char ***pazResult, /* Results of the query */
int *pnRow, /* Number of result rows written here */
int *pnColumn, /* Number of result columns written here */
char **pzErrmsg /* Error msg written here */
);
void sqlite3_free_table(char **result);
调用 sqlite3_get_table 函数时,会从数据库 db 中执行 zSql 语句,检索结果放在一个动态申请的二维字符串数组中,字符串数组地址通过 pazResult 参数返回。在检索结果使用完之后,需要用 sqlite3_free_table 函数释放 pazResult 所指向的二维数组。
sqlite_get_table 检索blob数据存在的问题
sqlite_get_table 函数返回结果是二维字符串数组,对于整形、浮点、字符串类型数据,可以将其打印成字符串到字符串数组中,空类型可以打印空字符串或者 NULL 字符串。blob数据是二进制对象数据,并没有合适的将其转换成字符串的方法。在 sqlite_get_table 函数返回的结果数组中,blob数据的首地址将返回,但是返回结果的内容长度却与真实blob数据长度不符合。
SQLITE_API int sqlite3_get_table(
sqlite3 *db, /* The database on which the SQL executes */
const char *zSql, /* The SQL to be executed */
char ***pazResult, /* Write the result table here */
int *pnRow, /* Write the number of rows in the result here */
int *pnColumn, /* Write the number of columns of result here */
char **pzErrMsg /* Write error messages here */
){
int rc;
TabResult res;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || pazResult==0 ) return SQLITE_MISUSE_BKPT;
#endif
*pazResult = 0;
if( pnColumn ) *pnColumn = 0;
if( pnRow ) *pnRow = 0;
if( pzErrMsg ) *pzErrMsg = 0;
res.zErrMsg = 0;
res.nRow = 0;
res.nColumn = 0;
res.nData = 1;
res.nAlloc = 20;
res.rc = SQLITE_OK;
res.azResult = sqlite3_malloc64(sizeof(char*)*res.nAlloc );
if( res.azResult==0 ){
db->errCode = SQLITE_NOMEM;
return SQLITE_NOMEM_BKPT;
}
res.azResult[0] = 0;
rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg);
assert( sizeof(res.azResult[0])>= sizeof(res.nData) );
res.azResult[0] = SQLITE_INT_TO_PTR(res.nData);
if( (rc&0xff)==SQLITE_ABORT ){
sqlite3_free_table(&res.azResult[1]);
if( res.zErrMsg ){
if( pzErrMsg ){
sqlite3_free(*pzErrMsg);
*pzErrMsg = sqlite3_mprintf("%s",res.zErrMsg);
}
sqlite3_free(res.zErrMsg);
}
db->errCode = res.rc; /* Assume 32-bit assignment is atomic */
return res.rc;
}
sqlite3_free(res.zErrMsg);
if( rc!=SQLITE_OK ){
sqlite3_free_table(&res.azResult[1]);
return rc;
}
if( res.nAlloc>res.nData ){
char **azNew;
azNew = sqlite3Realloc( res.azResult, sizeof(char*