sqlite

#include <iostream>

#include <sqlite3.h>

using namespace std;

//OK啊大家,本文件我们来学习一下一个mysql更加轻量化,更加方便使用的数据库~~

//那么也就是SQlite,这个数据库呢,不需要太复杂的环境配置,只需要包含头文件和库文件就行了~~~

//然后在编译的时候,我们需要链接上sqlite3的库文件~~~

//它的存储的方式是一个单个的文件,不像mysql那样子,需要多个文件来存储~~~

//所以他也就更轻量,门槛也会更低

//然后值得一提的是,大家不用觉得说,学习sqlite3的语法,会很复杂~~~

//其实是不会的,因为它和mysql的语法是一致的~~~

//所以我们只要会mysql的语法,那么sqlite3的语法也就很简单了~~~

//唯一有什么区别的,也就是调用的函数啊,命令行命令什么的,会不一样

//但是这也不是问题,我们只要用它的增删查改就行~~~

//下面我们来一个一个函数的解析~~~

/*************************************************************

* SQLite 数据库连接管理核心函数 - 基础中的基础

* 所有操作都依赖数据库连接的正确打开和关闭,务必掌握

************************************************************/

/**

* @brief 打开/创建 SQLite 数据库连接

* @details 这是操作 SQLite 数据库的第一步!如果指定路径的数据库文件不存在,函数会自动创建一个新的空数据库;

* 如果文件已存在,则建立与该数据库的连接。后续所有数据库操作(查、增、删、改)都需要这个连接句柄。

*

* @param filename 数据库文件的路径(C风格字符串)

* - 示例1:"test.db" → 当前程序目录下的 test.db 文件

* - 示例2:"/home/user/data/mydb.db" → Linux 绝对路径

* - 示例3:"C:\\Users\\user\\mydb.db" → Windows 绝对路径(注意转义反斜杠)

* - 特殊值:":memory:" → 创建内存数据库(程序退出后数据消失)

* @param ppDb 指向 sqlite3* 类型指针的指针(输出参数)

* - 函数执行成功后,会把「数据库连接句柄」写入这个指针指向的内存地址

* - 使用前需要先定义 sqlite3* db = nullptr; 然后传入 &db

* @return int 执行结果码

* - SQLITE_OK (值为0):连接成功/数据库创建成功

* - 非0值:错误码(比如 SQLITE_CANTOPEN 表示无法打开/创建文件,SQLITE_IOERR 表示IO错误)

*

* @使用步骤:

* 1. 定义 sqlite3 类型的指针变量,初始化为 nullptr:sqlite3* db = nullptr;

* 2. 调用 sqlite3_open,传入数据库路径和指针地址:int ret = sqlite3_open("test.db", &db);

* 3. 检查返回值:如果 ret != SQLITE_OK,说明连接失败,用 sqlite3_errmsg 获取错误信息;

* 4. 连接成功后,通过 db 句柄执行后续的 SQL 操作;

* 5. 操作完成后,必须调用 sqlite3_close 关闭连接。

*

* @示例代码片段:

* sqlite3* db = nullptr;

* int ret = sqlite3_open("my_database.db", &db);

* if (ret != SQLITE_OK) {

* printf("数据库连接失败:%s\n", sqlite3_errmsg(db));

* sqlite3_close(db); // 即使失败,也建议尝试关闭(避免资源泄漏)

* return -1;

* }

*/

int sqlite3_open(const char *filename, sqlite3 **ppDb);

