`sqlite3_exec` 是 SQLite C API 中一个**高层接口函数**,用于执行一条或多条 SQL 语句,并可选择性地处理查询结果。它封装了底层的编译、执行和结果回调机制,非常适合快速开发和简单操作。
---
## ✅ 函数原型
```c
int sqlite3_exec(
sqlite3 *db, // 数据库连接句柄
const char *sql, // 要执行的 SQL 字符串
int (*callback)(void*, int, char**, char**),// 回调函数(仅 SELECT 时使用)
void *arg, // 传递给回调函数的参数
char **errmsg // 错误信息输出(出错时设置)
);
```
### 返回值:
- `SQLITE_OK (0)`:成功
- 其他值:错误码(如 `SQLITE_ERROR`, `SQLITE_MISUSE` 等)
> ⚠️ 如果执行失败且 `errmsg` 不为 `NULL`,SQLite 会自动分配内存并写入错误描述字符串,需调用者手动释放(用 `sqlite3_free()`)。
---
## 🔹 参数详解
| 参数 | 说明 |
|------|------|
| `db` | 已打开的数据库连接(由 `sqlite3_open` 创建) |
| `sql` | 一串或多个以分号`;`分隔的 SQL 语句 |
| `callback` | 每当有结果行时被调用的函数(只用于 `SELECT` 查询) |
| `arg` | 用户自定义数据,传给 `callback` |
| `errmsg` | 出错时保存错误消息的指针地址 |
---
## 🟡 示例代码:插入 + 查询
```c
#include <sqlite3.h>
#include <stdio.h>
// 回调函数:处理 SELECT 查询的每一行
static int callback(void *data, int argc, char **argv, char **azColName) {
fprintf(stderr, "%s: \n", (const char*)data);
for (int i = 0; i < argc; i++) {
printf(" %s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0; // 返回非零值会中断查询
}
int main() {
sqlite3 *db;
char *err_msg = 0;
const char *sql;
int rc;
// 打开数据库(如果不存在则创建)
rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
// 创建表
sql = "CREATE TABLE IF NOT EXISTS users ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name TEXT NOT NULL, "
"age INT);";
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
// 插入数据
sql = "INSERT INTO users (name, age) VALUES ('Alice', 30); "
"INSERT INTO users (name, age) VALUES ('Bob', 25);";
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
// 查询数据(使用回调)
sql = "SELECT * FROM users;";
rc = sqlite3_exec(db, sql, callback, (void*)"Query Results", &err_msg);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
}
// 关闭数据库
sqlite3_close(db);
return 0;
}
```
### 输出示例:
```
Query Results:
id = 1
name = Alice
age = 30
Query Results:
id = 2
name = Bob
age = 25
```
---
## 🔹 回调函数详解
```c
int callback(void *arg, int argc, char **argv, char **azColName)
```
| 参数 | 含义 |
|------|------|
| `arg` | 就是传给 `sqlite3_exec` 的第四个参数(如 `"Query Results"`) |
| `argc` | 当前行的列数 |
| `argv` | 字符串数组,每个元素是该列的值(文本形式),NULL 值为 `NULL` 指针 |
| `azColName` | 列名数组(如 `"id"`, `"name"`) |
📌 注意:`argv[i]` 可能是 `NULL`,需要判空!
---
## ✅ 使用场景总结
| 场景 | 是否推荐使用 `sqlite3_exec` |
|------|----------------------------|
| ✅ 执行 DDL(CREATE, DROP) | ✔️ 非常方便 |
| ✅ 执行 INSERT/UPDATE/DELETE | ✔️ 简单直接 |
| ✅ 执行 SELECT 并处理结果 | ✔️ 适合小数据量 |
| ❌ 大量数据查询或高性能需求 | ✖️ 推荐使用 `sqlite3_prepare_v2` + `step` 循环 |
| ❌ 绑定参数防注入 | ✖️ 不支持占位符绑定,容易 SQL 注入! |
---
## ❗ 安全警告:SQL 注入风险
```c
// 危险!拼接用户输入
char sql[512];
sprintf(sql, "SELECT * FROM users WHERE name = '%s'", user_input);
sqlite3_exec(db, sql, callback, 0, &err_msg);
```
👉 如果 `user_input = "'; DROP TABLE users; --"`,会导致删表!
✅ 正确做法:使用预编译语句 `sqlite3_prepare_v2` + `bind`
```c
sqlite3_stmt *stmt;
const char *sql = "SELECT * FROM users WHERE name = ?";
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, user_input, -1, SQLITE_STATIC);
while (sqlite3_step(stmt) == SQLITE_ROW) {
// 处理结果
}
sqlite3_finalize(stmt);
```
---
## ✅ `sqlite3_exec` 内部工作流程
```text
[sqlite3_exec]
↓
解析 SQL 字符串(分号分割)
↓
对每条语句调用 sqlite3_prepare_v2
↓
执行(sqlite3_step)直到完成
↓
如果是 SELECT → 调用用户提供的 callback
↓
收集错误信息(如有)
↓
返回最终状态码
```
> 实际上它是对底层 Prepare/Step/Finalize 流程的封装。
---
## ✅ 常见错误码
| 错误码 | 含义 |
|--------|------|
| `SQLITE_OK` | 成功 |
| `SQLITE_ERROR` | SQL 语法错误 |
| `SQLITE_BUSY` | 数据库忙(被锁) |
| `SQLITE_MISUSE` | API 使用不当(如并发访问) |
| `SQLITE_CORRUPT` | 数据库文件损坏 |
| `SQLITE_PERM` | 权限不足 |
可通过 `sqlite3_errstr(rc)` 获取错误文字描述。
---
## ✅ 何时不用 `sqlite3_exec`?
虽然方便,但在以下情况应避免使用:
1. **需要绑定参数**(防止注入)
2. **频繁执行相同 SQL**(应预编译重用)
3. **大数据集遍历**(回调效率低)
4. **需要精确控制执行过程**(如暂停、恢复)
👉 应改用:
```c
sqlite3_prepare_v2()
sqlite3_bind_XXX()
sqlite3_step()
sqlite3_finalize()
```
---
###