查看原文
其他

C++ sqlite3使用指南

程序喵大人 程序喵大人 2022-08-22


我在客户端开发时由于需要使用数据库,于是选择了轻巧的sqlite数据库研究了一下,今天在这里和大家分享下我总结的sqlite使用文档。

源码下载wget http://www.sqlite.org/2017/sqlite-autoconf-3160200.tar.gz
tar xvzf sqlite-autoconf-3160200.tar.gz
交叉编译./configure --host=arm-himix200-linux --prefix=/home/zjucad/wangzhiqiang/toolDir/sqlite-autoconf-3160200/libs
make
make install

按顺序执行上述命令后sqlite的链接库和头文件等就会出现在prefix目录下

api使用

基本的使用流程

sqlite3_open()->sqlite3_prepare_v2()->sqlite3_step()->sqlite3_column()->sqlite3_finalize()->sqlite3_close()

具体介绍:

int sqlite3_open(
 const char *filename,   /* Database filename (UTF-8) */
 sqlite3 **ppDb          /* OUT: SQLite db handle */
);
打开一个数据库,第一个参数为数据库的路径,注意这里不能为:memory:,这表示存在内存中,第二个参数为返回,返回这个数据库的handle,函数返回值是int,成功则返回SQLITE_OK,失败的话可以通过sqlite3_errmsg()查看错误信息。
注:sqlite还有个sqlite3_open_v2()函数,添加一些flag,看着不太常用。
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 */
);
准备数据库语句的上下文,相当于编译sql语句,最后由step函数执行编译好的语句,ppStmt需要自己首先定义好,最后一个参数基本传NULL。
注:sqlite还有个sqlite3_prepare()函数,但是现在官方推荐使用v2版本,有错误v2版本能够立即暴露出来。

一般使用sqlite3_prepare_v2()都是和sqlite3_bind_xxx()系列函数一起使用,例如执行插入语句,使用sqlite3_bind_xxx()可以动态绑定某些参数的值,后续给出示例代码。

int sqlite3_step(sqlite3_stmt*);
执行数据库语句
int sqlite3_reset(sqlite3_stmt *pStmt);
重置数据库上下文,当需要重新bind参数时可以使用这个函数,避免多次prepare。
int sqlite3_finalize(sqlite3_stmt *pStmt);
删除数据库语句的上下文,调用了sqlite3_prepare_v2()后无论成功失败都需要调用,避免内存泄漏。
int sqlite3_close(sqlite3 *);
关闭数据库

在执行查询操作时,可能结果不止一条,那sqlite3_step()函数就会返回SQLITE_ROW直到所有结果都被查询到会返回SQLITE_DONE,这里每次sqlite3_step()返回SQLITE_ROW后都需要再次调用返回下一次查询的结果。

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);
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);

执行查询语句后需要使用这类函数,获取查询到的结果,第二个参数代表第几列,需要根据数据库表的每一列具体类型调用相应的函数,可以通过sqlite3_column_type()获取某一列的类型,通过sqlite3_column_count()函数获取这次查询结果共有多少列。

在一些简单的场景下也可以使用sqlite3的wrapper函数
主要有sqlite3_exec()和sqlite3_get_table()函数,相当于调用了sqlite3_prepare_v2()->sqlite3_step()->sqlite3_finalize()这个流程。

区别是sqlite3_exec()在查询中需要添加回调函数,sqlite3_get_table()主要用在查询上,没有回调,会把查询到的结果存到一块地址。

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 */
);

注意这里的最后一个参数是错误信息,如果执行函数没有返回SQLITE_OK,可以查看errmsg处的信息,这块内存也需要自己手动调用sqlite3_free()来释放。
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()主要用在查询上,会把查询到的结果存到一块地址。由调用者手动调用sqlite3_free_table()释放内存。

注意事项

  • sqlite3-column-text()等函数返回的地址不需要自己free,sqlite会在调用sqlite3_step() or sqlite3_reset() or sqlite3_finalize()时自动释放

  • 如何设置自增字段
    字段是integer 的primary key就会自增

    CREATE TABLE t1(
    a INTEGER PRIMARY KEY,
    b INTEGER
    );

    如上代码,字段a就会自增,最大值是9223372036854775807,如果insert超过了这个数量,就会返回SQLITE_FULL错误,但90%的数据库行数可能不会超过这个数量。

示例代码

sqlite3 *db_;
std::string sql =
       "create table if not exists sqlite("
       "id INTEGER primary key     AUTOINCREMENT,"
       "value      int64      not null);";
char *err_msg = NULL;
int ret = sqlite3_exec(db_, sql.c_str(), NULL, 0, &err_msg);
if (ret != SQLITE_OK) {
   std::cout << "create table error " << err_msg;
   sqlite3_free(err_msg);
   return false;
}
std::string sql =
       "insert into sqlite(value) values(?1)";
sqlite3_stmt *stmt;
int ret = sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, NULL);
if (ret != SQLITE_OK) {
   std::cout << "prepare sql error " << ret << " " << sqlite3_errcode(db_);
   return false;
}
if (sqlite3_bind_int64(stmt, 1, 1) != SQLITE_OK) {
   std::cout << "bind timestamp error " << sqlite3_errcode(db_);
   return false;
}
if (sqlite3_step(stmt) != SQLITE_DONE) {
   std::cout << "step insert fail " << sqlite3_errcode(db_);
   return false;
}
sqlite3_finalize(stmt);

▼更多精彩推荐,请关注我们▼


代码精进之路


  代码精进之路,我们一起成长!



深入浅出虚拟内存
深入浅出虚拟内存(二)绘制虚拟内存排布图
深入浅出虚拟内存(三)堆内存分配及malloc实现原理
源码分析shared_ptr实现new[]和delete[]为何要配对使用?内存对齐
C++定时器的实现
C++线程池的实现

想看懂stl代码,先搞定type_traits是关键

客官!在看一下呗~

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存