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

相关推荐
小陈工3 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
科技小花8 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸8 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain8 小时前
linux个人心得22 (mysql)
数据库·mysql
彧翎Pro8 小时前
基于 RO1 noetic 配置 robosense Helios 32(速腾) & xsense mti 300
前端·jvm
阿里小阿希9 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神9 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员9 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java9 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿9 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb