# eduwin + SQLite 数据库编程

SQLite 是一个自包含、零配置、嵌入式的 C 语言数据库引擎。eduwin 已将 SQLite 编译进 `eduwin.lib`，学生程序只需 `#include "sqlite3.h"` 即可直接使用，无需额外配置。

---

## 快速开始

```c
#include "eduwin.h"
#include "sqlite3.h"

int main(void)
{
    init_app();

    sqlite3 *db = NULL;
    int rc = sqlite3_open("test.db", &db);
    if (rc != SQLITE_OK)
    {
        alert(L"错误", L"无法打开数据库");
        return 1;
    }

    char *err = NULL;
    sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS users ("
                     "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                     "name TEXT NOT NULL, "
                     "email TEXT)", NULL, NULL, &err);
    if (err)
    {
        alert(L"错误", L"建表失败");
        sqlite3_free(err);
    }

    HWND win = create_window(L"SQLite 测试", 500, 300);
    show_window(win);
    run_app();

    sqlite3_close(db);
    return 0;
}
```

编译命令：

```bash
build.bat
```

只要在 `.c` 文件中 `#include "sqlite3.h"`，link 时自动从 `eduwin.lib` 获取 SQLite 实现。

---

## 核心 API

SQLite 最常用的三个函数：

### 打开/关闭

```c
int sqlite3_open(const char *filename, sqlite3 **ppDb);
int sqlite3_close(sqlite3 *db);
```

- `sqlite3_open` 打开或创建数据库文件（UTF-8 路径）
- `sqlite3_close` 关闭数据库，释放资源
- 返回 `SQLITE_OK`（0）表示成功

### 执行 SQL

```c
int sqlite3_exec(sqlite3 *db, const char *sql,
                 int (*callback)(void *, int, char **, char **),
                 void *data, char **errmsg);
```

最简单的执行方式——不需要回调时传 `NULL`：

```c
sqlite3_exec(db, "INSERT INTO users VALUES(NULL, '张三', 'zhangsan@test.com')",
             NULL, NULL, &err);
```

### 带参数查询（推荐）

```c
/* 根据学号查询用户，返回姓名，未找到返回 NULL */
const char *find_user_name(sqlite3 *db, int user_id)
{
    sqlite3_stmt *stmt = NULL;

    /* 1. 准备 SQL（？是占位符） */
    int rc = sqlite3_prepare_v2(db,
                "SELECT name FROM users WHERE id = ?",
                -1, &stmt, NULL);
    if (rc != SQLITE_OK)
        return NULL;

    /* 2. 绑定参数（第 1 个 ? 绑定 user_id） */
    sqlite3_bind_int(stmt, 1, user_id);

    /* 3. 执行查询 */
    const char *result = NULL;
    if (sqlite3_step(stmt) == SQLITE_ROW)
    {
        /* 4. 读取结果集第 0 列 */
        result = strdup((const char *)sqlite3_column_text(stmt, 0));
    }

    /* 5. 释放语句 */
    sqlite3_finalize(stmt);
    return result;
}
```

流程：**准备 → 绑定 → 单步执行 → 读列 → 销毁**。

`?` 是占位符，从 1 开始编号。多参数时：

```c
/* UPDATE 示例：带三个占位符 */
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db,
    "UPDATE users SET name = ?, email = ? WHERE id = ?",
    -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, name_utf8, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, email_utf8, -1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt,  3, user_id);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
```

始终使用参数绑定而非字符串拼接，可防止 SQL 注入。

### 获取最后插入的 ID

```c
int last_id = (int)sqlite3_last_insert_rowid(db);
```

---

## 字符串编码

SQLite 的 API 使用 **UTF-8** 编码，而 eduwin 使用 **UTF-16（宽字符）**。eduwin 提供两个内置工具函数完成互转：

```c
char *wtou8(const WCHAR *ws);
WCHAR *u8tow(const char *us);
```

- `wtou8`：宽字符串 → UTF-8 字符串（`malloc` 分配内存，用完需 `free`）
- `u8tow`：UTF-8 字符串 → 宽字符串（`malloc` 分配内存，用完需 `free`）

这两个函数已编译在 `eduwin.lib` 中，`#include "eduwin.h"` 即可使用。

用法：

```c
char *sql = wtou8(L"INSERT INTO users VALUES(NULL, '测试用户', 'test@test.com')");
sqlite3_exec(db, sql, NULL, NULL, &err);
free(sql);

const char *name8 = (const char *)sqlite3_column_text(stmt, 1);
WCHAR *name16 = u8tow(name8);
set_text(label, name16);
free(name16);
```

---

## 完整示例：用户管理

`demo\sqlite_demo.c` 是一个完整的用户信息管理程序，演示了增删改查全部操作，可直接编译运行（`demo\build_sqlite_demo.bat`）：

功能包括：

- 列表显示所有用户（ListView）
- 添加用户（姓名、邮箱、电话）
- 双击列表项回填到输入框，修改后保存
- 选中用户后删除
- 实时搜索过滤
