- todo: ARC-05越层核验0命中+四领域拆分/ARC-06破环aiShared/FR-D3三子项闭环销账+新增ARC-06b遗留
- 待审查: 登记CR-260616-02(batch29+R-PD-10代码 fc767e1)待审
- DOC-14: df-storage/SQLite-CRUD文档表数V13/V9→V15对齐migrations.rs schema_version=15
3.6 KiB
3.6 KiB
SQLite CRUD 模式
创建: 2026-06-10 | 状态: 初稿
概述
DevFlow 使用 SQLite (rusqlite) 作为本地存储引擎。df-storage 负责 SQLite 连接管理、Schema 迁移和 CRUD 操作。
当前状态
- SQLite 连接管理: 已实现(
Arc<Mutex<Connection>>单连接,非连接池) - Schema 迁移 (V1-V15 累计 14 张表 + 12 索引): 已实现
- CRUD 层: 已实现(
impl_repo!宏 + 17 个 Repo + 向量检索)
设计要点
已实施内容
impl_repo!宏 CRUD — 一次性为各表生成 insert / get_by_id / list_all / query / update_field / update_full / delete(当前 17 个 Repo,非 trait 抽象)- 参数化查询 —
?1/?2占位符 +params![]绑定,防止 SQL 注入 - 列名白名单校验 —
ALLOWED_COLUMNS校验 query / update_field 的动态列名 update_full(record)— 整体更新全可变字段(保留 id 与 created_at)的原子写- 向量检索 — KnowledgeRepo 余弦相似度 top-N(纯 Rust,数据量 <50k 暴力遍历)
仍待实施
- 泛型
Repository<T>trait — 当前用宏非 trait,无统一接口抽象 - 事务支持 — 跨表操作的原子性保证(当前单连接 + Mutex,无显式事务)
- 批量操作 —
insert_batch/update_batch
约定
- ID 字段统一使用
TEXT(UUID v4) - 时间字段使用
TEXT(毫秒时间戳字符串,now_millis_str()返回 String) - JSON 字段使用
TEXT存储 JSON 字符串 - 所有写操作统一返回
df_core::error::Error错误类型:insert 返Result<String>(id),update/delete 返Result<bool>(是否影响行)
update_field 的强制时间戳约束
impl_repo! 宏为各 Repo 统一生成的 update_field(id, field, value) 生成 SQL:
UPDATE {table} SET {field} = ?1, updated_at = ?2 WHERE id = ?3
总是连带刷新 updated_at = now,不可关闭。
适用与不适用
- 适用:内容更新(标题、状态、描述等)——这些变更本就该反映为新近修改时间。
- 不适用:纯元数据标记切换(如归档 archived)。归档是元数据,不应改对话的"最近活跃时间",否则侧栏相对时间会跳变为"刚刚"。
例外模式:专用方法走原生 SQL
对元数据类切换,新增专用方法绕过 update_field,直接写原生 SQL 只动目标列。例如 AiConversationRepo::set_archived:
UPDATE ai_conversations SET archived = ?1 WHERE id = ?2
不带 updated_at。
同类专用原生 SQL 方法(均不调 update_field),按「是否刷 updated_at」分两类:
- 不刷(与 set_archived 同策略,只改目标列):
KnowledgeRepo::set_embedding—UPDATE knowledges SET embedding = ?1 WHERE id = ?2(向量写库,非业务修改)
- 刷(策略相反,刷新时间反映复用行为):
KnowledgeRepo::increment_reuse_count—UPDATE knowledges SET reuse_count = reuse_count + 1, updated_at = ?1 WHERE id = ?2(原子自增 + 刷时间,因复用确属"近期活跃")
取舍:是否刷 updated_at 取决于语义——元数据/内部字段切换不刷(避免污染相对时间),反映业务行为变更的刷。
相关机制
update_full(record):整体更新全可变字段,保留 id 与 created_at,用于 save_conversation 落库对话内容(此时确实要刷 updated_at)。ALLOWED_COLUMNS白名单:update_field / query 的列名经白名单校验防注入。set_archived 因列名硬编码无需白名单。
相关文件
crates/df-storage/src/lib.rs— 存储层入口crates/df-storage/src/migrations.rs— Schema 定义与迁移crates/df-core/src/error.rs— 统一错误类型