Files
ChuSQL/.agents/context/design-decisions.txt
T
ChuNa7a 6996e19694
build / build (push) Blocked by required conditions
tests / build (push) Waiting to run
tests / tests (push) Waiting to run
修bug,完善功能
2026-10-16 23:52:12 +08:00

260 lines
31 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ChuSQL 当前设计决策
进度见 progress.txt,限制见 known-issues.txt,用户操作见 docs/。
## 架构与接口
### 文档与后续方向
- 用户文档按主题记录于 docs/commands.md、docs/types.md、docs/architecture.md 和 docs/install.md;只读范式入口已提供。
后续完善绑定后的中间表示(bound IR)与 DOMAIN,扩展物化资源控制、递归查询,并按实测减少行版本发布与在线整理的开销;规划不代表现有能力。
- 范式迁移首版保留来源,拒绝 NULL、重复行与依赖不保持;发布目标须验证约束、完整重建并使用独立原子结构 WAL 组,不能复用禁止 DDL 的会话事务拼接执行。
- 发布真实性规划采用 TUF(用于验证软件更新元数据的框架)与 Ed25519 签名;初始 root 信任元数据必须可信取得,离线验证不证明最新版本。操作系统原生代码签名另行接入;这些签名能力尚未实现。
### 组件边界与分层
- Web / CLI → server TCP → Haskell 引擎 → FFI → Rust 存储;server 独占数据目录,Haskell 库静态链接,存储为 cdylib。
- 引擎按 AST → 语义检查 → RelOp → 优化 → 执行分层;语义与优化只读 schema,表存在性以 catalog 为准,索引由存储管理。
- JSON 使用显式稳定标签,FFI 提供 version / open / close / request / maintenance / free / last_error;Web 不本地执行 SQL。
### 类型语义的职责
- 兼容值与类型语义集中于 Core.Model,运行时类型与版本化编码集中于 Core.Runtime,函数注册与绑定集中于 Engine.Runtime;旧运算符保留 Engine.Builtin,错误分类集中于 Engine.Error。
- 比较与赋值兼容性分开,整数与整值浮点统一相等和哈希,SQL NULL 比较走三值逻辑;DML 的 CHECK 随写计划一次绑定,结构变更对新 schema 重新绑定。
### 写入求值与约束
- 写表达式和 CHECK 复用查询的列槽位绑定及行布局整理,避免按物理返回顺序取错值;UPDATE 在执行前按 schema 一次绑定目标槽位及列定义,按槽位校验、转换并覆盖单个值。目标不存在或歧义明确失败,不保留缺失类型时直接写入的兜底;保留逐项赋值、全部赋值后的 CHECK 及未命中行不求值。
- INSERT 目标清单拒绝重复列,合法目标在本次执行中一次映射到 VALUES 槽位;行构造保留 schema 顺序及原输入表达式求值顺序,默认值、自增、显式 NULL 和整批校验沿用既有语义。缺少已绑定输入值明确失败,不把它作为省略列处理。
- UPDATE 在全部行求值及 CHECK 成功后、调用存储写入前,复用 INSERT 的 NOT NULL 和 UNIQUE / PRIMARY KEY 检查;唯一性以最终整表结果判断,多个 NULL 不冲突。约束失败不发布部分行修改,事务快照保留并允许修正后继续提交。
- NOT NULL 和 UNIQUE / PRIMARY KEY 检查使用 schema 位置绑定列,缺值或位置不符明确失败,不用 mapMaybe 跳过缺列。
INSERT 的已有行与经引擎重写 schema 的 ALTER 输入行在约束边界按原 schema 整理;保留列级错误顺序、NULL 去重规则和数值规范化比较。
### 写计划与结构守卫
- 写语句及已有表 DDL 使用仅保存 schema 的 BoundWrite,复用查询的依赖验证规则;收集目标、嵌套查询和传递 DOMAIN,新增列或改类型包含新声明 DOMAIN。
先以只读 validate_write 确认写守卫能力,再发送实际写入;存储按库名排序取门,在目标库独占门及其他依赖共享门内复查后执行,不改变单次请求之间的数据隔离语义。
- BoundWrite 的 DML 动作保存 INSERT 输入槽位、UPDATE 赋值目标、DELETE 布局和预绑定 CHECK / 非空 / 唯一性约束,执行期不再重新解析或按名称绑定目标;自动编号仍从当前行集求起点,唯一性仍按最终行集校验,保持逐项赋值及未命中行不求值。
- CHECK 定义与执行共用布尔条件、函数和槽位绑定;聚合及子查询拒绝。结构变更先按新 schema 校验保留约束、默认值及键能力,空表不跳过;CHECK 文本未采用独立列身份,改名不自动重写,失效引用明确拒绝。
## 查询与优化
### 优化与成本选择
- 优化保持结果语义和可观察行序;相关外层列不能被投影删除,LEFT JOIN 只下推安全的左侧谓词。
- schema 携带行数、distinct、索引和直方图,缺统计时保守退回原计划。范围成本使用 16 桶等宽直方图,无直方图时按四分之一估算。
- 内连接从各起点贪心选序,仅在严格更省且行序不可观察处重排;统计不足或不安全条件时不改写。
### 名称与作用域
- 表 / 列限定名在语义层按作用域及唯一候选解析,别名优先;会话将当前库限定 DDL 转为裸名,拒绝跨库目标,库名随请求传递。
- 相关子查询的语义列环境按词法层逐层查找,保留最近层的列及类型,不把不同外层并成一张表;本层歧义不向外回退。JOIN ON 和外部引用收集使用同一外层环境,来源检查失败明确传播。执行期仍使用既有扁平外层行布局。
- SELECT 在引擎入口直接进入 BoundQuery,避免规范化引用再次作为原始来源名解析;二段限定名被最近层同名来源遮蔽,该来源缺列时不回退。裸列仍按列可见性逐层查找,已解析引用的类型推导不重新解释来源遮蔽。
### 列槽位与重绑定
- 绑定后的子查询保存父布局中的局部或外层列槽位,取值与复用缓存键共享该绑定;诊断名称相同不表示位置相同。重绑定不把外层引用转为局部引用,布局失配和引用清单失配明确返回错误;原始关系执行 API 保留未绑定 Subquery 的名称入口。
- 已绑定列重绑定时保留仍匹配名称的有效位置,原位置不匹配仅允许唯一同名列重定位;歧义或负位置拒绝。外层槽位保留相对位置并更新局部宽度;独立列身份未实现,结构改写仍须在绑定前完成,不能凭同名槽位识别重排后的不同列。
- 相关子查询保留已解析外层限定名;求值绑定补回单表物理列的限定名,绑定去重与扫描投影使用同一列值,库限定引用仍校验 schema 来源。
### 子查询执行与复用
- JOIN 条件按子查询外层引用的值缓存替换后的表达式;首次遇到行对时才求值,每个行对仍计算普通条件,保留输出顺序、空输入和错误返回。
- 半连接仅重写独立 IN 或单表唯一等值相关 EXISTS 的单独过滤谓词;键按两侧布局分别绑定,输出只保留原左行。独立 IN 沿用物化身份,NOT IN 感知右侧 NULL,反 EXISTS 保留空值键;大数混合比较退回原比较语义。
复杂条件、可能报错的相关投影和相关聚合不重写,空左侧不执行右侧,保持完整右侧求值;空集 IN 为假、NOT IN 为真(含 NULL 左值),非空集合保持三值逻辑。
- 每次查询执行用独立状态物化成功行集,子查询按已解析定义和具名外层绑定值共享,原始名称调用保守包含整个外层行;派生表按执行子树和外层行缓存,先共享再应用别名。各消费者独立处理标量基数与三值逻辑,失败仍明确返回,保持既有求值顺序;不跨执行保留,也不提供整条查询快照或磁盘溢出。
## 存储提交前校验
- WAL 提交组落盘后、首次应用前进入 recovery_required;任何应用、刷盘或检查点错误都保持该状态,完整落盘与截断成功后才解除。
- 普通提交组在分配对象编号及写 WAL 前,使用 HeapTable::encode_row 检查全部载荷行;单写、批写、整表替换、事务及结构重写共用此边界,避免可预见的行大小错误在提交后触发恢复封闭。账号专用 WAL 路径保留原有账号行预校验;不改变页格式与已有恢复顺序。
- 索引键容量共用 DiskBTree::check_key,存储提交预检复用堆表实际键编码;结构替换只检查保留的内置 id 索引,删列重写排除已移除索引。建索引在写 WAL 前扫描目标列校验;堆表插入、建索引和整表替换在修改数据前检查键长,不用截断键或跳过错误降级。
- 新插入 id 的冲突预检复用 row_key,在同库写门内按 WAL 操作顺序维护键计数;未涉及的现存键按需查询 id 索引,不为单次插入全表扫描。
删除键在一条操作内去重且每键只减一,保留旧重复 id 的首行删除规则;整表替换重建预检基线,沿用其兼容重复 id 的行为。预检不改变恢复重放与原有页格式。
- 目录预演覆盖普通写入可能触发的扩列及结构重写的载荷行,列推导与版本递增共用 Catalog::ensure_columns;只在结构操作或输入可能扩列时克隆目录。
按行及操作顺序累计变化,在写 WAL 前拒绝版本耗尽,保留同一行多列只递增一次及未定型 NULL 不扩列的规则;已有列的数据写入不因版本达到上限而被拒绝。
- Request::valid_columns 检查所有写入载荷的顶层字段名及事务内行,存储表名与列名字符集共用 valid_storage_name;保持已有非空 ASCII 字母、数字及下划线规则。
只在请求边界约束新载荷,不递归检查值内部键,不对旧目录、堆行及 WAL 重放加新名称限制,也不静默改名或丢字段。
## 查询来源与排序
- FROM 是来源树,派生表必须带别名且不能引用外层,输出取裸列名,重名报错;未定型 NULL 保留独立查询推导类型,不能声明为存储类型。
- ORDER BY 优先匹配唯一输出别名,再查来源列;只将排序表达式提前计算,非排序投影保持在 LIMIT 后求值。CTE 列清单同步重命名内层排序别名。
- ORDER BY 输出序号保留到计划生成,不转换为诊断名称;普通查询选定对应投影表达式,DISTINCT 按去重结果槽位排序,重复别名不丢失位置。名称排序仍拒绝歧义,序号不作为相关子查询自由列,排序键遵守类型能力、分组及 LIMIT 求值时序。
- 派生表从透明算子与 schema 统计估算行数,缺统计或表达式可能报错时不重排;投影下推保留计算项,避免删除错误。内存存储提供实时行数及封顶去重统计。
### 数值与子查询语义
- DECIMAL(p,s) 使用有限浮点、ties-to-even 舍入和舍入后溢出检查,要求 1 <= p <= 308、0 <= s <= p;Haskell 与 Rust 写入入口统一边界,日志记录规范化值。
- CTE 解析期展开为派生表,只接受 SELECT。相同执行子树在一次查询中物化复用;不同投影或优化后的子树仍分别执行,不支持递归或显式物化语法。
- 比较及 ANY / ALL 使用独立 AST 节点,量词复用三值逻辑,空集 ANY=false、ALL=true。
- 子查询在 Filter / Compute 中按外层绑定分组复用;适用的独立 IN 与简单等值相关 EXISTS 使用半连接或反连接,复杂条件保留原路径。扫描按页分片,至少 1000 行、最多 8 线程,结果保序,safe FFI 释放执行能力。
## 会话、事务与存储
### 会话快照与差分
- 每连接独立会话及工作库;短锁取得库句柄后执行,每库读写门控制可见性,表锁保护页,在用库不强行删除。
- BEGIN 物化当前库快照,事务内走内存执行,COMMIT 提交相对基线差分,ROLLBACK 丢弃快照。
- COMMIT 先跳过数据未修改的表,整数 id 用 IntMap / IntSet 查找差异与冲突;保留原行顺序及重复 id 的首行查找语义,冲突检查和提交操作共享一次差分。
- SQL 数据语句和提交使用共享全局门与共享库门;同库最终校验和发布使用单独的短提交门。自动提交也走事务计划、行锁和版本复查,保持失败不部分发布。权限与 DDL 全局独占,原始管理员存储写入通过版本变化参与冲突检查。
- 有整数 id 时按键提交及检查写写冲突,无整数 id 时整表比对;未修改表不回写,冲突保留事务并返回 serialization_failure。
- COMMIT 在短提交门内预演实际事务操作,并复用引擎的领域 / CHECK / 非空 / 唯一性规则校验合并最终行集,避免不同整数主键绕过业务唯一约束;校验失败不写 WAL,事务保留。内存预演与存储一致,每个去重删除键只删除首个旧重复 id 行。
- 可选 BEGIN [TRANSACTION] ISOLATION LEVEL SERIALIZABLE / START TRANSACTION ISOLATION LEVEL SERIALIZABLE 保留整库快照,在写事务提交时复查读写语句全部表的 schema 和行集,包含子查询与幻行;按表保守拒绝冲突,默认 BEGIN 的不同行差分合并不变。保存点恢复语句列表,也恢复读依赖;只读事务不因数据变化拒绝提交。
- 保存点保存不可变快照引用,同名取最近一层,回退 / 释放丢弃之后的点,结束清空;事务命令由会话层执行。
### 日志与恢复
- WAL 先记录操作及提交标记,再应用、刷数据与 catalog、写检查点、截断;仅重放已提交且超过检查点的记录。
- WAL 帧保留长度、LSN、类型码、表名及 JSON payload,兼容旧记录;恢复按内容、schema 和文件存在性判断幂等。
- 检查点按库 LSN,前缀截断用同目录临时文件原子替换,未提交尾部直接截短;失败保留 WAL 并进入 recovery_required。
- 检查点采用同目录临时文件替换,长度必须为 8 字节;完整 WAL 帧损坏明确报错,尾部不完整帧保留撕裂处理。刷盘或检查点失败后封闭请求,重启重放已提交组。
### 统计与页面整理
- distinct 在封顶前按值引用计数维护,列封顶不清除其他列状态;直方图独立累计与递减,桶重映射导致删除目标为空时扣最近非空桶以保持总量。rebuild_stats 先扫描数值范围,再按固定边界计数,消除重建过程的桶重映射偏差并保持输入顺序无关。
- 表 / 索引 DDL 进 WAL,删表不重取已移除句柄;库建删不进 WAL,删除以墓碑目录收敛。
- 自动页内 compact 保持槽号及索引位置,死空间达到四分之一时触发;显式 compact 经 WAL 按原扫描顺序打包存活行,回收跨页空洞和隐藏列字节,并重建索引位置。
## 身份、权限与引导
### 对象编号
- 对象标识使用数据库编号与库内表编号组合;数据库编号从 system catalog 的对象高水位分配,表编号从本库高水位分配,删除不降低计数器。对象解析只读取 catalog。
- 建表编号在 WAL 组写入前分配,随建表记录提交;目录应用时推进高水位,恢复使用日志编号。只有建表组复制操作列表,普通数据提交沿用借用列表。
### 身份与角色
- 用户是可登录角色,共用名称空间;identities 保存属性、时间、稳定编号及版本,users 只存编号和口令哈希,编号不复用。
- 最高权限按身份属性判断,不按名称、不继承;默认用户 LOGIN、角色 NOLOGIN,两者 NOSUPERUSER / ENABLED。保护最后一个启用的登录管理员。
- 仅旧格式迁移按管理员参数识别初始最高权限;旧账号保留信息,角色补编号,重名拒绝;身份、凭据及删除清理用同一 Accounts WAL。
- server 判定权限,目录封装于 Server.Catalog;角色成员取向上闭包,写入拒绝环。GRANT role TO member 表示 member 继承 role 权限。
### 对象授权与会话失效
- members 保持身份成员目录;对象授权存于受保护 __system_object_acl,使用稳定对象与身份编号,保存所有者、授予者和 grant option。CONNECT / CREATE 限数据库,四项数据权限限表或单库表范围;ALL 按作用域展开。
- PUBLIC 默认只提供业务数据库 CONNECT,可逐库撤销;CREATE 建表登记创建者所有权,数据库所有权不隐含他人表的数据权限。启用角色可继承所有权,不继承 SUPERUSER。
- 授权链从独立管理来源及所有权展开固定点;撤销清理无来源下游,保留独立来源,循环不能自持。禁用保留所有权和管理来源授权,停止权限贡献并清理失效下游。
- 旧名称授权仅迁移一次到现存稳定对象;旧全局 * 固定为当时现存表,缺失对象或身份清理,旧记录无授予者时使用管理来源。
- 权限快照按字符分块存储,比较旧快照后以同一 WAL 组替换;跨页及 Unicode 不受单行上限限制。普通存储入口隐藏并拒绝访问权限快照表。
- 服务端共享公平读写锁排序查询与权限变更;事务提交复查身份、权限和稳定对象。建表前写入创建者声明,启动时依据现存对象恢复所有权或清理未完成声明。
- 口令 / 属性推进 revision,权限变更预先展开受影响身份;TCP 只标记连接失效,下一请求回 unauthorized 后关闭,避免 Windows 阻塞句柄异步中断;登记在 finally 清理。
### 系统目录与引导
- 系统表统一 __system_ 前缀并分层保护,内部目录按白名单访问;旧名按文件先、catalog 后迁移,崩溃后可重复收敛。
- 安装先 bootstrap:建立 system、默认 root(LOGIN / SUPERUSER / ENABLED)、18 个内置名称,再起服务;引导请求不开放 TCP。
- 清单编入引导并与解析器测试比对;system.__system_types 保存名称、基础表示、参数能力、编号。重复引导保留口令并补齐类型,冲突或身份降权失败;类型建表与写入用同组 WAL。
### 本机 sudo 认证
- sudo 认证用独立 allow_sudo_auth 属性和显式 sudo_auth_user 映射;默认关闭,只有 SUPERUSER 能管理,不继承、不隐含权限。目录管理者不得管理启用该属性的身份。
- 选择操作系统保护的随机本机凭据与 TCP 一次性 HMAC 挑战,以复用三平台既有会话协议;Unix 只信任有效 UID 0,Windows 只信任提升 Administrators 令牌。双方校验内核回环地址,不接受客户端自报 OS 用户或 SUDO_USER。
- 本机凭据只存放于系统保护的端口专属目录,原生入口校验提升身份;实例重启重新生成,现存目录拒绝覆盖。此方案要求服务端也提升运行,以保证创建凭据时的操作系统保护边界。
- 挑战绑定账号、连接及实例,60 秒过期并在每次尝试时消费;证明常量时间比对,凭据不经网络发送。成功登录复用身份版本、登录时间及连接失效机制,审计不含凭据或证明。
- sudo 属性由 Accounts WAL 持久化,旧目录缺列触发身份快照重写,旧记录缺属性默认 false,已有的错误类型明确拒绝。
## 范式分析
- 范式检查采用阿姆斯特朗属性闭包与子集枚举,候选键声明转为决定全关系的依赖;完整覆盖声明与领域原子性分别控制可证明结论,不从样本推断。二元 BCNF 建议按违反依赖无损拆分,投影依赖之并用于验证依赖保持。
## 自定义类型
### DOMAIN 定义与变更
- DOMAIN 命名基础类型,列编码 domain(名称,基础类型) 保留引用,值及索引沿用底层语义。领域目录的 base 列保存类型、可空性与默认值,check_名称 列保存具名条件,旧的仅基础类型定义继续可读;未改变值、页或 WAL 编码版本。
- DOMAIN CHECK 在独立 VALUE 槽位绑定,拒绝聚合及子查询,TRUE / UNKNOWN 通过。领域继承全部 CHECK 和非空限制,默认值按列、最近领域、基础领域选取;继承属性只在绑定时合成,不写回列声明。
- ALTER DOMAIN 在库读门内取完整定义及数据快照,引擎校验传递依赖、默认值和已有行;库身份与 WAL 检查点组成精确令牌,在写门内复查后走 ReplaceSchema WAL。
并发提交返回 serialization_failure;复用已有恢复与结构版本,不引入通用结构事务。创建依赖领域的表 / 领域也在发布点验证结构守卫;事务提交复查修改表及传递领域依赖。
- 各库 __system_domain_<名称> 空目录表的 base 列保存定义,复用 catalog / DDL WAL;普通 SQL 隐藏并保护目录。
- 嵌套只引用当前库已有类型,语义层从 schema 递归解析并拒绝循环,存储写锁内逐层校验;删除检查类型间及间接列依赖。
- 类型 DDL 要求管理员,事务内禁止。DOMAIN 已支持默认值、非空与 CHECK 约束;暂不引入自定义函数 / 运算符。
### 运行时类型与表达式
- 运行时类型使用稳定构造器编号与结构化 TypeId,逻辑类型与 PhysicalType 分离;递归值使用版本 1 标签编码,读取时校验类型、形状及载荷。
- TypedExpression / TypedPlan 仅使用 schema 绑定,SQL BoundQuery 通过既有 RelOp 执行并按输出位置验证;输出类型保留嵌套 DOMAIN 名称,基础日期、时间戳及 Blob 保留兼容类型,函数结果不继承参数领域。
- TypedExpression 列引用保存输入 schema 槽位与诊断名称;独立求值要求行顺序匹配,TypedPlan 扫描边界校验并按 schema 排序,每层投影绑定到子计划输出。
SQL RelOp 在优化后绑定表达式列槽位,投影单向转为 Compute,排序/分组转为 BoundSort/BoundAggregate,存储结果按算子布局排列;子查询局部列在前、外层绑定在后。
绑定算子不参与后续投影下推;独立作用域身份仍待迁移。
- SQL 局部引用使用 ColumnSlot,外层引用使用 OuterColumnSlot 的相对位置及局部布局宽度。重新绑定外层引用只查询外层布局,避免同名局部捕获;语义输入拒绝执行槽位。外层行仍扁平化,多级词法作用域/来源编号另行迁移。
### 查询依赖的结构守卫
- BoundQuery 执行前重新取得 schema 并比较依赖对象身份、schema 版本及列定义,依赖覆盖全部嵌套子查询及来源列的传递 DOMAIN 定义。
IPC 将依赖守卫限定在本次执行环境;扫描、分片、点查及范围读均在相关库读锁内复查全部依赖后读取,跨库读锁按库名排序,持有库引用防止删除。数据、统计和无关对象不参与比较;旧目录缺失版本时仍比较列定义,已知身份或版本丢失则拒绝。
守卫响应必须明确确认,旧二进制忽略字段时拒绝;未实现守卫的后端明确报错,纯内存视图可直接执行。守卫不改变既有多请求查询的数据隔离语义。
- 内建函数先解析为稳定 FunctionId 并验证签名,再执行;SQL NULL 保留三值语义。运行时描述保存 Eq / Ord / Hash 能力,List、Maybe、Tuple 根据参数递归派生,DOMAIN 沿用基础类型。SQL 比较和量词按运算要求能力,GROUP BY / DISTINCT 同时要求 Eq 和 Hash。
- 复合值排序使用类型身份与结构的字典序;哈希按构造器与子值递归计算,统一浮点正负零,不依赖显示文本。类型和值编码仍为版本 1;哈希只用于内存执行,不需重建持久化索引。SQL NULL 与 Maybe Nothing 分开,空容器也保留完整身份。
- 复合值的 Eq / Ord / Hash 不等于索引键能力;运行时索引及 UNIQUE / PRIMARY KEY 继续拒绝,索引连接仅对现有整数或文本键探测,其余走扫描 / 哈希回退。
- SQL 字面量生成返回明确错误,先验证运行时值与有限浮点;复合值在 Web 使用 SQL 修改。
## 配置、构建与发行
### Web 编辑与受限管理
- Web 编辑在首个业务表加载时建立快照,在提交 / 回退时结束;既有大库基准 BEGIN 269.806 ms、COMMIT 376.345 ms,不按每次单元格编辑重建整库快照。
- Web 登录分别建立普通 API / SQL 和编辑连接,页面 / 数据库标识绑定编辑事务,SQL 事务另绑定查询标签;同 Cookie 请求串行执行。
- 编辑集保留在浏览器,提交从固定保存点重放;失败回退整个编辑集,冲突回滚并重新加载,成功回执去重;系统身份修改不进入数据事务。
- system_catalog_manager 只管理普通身份,不继承、不隐含业务访问权限;存储写锁内再次拒绝特权目标及特权属性修改。
### 离线维护与配置
- bootstrap 维护使用独占数据目录锁,在副本中修复并验证后保留原目录备份再发布;repair 不重放 WAL、不补初始化数据,原有数据文件必须字节一致。
- listen_host 是监听配置,static 固定为静态目录,管理员名由 --user 指定;data_dir 存数据,log_files 存日志,相对路径以启动目录解释。
- resources/settings.toml.linux 与 resources/settings.toml.windows 入库并打包,安装时填入平台路径并生成 settings.toml;UI 设置样本不入库、不进包。配置路径以 docs/config.md 为准。
### 安装与门禁
- 预编译包优先,server / 存储必装,Web / CLI 可选;Windows zip、Linux/macOS tar.gz。包内模板也放 resources/,按确切产物名打包并拒绝未知工件。
- 本地解包安装不联网,管理员名同时传 bootstrap 和 server;安装冒烟隔离配置并恢复用户 PATH,卸载可保留数据 / 配置。
- 门禁先刷新 release cdylib,再并行 Rust、串行 Haskell、注释三条车道;Server / Web 先刷新各工程的本地依赖,任一步失败即失败。
- 局部改动只测试受影响模块;较大改动由 agent 执行全量门禁。日志 / 临时文件在 logs/,必要时 -Full;门禁及打包强制 UTF-8 以避免 GBK 编码失败。
### 口令输入与诊断
- CLI 终端隐藏口令,管道兼容 LF / CRLF / 无行尾,保留空格与内部回车,提示写 stderr。
- CLI 禁用逐行自动历史,只记录完整 SQL 与元命令;含 IDENTIFIED / PASSWORD(忽略大小写)的输入整体排除,避免多行口令或执行失败留下片段。解析错误也隐藏此类原文,筛选保守包含字符串和标识符中的同名文本,已有历史不自动删除。
- TCP 与 Web 使用同一解析诊断脱敏路径;Web 回归校验整个 HTTP 错误响应不含口令。内置 SQL 控制台记录结果或诊断,不将 SQL 原文写入日志,外部代理和采集需自行配置。
### 对象及结构版本编码
- BoundQuery 保存来源对象身份及列定义;DOMAIN 依赖从来源列递归展开,执行前复查。库 / 对象编号在 TableInfo 中输出十进制字符串,读取兼容旧数值,零对象编号表示无有效身份。
- 表级 schema_version 由 catalog 持久化,新表从 1 开始,旧目录缺失版本保留 0;结构变化递增,新增结构 WAL 记录保存确定目标版本,恢复不重新分配。
线上使用精确十进制字符串并兼容数值输入,0 或缺失表示未知版本;IPC schema / 快照与内存 Server 保留版本,内存 DDL 同样递增并拒绝耗尽。
- 注释以 AGENTS.md 为准;原因写本文件,限制写 known-issues.txt,进度及计数只以 progress.txt 为准。
### 容器部署
- 同一运行镜像包含数据库、引导、CLI 和 Web,Compose 将数据库与 Web 分为单进程服务;Web 不挂载数据库目录。运行 UID/GID 为 10001,命名卷保存数据、日志及编辑器偏好。
- 每次 server 启动复用既有幂等 bootstrap,口令从文件经 stdin 输入,不使用文件标记判断系统目录状态;失败阻止启动,不自动 repair / reset。已有口令不随 secret 文件修改而重设。
- Rust 与 Haskell 使用固定 digest 的 Bookworm 构建阶段,运行层沿用 Bookworm。BASE_REGISTRY 只替换下载地址,保持 digest 校验;Debian 与 Cargo 镜像源保留签名及锁文件校验。源码构建单任务且限制 GHC 堆,WSL 使用 1 核 / 3 GiB、无额外交换空间的 Buildx 或异机构建导入。
- Compose 通过 CHUSQL_IMAGE 固定验收镜像,服务默认限制 1 核 / 512 MiB、128 个进程或线程且无额外交换空间。不同 UID 的旧数据卷须停机备份后显式调整所有者,不由服务启动脚本自动递归改权限。
## 博客业务接入
- 业务用户与数据库身份分离,角色使用标量 CHECK;账号完整 Argon2id PHC、发布授权、文章权威状态、评论、互动、审计及迁移标记持久化,可重建文章索引独立。正文与资源留在 Git 文件,接入验收见 docs/blog-database.md。
- 标量索引键上限为 min(128, floor((page_size - 11) / (order - 1)) - 12),保留按旧 64 字节预算校验配置的规则,避免现有合法页 / 分支数组合失效。旧索引头可读取,首次写长键更新能力字段;页、槽位、WAL 与键排序编码不变,提交前及堆直接写入共用配置上限。
## 存储并发与页恢复
- 全局公平门排序权限和 DDL;SQL 数据准备共享库门,最终约束校验与 WAL 发布串行,以保留连续已应用提交前缀。行锁等待位于库门外,持有内部行编号及旧、新唯一值锁;固定名称顺序用于跨库门,等待图用于跨请求行锁。环中的新等待者中止,锁等待总上限 5 秒。保存点回滚恢复计划和读依赖,严格持锁到事务结束。
- 行版本保存在独立的 mvcc.json,以 WAL 提交 LSN 为可见序号,使用不复用内部行编号、起止序号、内容版本和逻辑行序。内容版本与位置变化分开,避免其他行的删除或排序造成伪写冲突。发布顺序是逻辑 WAL、数据与索引、版本目录、库检查点;重放后幂等发布版本。版本超前于可恢复 WAL 时拒绝打开,不冒充已完成恢复。
- 选择独立版本文件以保留现有页与键格式,代价是整库扫描和版本文件写放大。SQL 仍使用物化工作行集;默认快照隔离,SERIALIZABLE 写事务按表保守复查读依赖。版本保留到最老活跃快照,结束事务回收内存,后续发布回收磁盘版本。database_snapshot 的单版本缓存继续供普通后端读取,不承担事务存活登记。
- 保留现有页内布局,以 .page-wal 记录 CPW1 全页镜像、写序号与校验和,以版本 1 的 .pages 保存页 LSN。镜像先同步,数据文件再同步,页元数据经临时文件原子替换并同步目录,最后清理镜像;库 WAL 检查点在这些步骤之后推进。
- 同一逻辑提交可多次刷新同一页,镜像按独立写序号恢复,不仅按页 LSN 比较;截断也写入重置标记。缓存淘汰先记录并写回当前脏页批次,避免反复记录未修改的缓存页。日志错误使当前页句柄进入恢复封闭,保留材料而不靠析构重试。
- 已提交逻辑组重放前丢弃受影响表的派生索引,再依据修复后的堆重建;整表载荷直接重写,避免残缺根或部分行集阻断幂等恢复。账号变更统一走带提交标记和检查点的组,保留旧账号日志解码 / 恢复。
- 显式 compact 在独立文件代次复制堆与索引,最终库写门内追平并发变更,保留切换时逻辑行序。影子文件同步后用 Migrate WAL 发布 catalog.file_generation,对象身份与结构版本不变。旧快照阻止退役文件删除;启动恢复后清理孤立代次。保留有序前缀、重写其余后缀避免重排可观察行序,代价是变化较多时切换窗口变长。启动隐藏列整理保留离线 Compact 日志兼容。