SQLite3核心函数与触发器

文章目录

sqlite3_exec

  • 执行无结果查询,用于执行那些不返回数据的 SQL 语句。
  • 作用 :执行一条或多条不返回数据的 SQL 语句(也称为实用程序查询)。它是一个便捷的包装函数,内部封装了 sqlite3_prepare_v2sqlite3_stepsqlite3_finalize 的过程
  • 参数
    1. sqlite3*:一个打开的数据库实例指针。
    2. const char *sql:需要执行的 SQL 语句(UTF-8 编码),可以包含多条语句
    3. int (*callback)(void*,int,char**,char**):回调函数。对于 SELECT 语句返回的每一行数据,此回调都会被调用。如果不需要处理结果集(如执行 INSERT),应传入 NULL
    4. void*:回调函数的第一个参数,用于传递自定义数据
    5. char **errmsg:错误信息。如果执行出错,这里将返回指向错误信息的指针,使用后需要用 sqlite3_free 释放
  • 返回值 :成功返回 SQLITE_OK (0),失败返回其他错误代码
  • 适用场景
    • 数据库的初始化和设置,如创建表、索引、视图
    • 执行 INSERTUPDATEDELETE 操作,尤其是在不需要处理返回结果集时
    • 执行 PRAGMA 语句来配置数据库连接,例如设置日志模式或同步模式
  • 示例
c 复制代码
#include <sqlite3.h>
#include <stdio.h>

int main() {
    sqlite3 *db;
    char *err_msg = 0;
    
    // 打开数据库
    int rc = sqlite3_open("test.db", &db);
    
    if (rc != SQLITE_OK) {
        fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
        return 1;
    }
    
    // 执行创建表的 SQL
    char *sql = "CREATE TABLE IF NOT EXISTS Students(Id INT, Name TEXT);";
    rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
    
    if (rc != SQLITE_OK) {
        fprintf(stderr, "SQL 错误: %s\n", err_msg);
        sqlite3_free(err_msg);
    } else {
        fprintf(stdout, "表创建成功\n");
    }
    
    sqlite3_close(db);
    return 0;
}

sqlite3_prepare_v2

B["准备语句
sqlite3_prepare_v2()"]

复制代码
B --> C["准备语句对象<br>sqlite3_stmt*"]
C --> D{"是否有参数<br>占位符 (?)"}

D -- 是 --> E["绑定参数值<br>sqlite3_bind_text(),<br>sqlite3_bind_int()等"]
E --> F["执行/求值<br>sqlite3_step()"]

D -- 否 --> F

F --> G{"返回值"}
G -- "SQLITE_ROW" --> H["提取数据<br>sqlite3_column_xxx()"]
H --> F

G -- "SQLITE_DONE" --> I["销毁语句<br>sqlite3_finalize()"]

G -- "SQLITE_ERROR<br>或其他错误" --> I -->
  • 编译准备语句。是执行参数化查询和获取数据的基础,将SQL 文本编译成字节码,并返回一个用于后续操作的语句对象
  • 作用 :将 SQL 语句编译成准备语句(prepared statement)对象。这个对象将被用于后续执行。_v2 版本提供了更好的错误处理和扩展功能 。
  • 参数
    1. sqlite3 *db:数据库实例指针。
    2. const char *zSql:要编译的 SQL 语句(UTF-8)
    3. int nByte:SQL 语句的最大字节长度。通常传入 -1,表示函数自动计算到字符串终止符。
    4. sqlite3_stmt **ppStmt:输出参数,指向准备语句句柄的指针
    5. const char **pzTail:如果输入的 SQL 字符串包含多个语句,此参数将被设置为第一条语句结束后的下一个语句的起始位置。
  • 返回值 :成功返回 SQLITE_OK
  • 适用场景
    • 任何需要执行 SELECT 查询并处理结果集的场景
    • 当需要重复执行相同的 SQL 语句(如循环插入)时,预编译可以显著提高性能
    • 防止 SQL 注入的必要步骤,因为它允许你将数据与 SQL 逻辑分离

sqlite3_bind_text

  • 绑定字符串参数。在 sqlite3_prepare_v2 之后,可以使用这一系列函数将值绑定到 SQL 语句中的占位符上
  • 作用 :将文本(字符串)值绑定到准备语句中的某个 SQL 参数(占位符)上。常见的占位符形式有 ??NNN:AAA$AAA@AAA
  • 参数
    1. sqlite3_stmt*:准备语句对象指针
    2. int:要绑定的参数的索引。最左边的参数索引为 1
    3. const char*:要绑定的文本值(UTF-8 编码)
    4. int n:文本值的字节长度。可以传入 -1,表示函数自动计算直到遇到第一个 \0 终止符。如果传入非负值,则表示要使用的前 n 个字节
    5. void(*)(void*):最后一个参数是一个析构函数,用于告诉 SQLite 如何处理第 3 个参数传入的字符串内存
      • 传入 SQLITE_STATIC:表示 SQLite 不需要管理这块内存(例如,字符串是静态分配的,或者在当前作用域内保证不会被释放)
      • 传入 SQLITE_TRANSIENT:告诉 SQLite 在函数返回前需要复制 这份数据。因为你的原始数据(如 std::string 或局部字符数组)可能在 SQLite 使用完之前就被销毁了
  • 返回值 :成功返回 SQLITE_OK
  • 适用场景
    • 防止 SQL 注入 :永远不要直接拼接用户输入的字符串来构造 SQL,而应该使用占位符和 sqlite3_bind_xxx 函数
    • 插入或更新包含文本、BLOB 等复杂类型的数据
  • 示例(结合 prepare 和 step)
c 复制代码
// 假设 db 已打开,现在要插入数据
sqlite3_stmt *stmt;
const char *sql = "INSERT INTO Students (Id, Name) VALUES (?, ?);";

// 1. 准备
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) { /* 处理错误 */ }

