
以下是为这些 SQLite 核心函数编写的**通俗易懂、详细全面的 C 风格注释**涵盖每个函数的功能、参数、返回值、使用步骤、注意事项等新手也能轻松理解 cpp /************************************************************* * SQLite 数据库连接管理核心函数 - 基础中的基础 * 所有操作都依赖数据库连接的正确打开和关闭务必掌握 ************************************************************/ /** * brief 打开/创建 SQLite 数据库连接 * details 这是操作 SQLite 数据库的第一步如果指定路径的数据库文件不存在函数会自动创建一个新的空数据库 * 如果文件已存在则建立与该数据库的连接。后续所有数据库操作查、增、删、改都需要这个连接句柄。 * * param filename 数据库文件的路径C风格字符串 * - 示例1test.db → 当前程序目录下的 test.db 文件 * - 示例2/home/user/data/mydb.db → Linux 绝对路径 * - 示例3C:\\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 类型的指针变量初始化为 nullptrsqlite3* 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 文本的字节长度 * - 传入 -1SQLite 会自动计算长度直到字符串结束符 \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. 编写带占位符的 SQLconst 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); // 第一个 ? 绑定 1001id * sqlite3_bind_int64(stmt, 2, 25); // 第二个 ? 绑定 25age */ int sqlite3_bind_int64(sqlite3_stmt*, int index, sqlite3_int64 value); /** * brief 给预处理语句的占位符绑定文本字符串最常用的绑定函数 * details 用于绑定字符串类型的值比如姓名、地址、描述等支持自定义内存管理方式。 * * param pStmt 已编译成功的预处理语句句柄 * param index 占位符的序号从 1 开始 * param value 要绑定的文本字符串C风格字符串比如 张三、北京市 * param len 字符串的字节长度 * - 传入 -1SQLite 自动计算长度直到 \0推荐使用 * - 传入具体数值只绑定前 len 个字节适用于超长字符串/二进制数据。 * param destructor 内存释放策略决定 SQLite 是否复制/释放字符串 * - SQLITE_STATICSQLite 不复制字符串也不释放内存 → 适合绑定「常量字符串/栈上字符串」 * - SQLITE_TRANSIENTSQLite 会复制一份字符串到内部缓存 → 适合绑定「堆上字符串/临时字符串」 * - 自定义函数传入自定义的释放函数高级用法新手用不到。 * 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. 编写静态 SQLconst char* sql CREATE TABLE user (id INT PRIMARY KEY, name TEXT);; * 3. 执行 SQLint 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); ### 总结 1. **核心流程**打开数据库sqlite3_open→ 编译SQL为预处理语句sqlite3_prepare_v2→ 绑定参数sqlite3_bind_*→ 执行语句sqlite3_step→ 获取结果sqlite3_column_*→ 销毁预处理语句sqlite3_finalize→ 关闭数据库sqlite3_close 2. **关键注意点** - 占位符序号从 1 开始列序号从 0 开始易混点 - 预处理语句必须用 sqlite3_finalize 销毁否则数据库无法关闭 - sqlite3_exec 适合静态SQL无参数预处理语句适合动态SQL带参数防注入 3. **调试技巧**任何函数返回非 SQLITE_OK 时立即调用 sqlite3_errmsg 获取错误信息是排查问题的核心。