# SQLite Deft 内置了 SQLite——无需安装驱动,也无需加载共享库。`sqlite/*` 标准库 函数在脚本二进制、ANSI 二进制和完整 TUI 中行为完全一致。SQLite 始终可用, 无需任何构建标志。 ## 打开与关闭
def db [sqlite/open ":memory:"]               # in-memory database
# ... or ...
def db [sqlite/open "data.db"]                # file-backed

sqlite/close $db
句柄是一个 `native_resource`。关闭它会释放底层的 SQLite 句柄;使用已关闭 的句柄会出错。 ## Exec——DDL 与 DML `sqlite/exec` 运行不返回行的 SQL。返回受更改影响的行数。
sqlite/exec $db "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)"

sqlite/exec $db "INSERT INTO users (name, age) VALUES ('Alice', 30)"
### 参数化 exec 在 SQL 字符串之后以列表形式传入参数。占位符是 `?`:
sqlite/exec $db "INSERT INTO users (name, age) VALUES (?, ?)" @{ "Alice" 30 }
sqlite/exec $db "INSERT INTO users (name, age) VALUES (?, ?)" @{ "Bob"   25 }
数字、浮点数、字符串和 nil 都能正确绑定。无效 SQL 返回 `%Err`——用 `err?` 检查:
set r [sqlite/exec $db "INSERT INTO bogus_table VALUES (1)"]
if [err? $r] { echo "failed: $r~message" }
## Query——SELECT `sqlite/query` 返回以列名为键的映射列表:
def rows [sqlite/query $db "SELECT * FROM users"]
for row $rows {
    echo "$row~name (age $row~age)"
}
# Alice (age 30)
# Bob (age 25)
带参数时:
def adults [sqlite/query $db "SELECT * FROM users WHERE age >= ?" @{ 18 }]
NULL 单元格以 `nil` 返回。 ### `query-one`——单行 `sqlite/query-one` 返回第一个匹配的行,或 `nil`:
def alice [sqlite/query-one $db "SELECT * FROM users WHERE name = ?" @{ "Alice" }]
if $alice { echo "found: $alice~name" }
### `scalar`——单值 `sqlite/scalar` 返回第一行的第一列,或 `nil`:
def count [sqlite/scalar $db "SELECT COUNT(*) FROM users"]
echo "user count: $count"
## 内省
sqlite/tables $db                              # => list of table names
sqlite/columns $db "users"                     # => column metadata list
`sqlite/columns` 返回 `PRAGMA table_info(...)` 中的行。每行包含 `:name`、 `:type`、`:notnull`、`:pk` 等字段。 ## 事务
sqlite/begin $db
try {
    sqlite/exec $db "INSERT INTO users (name, age) VALUES (?, ?)" @{ "Carol" 28 }
    sqlite/exec $db "UPDATE counters SET value = value + 1 WHERE name = 'users'"
    sqlite/commit $db
} catch e {
    sqlite/rollback $db
    echo "rolled back: $e~message"
}
回滚会丢弃自对应 `begin` 以来的一切:
sqlite/begin $db
sqlite/exec $db "INSERT INTO users (name, age) VALUES ('Temp', 0)"
sqlite/rollback $db
# The 'Temp' row is gone.
## 管道与转换器 查询结果是普通列表,因此所有集合操作都可以直接使用:
# Filter and map results
def adults $rows
    |> filter { ($it~age >= 18) }
    |> map { $it~name }
    |> collect

# Aggregate
set total-age $rows |> map { $it~age } |> reduce 0 { $acc + $it }

# Index by name
def by-name [from-entries [map $rows {|r| @{ $r~name $r }}]]
echo $by-name~Alice~age                              # => 30
对于可复用的变换,将其抽成转换器:
def xf [comp
    [filter { ($it~age >= 18) }]
    [map { $r~name }]]

[into @{} xf $rows]
参见集合和 转换器。 ## 用 `runtime/on-shutdown` 清理 长时间运行的脚本和 TUI 应用应注册关闭钩子来关闭数据库句柄:
def db [sqlite/open "data.db"]

runtime/on-shutdown {
    try { sqlite/close $db } catch _e { nil }
}
该模式在 `packages/chat/src/chat.dft` 和 `packages/stdext/src/semantic.dft` 中用于生产环境的数据库持久化。 ## 完整示例
#!/usr/bin/env deft

def db [sqlite/open ":memory:"]

sqlite/exec $db """
    CREATE TABLE posts (
        id    INTEGER PRIMARY KEY,
        title TEXT NOT NULL,
        body  TEXT
    )
"""

sqlite/exec $db "INSERT INTO posts (title, body) VALUES (?, ?)" @{
    "First Post"
    "This is my first post."
}
sqlite/exec $db "INSERT INTO posts (title, body) VALUES (?, ?)" @{
    "Second Post"
    "More content here."
}

def count [sqlite/scalar $db "SELECT COUNT(*) FROM posts"]
echo "Total posts: $count"

def posts [sqlite/query $db "SELECT * FROM posts ORDER BY id"]

# Render via pipeline
$posts
    |> map { " - $it~title" }
    |> each { echo $it }

sqlite/close $db
## 并发说明 SQLite 句柄不能安全地在 OS 线程之间共享。在单个运行时内这不是问题——只有 一个线程会访问该句柄。对于跨运行时访问,可以: - 每个运行时针对同一文件打开单独的句柄(SQLite 通过 WAL 模式处理并发 文件访问),或者 - 使用 `runtime/eval` 将数据库工作分派到唯一的所属运行时。 ## `sqlite/*` 快速参考 | 函数 | 返回值 | 用途 | |---|---|---| | `[sqlite/open ":memory:" \| path]` | 句柄 | 打开 | | `[sqlite/close $db]` | nil | 关闭 | | `[sqlite/exec $db sql ?params]` | 数字 | DDL/DML;受影响的行数 | | `[sqlite/query $db sql ?params]` | 映射列表 | SELECT | | `[sqlite/query-one $db sql ?params]` | 映射或 nil | 第一行 | | `[sqlite/scalar $db sql ?params]` | 值或 nil | 第一行的第一列 | | `[sqlite/tables $db]` | 列表 | 所有表名 | | `[sqlite/columns $db "table"]` | 列表 | 列元数据 | | `[sqlite/begin $db]` | nil | 开始事务 | | `[sqlite/commit $db]` | nil | 提交 | | `[sqlite/rollback $db]` | nil | 回滚 |