/**

* @brief 关闭 SQLite 数据库连接并释放资源

* @details 数据库操作完成后,必须调用该函数关闭连接,否则会导致内存泄漏、文件句柄占用等问题。

* 关闭前要确保所有预处理语句(sqlite3_stmt)都已通过 sqlite3_finalize 销毁,否则会关闭失败。

*

* @param db 已打开的数据库连接句柄(由 sqlite3_open 成功返回的句柄)

* @return int 执行结果码

* - SQLITE_OK (0):关闭成功,所有资源已释放

* - SQLITE_BUSY:关闭失败(原因:还有未完成的预处理语句/查询操作)

* - 其他非0值:其他错误(比如句柄无效)

*

* @使用步骤:

* 1. 确保所有通过 sqlite3_prepare_v2 创建的预处理语句都已调用 sqlite3_finalize 销毁;

* 2. 调用 sqlite3_close,传入数据库句柄;

* 3. 检查返回值:如果返回 SQLITE_BUSY,说明还有未释放的预处理语句,需先清理再重试;

* 4. 关闭后,将 db 句柄置为 nullptr,避免后续误操作:db = nullptr;

*

* @注意事项:

* - 即使 sqlite3_open 失败,也建议调用 sqlite3_close(容错处理);

* - 关闭后的句柄不能再用于任何操作,否则会导致程序崩溃。

*

* @示例代码片段:

* int ret = sqlite3_close(db);

* if (ret != SQLITE_OK) {

* printf("数据库关闭失败:%s\n", sqlite3_errmsg(db));

* // 排查:是否有未 finalize 的 sqlite3_stmt?

* return -1;

* }

* db = nullptr; // 置空,防止野指针

*/

int sqlite3_close(sqlite3* db);

/**

* @brief 获取数据库操作的最后一次错误信息(英文)

* @details 当 sqlite3_open、sqlite3_exec、sqlite3_prepare_v2 等函数返回非 SQLITE_OK 时,

* 调用该函数可以获取具体的错误原因,是调试数据库问题的核心函数。

*

* @param db 数据库连接句柄(即使连接失败,只要句柄不是 nullptr 就能调用)

* @return const char* 错误信息字符串的指针

* - 返回的字符串由 SQLite 内部管理,不需要手动释放;

* - 如果没有错误,可能返回空字符串或 "not an error";

* - 如果数据库忙(比如未关闭预处理语句),会返回 "database is busy" 等提示。

*

* @使用场景:

* 任何数据库操作函数返回非 SQLITE_OK 时,立即调用该函数打印/记录错误信息。

*

* @示例代码片段:

* int ret = sqlite3_open("test.db", &db);

* if (ret != SQLITE_OK) {

* // 打印错误信息:比如 "unable to open database file"

* printf("连接失败原因:%s\n", sqlite3_errmsg(db));

* sqlite3_close(db);

* return -1;

* }

*/

const char *sqlite3_errmsg(sqlite3* db);

/*************************************************************

* SQLite SQL 语句执行核心函数 - 增删改查的核心

************************************************************/

/**

* @brief 将 SQL 语句编译为「预处理语句对象」(核心!支持参数绑定,避免SQL注入)

* @details 执行动态 SQL(比如带变量的查询/插入)时,必须先用该函数编译 SQL,再绑定参数,最后执行。

* 预处理语句的优势:1. 避免 SQL 注入;2. 重复执行时效率更高;3. 支持参数化查询。

*

* @param db 已打开的数据库连接句柄

* @param zSql 要编译的 SQL 语句文本(C风格字符串)

* - 示例:"SELECT * FROM user WHERE id = ?"(? 是参数占位符)

* - 示例:"INSERT INTO student (name, age) VALUES (?, ?)"

* @param nByte SQL 文本的字节长度

* - 传入 -1:SQLite 会自动计算长度(直到字符串结束符 '\0'),推荐使用;

* - 传入具体数值:只编译前 nByte 个字节(适用于超长 SQL)。

* @param ppStmt 指向 sqlite3_stmt* 的指针(输出参数)

* - 编译成功后,会把「预处理语句句柄」写入该指针指向的地址;

* - 使用前定义:sqlite3_stmt* stmt = nullptr; 传入 &stmt。

* @param pzTail 指向未编译的 SQL 剩余部分的指针

* - 比如 SQL 是 "SELECT * FROM a; SELECT * FROM b;",编译后 pzTail 会指向第二个 SELECT;

* - 普通场景下直接传 nullptr 即可(只编译一条 SQL)。

* @return int 编译结果码

* - SQLITE_OK (0):编译成功;

* - 非0值:编译失败(比如 SQL 语法错误、表不存在),用 sqlite3_errmsg 查原因。

*

* @使用步骤:

* 1. 定义预处理语句句柄:sqlite3_stmt* stmt = nullptr;

* 2. 编写带占位符的 SQL:const char* sql = "INSERT INTO user (name, age) VALUES (?, ?)";

* 3. 调用编译函数:int ret = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);

* 4. 检查返回值:失败则打印错误,成功则进行参数绑定。

*

* @注意事项:

* - 编译成功的 stmt 必须调用 sqlite3_finalize 销毁,否则会导致内存泄漏;

* - 占位符 ? 的序号从 1 开始(不是 0!)。

*/

int sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte,

sqlite3_stmt **ppStmt, const char **pzTail);

/**

* @brief 给预处理语句的占位符绑定 64 位整数值

* @details 用于给 SQL 中的 ? 占位符绑定整数类型的值(比如 id、age、数量等),支持大整数(long long 级别)。

*

* @param pStmt 已编译成功的预处理语句句柄(sqlite3_prepare_v2 返回的 stmt)

* @param index 占位符的序号(从 1 开始!不是 0!)

* - 比如 SQL 是 "INSERT INTO a (x, y) VALUES (?, ?)",第一个 ? 是 1,第二个 ? 是 2;

* @param value 要绑定的 64 位整数值(sqlite3_int64 等价于 long long)

* - 可以传入 int、long、long long 类型(会自动转换);

* @return int 绑定结果码

* - SQLITE_OK (0):绑定成功;

* - 非0值:绑定失败(比如序号超出占位符数量、stmt 无效)。

*

* @使用示例:

* // 假设 stmt 已编译:INSERT INTO user (id, age) VALUES (?, ?)

* sqlite3_bind_int64(stmt, 1, 1001); // 第一个 ? 绑定 1001(id)

* sqlite3_bind_int64(stmt, 2, 25); // 第二个 ? 绑定 25(age)

*/

int sqlite3_bind_int64(sqlite3_stmt*, int index, sqlite3_int64 value);

/**

* @brief 给预处理语句的占位符绑定文本字符串(最常用的绑定函数)

* @details 用于绑定字符串类型的值(比如姓名、地址、描述等),支持自定义内存管理方式。

*

* @param pStmt 已编译成功的预处理语句句柄

* @param index 占位符的序号(从 1 开始!)

* @param value 要绑定的文本字符串(C风格字符串,比如 "张三"、"北京市")

* @param len 字符串的字节长度

* - 传入 -1:SQLite 自动计算长度(直到 '\0'),推荐使用;

* - 传入具体数值:只绑定前 len 个字节(适用于超长字符串/二进制数据)。

* @param destructor 内存释放策略(决定 SQLite 是否复制/释放字符串)

* - SQLITE_STATIC:SQLite 不复制字符串,也不释放内存 → 适合绑定「常量字符串/栈上字符串」;

* - SQLITE_TRANSIENT:SQLite 会复制一份字符串到内部缓存 → 适合绑定「堆上字符串/临时字符串」;

* - 自定义函数:传入自定义的释放函数(高级用法,新手用不到)。

* @return int 绑定结果码

* - SQLITE_OK (0):绑定成功;

* - 非0值:绑定失败(比如序号错误、字符串为空且表字段不允许NULL)。

*

* @使用示例:

* // 假设 stmt 已编译:INSERT INTO user (name, address) VALUES (?, ?)

* const char* name = "张三";

* const char* addr = "北京市海淀区";

* // 绑定姓名(常量字符串,用 SQLITE_STATIC)

* sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);

* // 绑定地址(临时字符串,用 SQLITE_TRANSIENT)

* sqlite3_bind_text(stmt, 2, addr, -1, SQLITE_TRANSIENT);

*

* @注意事项:

* - 如果 value 是堆上分配的字符串(比如 malloc 分配),且用了 SQLITE_STATIC,

* 必须等 stmt 执行完成后再释放,否则会导致野指针!

*/