// 2. 绑定数据
int student_id = 1;
const char *student_name = "张三";

sqlite3_bind_int(stmt, 1, student_id);               // 绑定整数
sqlite3_bind_text(stmt, 2, student_name, -1, SQLITE_STATIC); // 绑定文本

// 3. 执行
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) { /* 处理错误 (如违反约束) */ }

// 4. 销毁语句对象,释放资源
sqlite3_finalize(stmt);

sqlite3_step

  • 执行准备语句。真正驱动 SQLite 执行字节码的函数
  • 作用:执行或继续执行一个准备语句
  • 返回值 :根据语句类型和当前状态返回不同值
    • SQLITE_ROW:当执行的语句是 SELECT 时,如果还有下一行数据可用,则返回此值。应用程序应调用相应的 sqlite3_column_xxx() 函数来提取数据,然后再次调用 sqlite3_step() 获取下一行。
    • SQLITE_DONE:语句执行成功完成。对于 SELECT,表示已到达结果集的末尾。对于 INSERTUPDATEDELETE,表示操作成功完成。
    • SQLITE_BUSY:数据库引擎无法获取锁。通常可以稍后重试。
    • SQLITE_ERROR:发生运行时错误。
    • SQLITE_MISUSE:例程使用不当。
  • 适用场景
    • 所有准备语句的执行阶段,无论是 SELECT 获取数据,还是 INSERT/UPDATE/DELETE 完成操作

