建站提交历史文章,原文写作时间 2023 年 5 月 18 日前后。

Sqlite 与 C/C++

本篇介绍 Windows SqliteC/C++ 开发的连接方法。

本章节不教 SQL 语法。 如尚未学习 SQL 建议与 SQL 混合食用。

下载

  • 前往 Sqlite 官网 SQLite Download Page 下载 Source Code C 语言源代码。
  • 解压安装包到工程目录,得到 shell.c sqlite3.c sqlite3.h sqlite3ext.h,后面主要用到 sqlite3.csqlite3.h

编译

  • 将源文件拷贝到工程目录下的 include/sqlite 目录。

  • 主代码导入头文件 #include "sqlite/sqlite3.h"

  • 然后使用以下两种方法之一用命令行编译(使用其他编译工具其原理都是基于命令行方式的):

    • 直接编译

      1
      gcc main.c include\sqlite\sqlite3.c -o main.exe -I include  # 注意 sqlite3.c 不能使用 g++ 编译
    • 动态库编译(PS:静态库也可)

      1
      2
      3
      4
      gcc -c -fpic include\sqlite\sqlite3.c -o sqlite3.o
      gcc -shared sqlite3.o -o libsqlite3.dll
      del sqlite3.o # 删除编译中间件, 保留 libsqlite3.dll 即可
      g++ main.cpp -o main.exe -I include -L . -l sqlite3 # 之后编译只要这步即可
      如果使用动态库编译方法, 后面只需要用到 include\sqlite\sqlite3.h 和 libsqlite3.dll,请注意保留这两个文件方便之后使用。
  • dll 动态库必须位于工程根目录,不得重命名。(因为我不会其他方法)

接口

Frequent Interface

仅介绍常用接口,足以满足基本开发需求,更多内容建议自行查看 sqlite3.h 头文件或搜索其他 blog

如果看不懂,别急,后面将提供演示。

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

    1
    2
    3
    4
    5
    6
    打开或创建数据库
    参数:
    filename: 数据库文件名
    ppDb: 数据库对象指针 (二级指针)
    返回值:
    返回 0, 出现异常返回非 0
  • int sqlite3_close(sqlite3 *pDb)

    1
    2
    3
    4
    5
    关闭数据库
    参数:
    pDb: 数据库对象 (一级指针)
    返回值:
    返回 0, 出现异常返回非 0
  • int sqlite3_errcode(sqlite3 *db)

    1
    2
    3
    4
    5
    获取错误码
    参数:
    db: 数据库对象
    返回值:
    返回错误码
  • const char *sqlite3_errmsg(sqlite3 *db)

    1
    2
    3
    4
    5
    获取错误信息
    参数:
    db: 数据库对象
    返回值:
    返回错误信息 (const char *)
  • int sqlite3_exec(sqlite3 *db, const char *sql, int (*callback)(void *, int, char **, char **), void *pArg, char **pErrmsg)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    执行 SQL 语句
    参数:
    db: 数据库对象
    sql: SQL 语句
    callback: NULL 或 回调函数, 下面详细介绍
    pArg: NULL 或 回调函数的第一项参数
    errmsg: NULL 或 传出错误信息 (可用 sqlite3_errmsg 替代, 故无用)
    回调函数: int (*callback)(void *pArg, int nCol, char **cols, char **heads)
    当 sql 为查询 (SELECT) 语句时调用, 每查询一行信息都会调用一次该函数
    参数:
    pArg: 任意参数指针, 由函数外部 pArg 指定传入
    nCol: 数据列数
    cols: 行数据指针
    heads: 标题数据指针
    返回值:
    用户自定义返回值, 正常结束要求返回 0, 如返回非 0, 外部函数将中断并返回错误 4: query aborted
    返回值:
    返回 0, 出现异常返回非 0
    (有点抽象, 建议看 Demo)
  • int sqlite3_get_table(sqlite3 *db, const char *sql, char ***pResult, int *pnRow, int *pnColumn, char **pErrmsg)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    获取 SQL 查询结果
    参数:
    db: 数据库对象
    sql: SQL 语句
    pResult: 结果指针 (三级指针, 顶级用于赋值, 次级用于表示一维列表, 末级表示字符串)
    pnRow: 行数指针 (不含标题)
    pnCol: 列数指针
    pErrmsg: NULL 或 传出错误信息
    返回:
    返回 0, 出现异常返回非 0
    (要求 sql 语句为查询语句, 否则请使用 sqlite3_exec, 我也未尝试会出现什么错误)
  • void sqlite3_free(void *p)

    1
    2
    3
    4
    5
    释放字符串内存, 如 pErrmsg 属于此类
    如果接收了此类内存指针, 一定别忘记释放
    回调函数接收的内存 及 库函数产生的内存会自动释放, 如 sqlite3_errmsg
    参数:
    p: 内存指针
  • void sqlite3_free_table(char **result)

    1
    2
    3
    4
    5
    释放数据表内存, 即 sqlite3_get_table 接收到的数据表
    如果接受了此类内存指针, 一定别忘记释放
    回调函数接收的内存 及 库函数产生的内存会自动释放, 如 sqlite3_exec 的 rollback h
    参数:
    result: 内存指针

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <bits/stdc++.h>
#include "sqlite/sqlite3.h"
using namespace std;