int sqlite3_bind_text(sqlite3_stmt*, int index, const char *value, int len,

void(*destructor)(void*));

/**

* @brief 直接执行一条/多条 SQL 语句(适合无返回数据的场景:创建表、插入、删除、更新)

* @details 简化版执行函数,无需编译预处理语句,直接执行 SQL。缺点:不支持参数绑定(易SQL注入),

* 适合执行静态 SQL(比如 CREATE TABLE、批量插入固定数据)。

*

* @param db 已打开的数据库连接句柄

* @param sql 要执行的 SQL 语句字符串(可以是多条,用分号分隔)

* - 示例:"CREATE TABLE IF NOT EXISTS user (id INT, name TEXT, age INT);"

* - 示例:"INSERT INTO user VALUES (1, '李四', 30); INSERT INTO user VALUES (2, '王五', 28);"

* @param callback 回调函数(仅查询语句有效,比如 SELECT)

* - 每查询到一行结果,就会调用一次该函数;

* - 无返回数据(比如 INSERT/DELETE)时,传 nullptr 即可;

* - 回调函数格式:int (*callback)(void*, int, char**, char**)

* - void* arg:传入的自定义参数;

* - int col_num:结果列的数量;

* - char** col_values:每列的值(字符串数组);

* - char** col_names:每列的名称(字符串数组)。

* @param arg 传递给回调函数的第一个参数(自定义数据,传 nullptr 即可)

* @param errmsg 指向错误信息字符串的指针(输出参数)

* - 执行失败时,SQLite 会分配内存并存储错误信息到该指针;

* - 必须用 sqlite3_free(errmsg) 释放内存,否则内存泄漏;

* - 使用前定义:char* err = nullptr; 传入 &err。

* @return int 执行结果码

* - SQLITE_OK (0):执行成功;

* - 非0值:执行失败(比如语法错误、约束冲突),errmsg 会存储具体原因。

*

* @使用步骤(无返回数据):

* 1. 定义错误信息指针:char* errmsg = nullptr;

* 2. 编写静态 SQL:const char* sql = "CREATE TABLE user (id INT PRIMARY KEY, name TEXT);";

* 3. 执行 SQL:int ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);

* 4. 检查结果:

* if (ret != SQLITE_OK) {

* printf("执行失败:%s\n", errmsg);

* sqlite3_free(errmsg); // 必须释放!

* }

*

* @注意事项:

* - 执行 SELECT 时也可以用,但回调函数编写较复杂,推荐用「预处理语句 + sqlite3_step」;

* - errmsg 非空时必须调用 sqlite3_free 释放,否则内存泄漏!

*/

int sqlite3_exec(sqlite3* db, const char *sql,

int (*callback)(void*, int, char**, char**), void *arg, char **errmsg);

/**

* @brief 执行预处理语句(核心!获取查询结果/执行增删改)

* @details 预处理语句编译并绑定参数后,通过该函数执行。对于查询语句(SELECT),需要多次调用该函数

* 来获取每一行结果;对于增删改语句(INSERT/UPDATE/DELETE),只需调用一次。

*

* @param pStmt 已编译+绑定参数的预处理语句句柄

* @return int 执行结果码

* - SQLITE_ROW (100):有一行结果数据就绪(仅 SELECT 有效),可以调用 sqlite3_column_* 获取数据;

* - SQLITE_DONE (101):语句执行完成(无更多结果);

* - 非0/100/101:执行失败(比如约束冲突、表不存在),用 sqlite3_errmsg 查原因。

*

* @使用步骤(查询场景):

* 1. 编译预处理语句:sqlite3_prepare_v2(...)

* 2. 绑定参数(如果有):sqlite3_bind_*(...)

* 3. 循环调用 sqlite3_step 获取结果:

* while (sqlite3_step(stmt) == SQLITE_ROW) {

* // 获取每列的数据

* const char* name = (const char*)sqlite3_column_text(stmt, 0);

* int age = sqlite3_column_int(stmt, 1);

* printf("姓名:%s,年龄:%d\n", name, age);

* }

*

* @使用步骤(插入/更新场景):

* 1. 编译预处理语句 + 绑定参数;

* 2. 调用一次 sqlite3_step:

* int ret = sqlite3_step(stmt);

* if (ret == SQLITE_DONE) {

* printf("插入成功!");

* } else {

* printf("插入失败:%s\n", sqlite3_errmsg(db));

* }

*/