sqlite3_mprintf

  • 安全格式化 SQL 字符串。是一个 SQLite 自定义的 printf 函数,用于安全地创建 SQL 字符串,它会自动处理字符串转义问题,是防止 SQL 注入的另一道防线
  • 作用 :类似于标准 C 库的 sprintf,但它分配内存,并提供了非标准的格式占位符(如 %q%Q)来安全地处理字符串中的单引号 。
  • 参数 :格式字符串和可变参数列表。标准的 %d, %s 等都支持。
    • 特殊格式符
      • %q处理单引号 。它会将输入的字符串中的每一个单引号(')替换为两个单引号(''),然后用单引号将整个字符串包裹起来。这确保生成的字符串可以安全地用于 SQL 语句
      • %Q处理单引号和 NULL 。它的作用和 %q 类似,但如果对应的参数是 NULL 指针,它会生成字符串 NULL(不带引号)
      • %w:处理双引号标识符(如列名、表名),它会将字符串用双引号括起来,并正确处理其中的双引号
  • 返回值 :返回一个指向新分配的字符串的指针,该字符串包含了格式化后的结果。使用完毕后,必须用 sqlite3_free() 释放内存
  • 适用场景
    • 需要动态构建 SQL 语句(例如,动态的表名或列名,这些不能用参数占位符 ? 代替)时
    • 作为 sqlite3_exec 的辅助工具,快速生成安全的 SQL 命令字符串。
  • 示例
c 复制代码
// 假设用户输入了名字 "O'Malley"
char *user_input = "O'Malley";

// 使用 %q 来安全地构建 SQL
char *sql = sqlite3_mprintf("INSERT INTO Employees (name) VALUES (%q);", user_input);
// 生成的 sql 字符串将是: "INSERT INTO Employees (name) VALUES ('O''Malley');"
// 注意单引号被正确处理了,不会破坏 SQL 结构。

// 执行 sql
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
// ... 处理执行 ...

sqlite3_free(sql); // 释放 sqlite3_mprintf 分配的内存

触发器

  • 触发器(Trigger)是数据库的一种高级功能,允许你在某个表上发生特定事件(如 INSERTUPDATEDELETE)时,自动执行一段预定义的 SQL 代码
  • 作用:实现数据完整性和业务逻辑的自动化。例如,自动更新"最后修改时间"字段、记录数据变更日志、或进行级联更新
  • 创建语法
sql 复制代码
CREATE TRIGGER trigger_name
{BEFORE | AFTER} {INSERT | UPDATE | UPDATE OF column_name | DELETE}
ON table_name
BEGIN
    -- 要执行的 SQL 语句
    -- 可以引用 NEW.column 和 OLD.column
END;
  • 关键概念
    • BEFORE / AFTER:指定触发器是在触发事件之前还是之后执行。
    • NEW :对于 INSERTUPDATE 操作,NEW.列名 引用的是将要插入或更新后的新值。
    • OLD :对于 UPDATEDELETE 操作,OLD.列名 引用的是被修改或删除前的旧值。
  • 适用场景
    1. 数据审计/日志记录:自动记录谁在什么时候修改了哪些数据。
    2. 数据验证:在插入或更新数据前,检查数据的合法性(例如,确保年龄不小于0)。
    3. 派生数据更新:当一个表中的数据改变时,自动更新另一个表中的汇总数据。
    4. 级联操作:实现外键之外的复杂级联更新或删除逻辑。
  • 示例(记录更新日志)
sql 复制代码
-- 创建一个日志表
CREATE TABLE StudentLog (
    LogId INTEGER PRIMARY KEY,
    StudentId INT,
    OldName TEXT,
    NewName TEXT,
    UpdateTime DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 创建一个触发器,当 Students 表的 Name 被更新时,记录日志
CREATE TRIGGER log_student_name_update
AFTER UPDATE OF Name ON Students
BEGIN
    INSERT INTO StudentLog (StudentId, OldName, NewName)
    VALUES (OLD.Id, OLD.Name, NEW.Name);
END;
  • 当执行 UPDATE Students SET Name = '李四' WHERE Id = 1; 时,上面的触发器会自动向 StudentLog 表中插入一条日志记录,详细记录了这次变更
相关推荐
Yungoal1 天前
SWOT、版本号递增、静默降级
sqlite
Mr数据杨2 天前
【Dv3Admin】Django一键配置权限规则
python·django·sqlite
bigcarp3 天前
Roundcube Webmail + sqlite
数据库·sqlite
AI成长日志4 天前
【实用工具教程】数据库基础操作实战:SQLite/MySQL连接、CRUD操作与查询优化
数据库·mysql·sqlite
iMingzhen4 天前
不想引入 Redis,我用一张 SQLite 表实现了消息队列
数据库·redis·ai·sqlite
白藏y5 天前
【数据库】SQLite的基础使用
数据库·sqlite
搜佛说5 天前
RocksDB, SQLite, TDengine Edge, LiteDB与sfsDb选型
物联网·edge·sqlite·边缘计算·时序数据库·iot·tdengine
竹林8186 天前
从数据混乱到丝滑管理:我在Python项目中重构SQLite数据库的实战记录
python·sqlite
Yungoal7 天前
C++链接并操作嵌入式数据库SQLite
数据库·c++·sqlite