int ret;
sqlite3 *pDB = NULL;
char *errMsg = NULL;

// 演示 sqlite3_get_table 获取表信息并打印
void display() {
char **res;
int nRow;
int nCol;
ret = sqlite3_get_table(pDB, "SELECT * FROM student", &res, &nRow, &nCol, 0);
if (ret) cout << sqlite3_errmsg(pDB) << endl;
int idx = 0; // 注意, 前面提到结果指针表示的是一位数组, 所以我们需要根据 nRow 与 nCol 信息将其作二维解析
for (int i = -1; i < nRow; i++) { // -1 行表示标题
for (int j = 0; j < nCol; j++) {
char *tmp = res[idx++];
if (tmp) // 注意如果为空指针表示表格此处为 NULL
cout << tmp << '\t';
else
cout << "NULL" << '\t';
}
cout << endl;
}
sqlite3_free_table(res); // 别忘了释放内存
}

// 演示 sqlite3_exec 获取表信息并打印
// sqlite3_exec 函数获取表信息时, 每获取一行都会调用一次 回调函数
int callback(void *pArg, int nCol, char **data, char **head) {
// pArg 可以用于传递任意数据类型指针, 从而实现自由传参, 需要时充分利用
if (*(bool *)pArg == false) { // 此处 pArg 用于保证 head 只打印一遍
for (int i = 0; i < nCol; i++) {
if (head[i])
cout << head[i] << '\t';
else
cout << "NULL" << '\t';
}
cout << endl;
*(bool *)pArg = true;
}
for (int i = 0; i < nCol; i++) {
if (data[i])
cout << data[i] << '\t';
else
cout << "NULL" << '\t';
}
cout << endl;
return 0;
}

int main() {
// 连接或创建数据库
ret = sqlite3_open("Test.db", &pDB);
if (ret) cout << sqlite3_errmsg(pDB) << endl;
cout << "Connected successfully!" << endl;

// 创建数据表
ret = sqlite3_exec(pDB, "CREATE TABLE IF NOT EXISTS student(id int PRIMARY KEY, name varchar(255), birth date)", 0, 0, 0);
if (ret) cout << sqlite3_errmsg(pDB) << endl;

// 插入一些数据
ret = sqlite3_exec(pDB, "INSERT INTO student VALUES(1, 'Jamhus Tao', 20041231)", 0, 0, 0);
if (ret) cout << sqlite3_errmsg(pDB) << endl;
ret = sqlite3_exec(pDB, "INSERT INTO student VALUES(2, NULL, 20000230)", 0, 0, 0);
if (ret) cout << sqlite3_errmsg(pDB) << endl;
ret = sqlite3_exec(pDB, "INSERT INTO student VALUES(3, 'Mike', 20000101)", callback, 0, 0);
// 注意, 上方回调函数无效, 因为 "INSERT INTO" 不是一个查询语句
if (ret) cout << sqlite3_errmsg(pDB) << endl;

// 使用 sqlite3_exec 查询并打印
bool status = false;
ret = sqlite3_exec(pDB, "SELECT * FROM student", callback, &status, 0);
if (ret) cout << sqlite3_errmsg(pDB) << endl;

// 使用 sqlite3_get_table 查询并打印
display();

// 清空数据表
ret = sqlite3_exec(pDB, "TRUNCATE TABLE student", 0, 0, 0);
if (ret) cout << sqlite3_errmsg(pDB) << endl;

// 删除数据表
ret = sqlite3_exec(pDB, "DROP TABLE student", 0, 0, 0);
if (ret) cout << sqlite3_errmsg(pDB) << endl;

// 关闭数据库连接
ret = sqlite3_close(pDB);
if (ret) cout << sqlite3_errmsg(pDB) << endl;

return 0;
}

Output:

1
2
3
4
5
6
7
8
9
Connected successfully!
id name birth
1 Jamhus Tao 20041231
2 NULL 20000230
3 Mike 20000101
id name birth
1 Jamhus Tao 20041231
2 NULL 20000230
3 Mike 20000101