int sqlite3_step(sqlite3_stmt *pStmt);

/**

* @brief 从预处理语句的当前结果行中,获取文本类型列的值

* @details 只有当 sqlite3_step 返回 SQLITE_ROW 时,才能调用该函数(否则获取不到有效数据)。

* 列号从 0 开始(和占位符序号相反!务必注意!)。

*

* @param pStmt 正在执行的预处理语句句柄(sqlite3_step 返回 SQLITE_ROW 的 stmt)

* @param iCol 列的序号(从 0 开始!第一列是 0,第二列是 1,以此类推)

* - 比如 SELECT name, age FROM user,获取 name 传 0,获取 age 传 1;

* @return const unsigned char* 文本列的值(需强制转换为 const char* 使用)

* - 返回的指针由 SQLite 管理,不需要手动释放;

* - 如果列值为 NULL,返回 nullptr;

* - 该指针仅在当前 sqlite3_step 有效,下次 step 后失效。

*

* @使用示例:

* // 假设 stmt 执行后返回 SQLITE_ROW,查询的是 name (0列) 和 address (1列)

* const char* name = (const char*)sqlite3_column_text(stmt, 0);

* const char* addr = (const char*)sqlite3_column_text(stmt, 1);

* printf("姓名:%s,地址:%s\n", name, addr);

*/

const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);

/**

* @brief 销毁预处理语句对象,释放所有关联资源

* @details 每个通过 sqlite3_prepare_v2 编译的 stmt,必须调用该函数销毁,否则会导致内存泄漏,

* 甚至导致 sqlite3_close 关闭数据库失败(返回 SQLITE_BUSY)。

*

* @param pStmt 要销毁的预处理语句句柄(可以是 nullptr,函数会容错)

* @return int 销毁结果码

* - SQLITE_OK (0):销毁成功;

* - 非0值:销毁失败(极少出现,一般是 stmt 无效)。

*

* @使用步骤:

* 1. 预处理语句执行完成后,立即调用该函数;

* 2. 销毁后,将 stmt 置为 nullptr,避免误操作;

*

* @示例代码片段:

* sqlite3_finalize(stmt);

* stmt = nullptr; // 置空,防止野指针

*/

int sqlite3_finalize(sqlite3_stmt *pStmt);

相关推荐
慵懒的猫mi1 小时前
deepin UOS AI 助手接入钉钉(DingTalk)配置指南
linux·数据库·人工智能·ai·钉钉·deepin
海山数据库1 小时前
移动云大云海山数据库分页查询性能优化时间:从16s到2ms
数据库·oracle·性能优化·he3db·大云海山数据库
Maverick062 小时前
Oracle PDB 创建
运维·数据库·oracle
左左右右左右摇晃2 小时前
JVM 整理(四) 堆
jvm·笔记
爱折腾的小码农2 小时前
neo4j数据库桌面管理工具
数据库·neo4j
常利兵2 小时前
Room 3.0大变身:安卓开发的新挑战与机遇
android·jvm·oracle
总要冲动一次2 小时前
MySQL 5.7 全量 + 增量备份方案(本地执行 + 远程存储)
数据库·mysql·adb
猿小喵2 小时前
MySQL数据库源码调试
数据库·mysql