v26.10.1 正式版

修复部分问题,优化性能,进一步推进类型系统
This commit is contained in:
LIm1tZz7 committed 2026-10-05 22:22:23 +08:00
1 parent 48eec484b7
commit 8ce4239452
89 files changed
+5808 -884

No files matched your search

+127 -4
View File
@@ -40,6 +40,8 @@ Web / CLI / 其他客户端
- 每条 TCP 连接拥有独立会话,当前数据库和事务互不影响。
- 读请求可并行,写请求由存储层协调。
- Web 与 CLI 共享服务端的权限和执行规则,不直接读写数据目录。
- Web 登录会话分别持有普通 API / SQL 连接与表格编辑连接,编辑事务按页面标识固定数据库。表格读取携带编辑标识时使用该事务快照;普通查询不进入编辑事务。
- 同一 Web Cookie 的请求串行执行。编辑提交以固定保存点重放本地编辑集,失败整体回退;成功回执按提交标识去重。SQL 控制台事务另按页面与标签标识限制连接访问。
## SQL 查询处理
@@ -97,15 +99,136 @@ Web / CLI / 其他客户端
| 机制 | 行为 |
| --- | --- |
| 角色继承 | 沿成员关系取得权限并集;拒绝自继承与环 |
| 表级授权 | 支持 SELECT、INSERT、UPDATE、DELETE 和 ALL |
| 对象授权 | 数据库 CONNECT / CREATE,表 SELECT / INSERT / UPDATE / DELETE;ALL 按作用域展开 |
| 转授权 | WITH GRANT OPTION 允许将持有的权限授予其他身份 |
| 撤销 | 管理员撤销权限及对应转授权能力;当前不级联撤销已转授权限 |
| 对象名称 | 授权前补全为库.表,检查与持久化使用同一名称 |
| 撤销 | 授予者撤销自身来源;所有者或最高权限身份管理对象授权,级联清理无合法来源的下游 |
| 稳定对象 | catalog 分配数据库及表编号,授权与所有者绑定对象和身份编号,删除后不复用 |
| 所有者 | 建表者获得表所有权;启用角色可继承所有权,数据库所有权不隐含他人表的数据权限 |
对象权限目录 __system_object_acl 对普通语句和存储请求隐藏。权限快照按字符分块,在系统库写锁内比较版本并通过同一 WAL 组替换;服务启动恢复建表前持久化的创建者声明,旧名称授权只迁移一次到现存稳定对象。
类型目录说明见 [types.md](types.md),身份字段与权限边界见 [users-and-roles.md](users-and-roles.md)。
## 权限变更与会话失效
账号、属性和授权变化会标记受影响的在线连接。角色变化先展开成员关系,确定涉及的登录身份。
账号、属性和授权变化会标记受影响的在线连接。角色变化先展开成员及转授权来源关系,确定涉及的登录身份。共享读写门允许查询并行,并将授权及对象结构变更与查询排序;事务提交再次验证权限和稳定对象编号。
TCP 连接在下一次请求时返回 unauthorized 并关闭;空闲连接不被强行中断。权限逐条查询,避免继续使用旧授权。Web 会话还通过账号版本校验,口令或身份变更使旧凭据失效。
## 查询引擎扩展设计(尚未实现)
按 bound IR → 子查询物化 / 递归 CTE 推进;DOMAIN 约束见[类型文档](types.md#domain-约束设计尚未实现)。以下设计尚未实施,当前能力见前文;实施状态以 project-plan.txt 为准。
### bound IR
#### 列与作用域绑定
保留语法 AST 用于解析和诊断;语义检查产出独立 `BoundStatement`,规划、优化及执行逐步只接收它。列引用绑定为作用域编号、来源编号、列编号和类型,保留原始位置用于诊断;相关列明确标记所属外层作用域,不再通过字符串补限定名辨认身份。
#### 表达式类型与错误语义
表达式节点携带结果类型、nullable、已解析运算符/函数标识和显式转换。没有证据证明安全时 `may_error` 为 true;优化不得删除或提前求值可能报错的表达式。未定型 NULL 只参与推导,最后绑定到上下文类型;不允许流入存储 schema。DOMAIN 身份保留在类型节点中,比较可以用底层语义,赋值需经过领域校验。
#### schema 与对象版本
绑定仅使用 schema,不能扫描数据或打开索引。catalog 解析对象后提供 schema 与版本;执行前再次验证稳定对象编号和 schema 版本。统计独立附着,不纳入绑定身份。内存存储使用与磁盘相同的绑定规则。
#### 实施与验收
迁移顺序为查询表达式 → RelOp 输入 → 写语句和约束;过渡适配器单向转换且仅位于边界,不维护两套优化规则。验收用现有查询全集并补同名遮蔽、相关列、别名、NULL/DOMAIN 和错误时序回归;既有可观察顺序保持。
### 通用子查询物化
#### 惰性执行与缓存身份
引入查询内 `LazyMaterialize` 算子:首次实际需要时执行,保留结果行序与重复;非相关查询每语句一次,相关查询按 bound IR 外层列绑定值缓存。缓存键沿用规范值相等、NULL 与领域底层语义,并绑定子计划和执行视图;不能跨语句、会话或权限版本复用。
#### 求值语义
首版只物化已证明确定且无副作用的子计划;可能报错的表达式保持现有首次使用和短路路径,无法证明错误时序相同的改写禁用。空输入不触发子查询,标量多行仍在实际消费时报错。ANY/ALL、IN、EXISTS 各自保留三值及空集规则,不能用集合去重替代结果。
#### 预算与回退
预算同时限制单缓存、查询总缓存和相关键数量;超过预算时在执行该次子计划前选择不缓存的原路径,不能重跑已执行一半的计划。磁盘溢写作为后续阶段,需明确临时目录、类型编码、权限及取消清理。EXPLAIN 展示物化范围与估算,运行统计不泄露缓存行值。
#### 验收条件
验收比较启用/禁用的结果多重集、行序、首次错误、空输入、NULL、重复外层绑定、多次 CTE 引用、预算及取消;不确定函数不可复用。
### 递归 CTE
#### 首版语法范围
保留现有非递归 WITH 行为;新增 WITH RECURSIVE 走绑定作用域,不在解析期无限展开。首版仅单一自引用、锚点 UNION ALL 递归项;自引用只能出现在递归项 FROM,锚点不可引用自身。互递归、多个自引用、递归项聚合/排序/LIMIT、外连接及非单调子查询明确拒绝。UNION 去重递归留后续设计,不能冒用 UNION ALL。
#### 类型绑定与迭代
锚点和递归项输出列数相同;显式列名唯一,先由锚点确定类型,再校验递归项可赋值转换,不按运行数据扩宽。无法给锚点 NULL 定型时要求显式类型。执行用 worktable,递归项仅读取上一轮 delta,按轮次和每轮既有行序追加输出,保留 UNION ALL 重复。
#### 资源预算与终止
拟定默认深度 1000、累计行数 100000、内存 64 MiB,并受服务端上限限制;达到任一预算返回 resource_limit,不返回截断结果。深度定义为递归项执行轮数;delta 为空正常终止。外层 LIMIT 首版不作为终止证明,先完整执行递归再消费,避免隐藏循环/错误。取消释放所有 worktable。
#### 验收条件
验收覆盖零轮、多轮、重复路径、循环超限、NULL/类型冲突、空锚点、名称遮蔽、非法自引用和查询取消。EXPLAIN 明确递归与预算。
## 存储与事务扩展设计(尚未实现)
先引入稳定行编号和 MVCC,再实施跨页 compact 与并行写。前置条件是新页格式、版本化 WAL、离线升级工具及恢复验收。
新格式必须标版本,旧二进制拒绝打开。旧数据在保留备份的停服副本上升级、校验后发布,不混用新旧写入路径。每阶段先运行改动模块的规定测试入口,全量门禁由所有者运行;性能数据不能代替语义、恢复或权限验收。
### 行版本 MVCC
#### 身份与可见性
引入库内单调 row_id,独立于用户 `id`、主键及物理槽号;删除不复用。每版本包含 row_id、版本编号、创建/结束提交序号与前版本定位,未提交版本只在事务私有工作集内。读视图固定 snapshot_csn,选取创建序号不大于快照且结束序号大于快照的版本,再合并自身修改。提交序号与 WAL LSN 分离,映射随提交记录持久化。
每连接仍只允许一个事务,保持快照隔离和保存点语义;不声称可串行化,写偏斜仍可能发生。写集记录读取的基线版本,提交验证该版本仍为最新;同 row_id 更新/删除冲突返回 serialization_failure。唯一键冲突在当前已提交状态和本次写集联合检查,不能仅用开始快照验证。无业务 id 的表也按 row_id 提交,不再整表替换。
索引键定位 row_id,索引查询再按快照检查版本;更新键保留旧键候选直到所有活跃快照不再需要,不能只索引最新值。扫描先保持旧格式导入的行序,更新不改 row_id 的扫描位置,新插入按持久化序号排序;不得借 MVCC 无意改变现有无 ORDER BY 输出。
#### 提交、恢复与回收
首版仍串行提交:验证 → 分配 CSN → WAL 记录版本变化及提交 → fsync → 应用 → 统一发布可见 CSN。读者仅获取已完整应用的可见序号;持久化提交后应用失败继续采用 recovery_required 封闭请求,不能把已提交组当作失败撤销。不引入未提交脏页,因此首版保持 redo-only;若将来允许提前刷未提交页,必须另行设计 undo/状态恢复。
保存点记录私有写集边界与不可变根,回退不撤销其他事务版本。活跃快照登记器包括流式扫描和物化消费者;最老快照决定旧版本回收界线。回收同时处理 heap 和索引,读定位有 pin,取消/断线可靠注销。长事务受现有寿命及新增保留空间预算限制,不能为了回收静默推进其快照。
升级在停服副本中为旧行分配 row_id、初始版本和顺序,重建索引,验证行数/值/约束后切换;不得把重复业务 id 合并。新 WAL 与页格式显式版本化,截断仍遵循已刷盘检查点,不顺带改成页 LSN 检查点。
#### 验收条件
验收包含重复/缺失业务 id、旧快照与键更新、删除重插、保存点、唯一并发冲突、写偏斜边界、长读回收、断线及提交每个持久化边界的崩溃恢复;复测 BEGIN/COMMIT 的传输和比较成本。
### 跨页 compact
#### 定位与影子页
使用 row_id 到版本位置的存储内部定位映射,索引不保存可失效的页/槽定位。首版在表维护独占锁下进行,等待该表读 pin 释放;其他表仍可运行。以快照回收界线保留所有可能可见版本,按既有逻辑扫描顺序打包到影子页和定位映射,随后 WAL 记录并原子切换文件代次。
#### 恢复与旧版本保护
旧代次直到无读引用且新代次检查点完成才删除;提交前崩溃丢弃影子文件,提交后重放切换。映射缺失、重复 row_id/版本和校验失败明确报错,不返回缺行扫描。隐藏列一并按当前 schema 改写,旧快照需要的结构不得提前删除。跨页整理不改变对象编号、行编号、值或输出顺序。
#### 验收条件
验收验证前后全量扫描和索引点/范围查一致、快照旧版本、隐藏列、页损坏、空间不足、取消与文件切换崩溃;记录回收页数及 IO,不能只比较耗时。
### 并行写
#### 实施顺序与锁范围
先允许事务私有写集准备并行,继续保留短串行 WAL/CSN 发布;第二阶段按稳定表编号与 row_id 排序取得写锁、唯一键锁。DDL/维护使用库结构锁,跨表事务必须一次取得完整锁集,禁止边执行边任意升级。权限读门在执行和最终复查期间保持现有语义;目录及 ACL 修改仍单独排序。
#### 唯一键锁与取消
唯一键锁使用与值相等一致的规范编码,缺主键也使用 row_id 锁。等待有取消与超时,超时返回明确错误且保持/回退事务遵循现有错误分类;固定锁序避免内部锁环,不承诺支持任意用户锁或死锁自动重试。
#### 提交发布顺序
允许不同表/行的验证和页面准备并行,但同一页短 latch、catalog 高水位、索引节点和 WAL 写入仍保护。发布 CSN 必须按提交顺序形成连续已应用前缀,后提交不能跨过未完成组而对读者可见。group commit 为后续性能阶段,不能改变每组原子性和持久化失败语义。
#### 验收条件
验收覆盖同表不同行、同一行、唯一键冲突、跨表相反顺序、DDL/compact/撤权竞争、超时取消和 WAL 故障;用确定同步点断言并发结果,再单独测吞吐与等待时间。
-7
View File
@@ -1,7 +0,0 @@
# README 徽章
所有徽章都是本地 SVG,采用统一的深色标签、彩色值区和圆角样式。
执行 `pwsh -File scripts/update-code-lines.ps1` 可重新生成全部徽章。代码行数包含代码、测试、注释和空行,排除 Git 忽略的构建工件;行数与提交编号是生成时的本地快照。
发布徽章链接 GitHub 版本列表。构建、测试和门禁徽章提供实时状态页面入口,本地图片显示 `view status` 或 `manual status`,不保存或宣称实时通过状态。门禁由所有者手动触发。
-6
View File
@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="160" height="24" viewBox="0 0 160 24" role="img" aria-label="build: view status">
<title>Open the build workflow for live status; this local badge is a navigation link.</title>
<defs><clipPath id="rounded"><rect width="160" height="24" rx="5"/></clipPath></defs>
<g clip-path="url(#rounded)"><rect width="160" height="24" fill="#1e293b"/><rect x="59" width="101" height="24" fill="#0369a1"/></g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,sans-serif" font-size="11"><text x="29.5" y="16">build</text><text x="109.5" y="16">view status</text></g>
</svg>

Before

Width:  |  Height:  |  Size: 634 B

-6
View File
@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="139" height="24" viewBox="0 0 139 24" role="img" aria-label="commit: fd5f320">
<title>Local HEAD snapshot when the badges were generated; open commit history for latest updates.</title>
<defs><clipPath id="rounded"><rect width="139" height="24" rx="5"/></clipPath></defs>
<g clip-path="url(#rounded)"><rect width="139" height="24" fill="#1e293b"/><rect x="66" width="73" height="24" fill="#475569"/></g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,sans-serif" font-size="11"><text x="33" y="16">commit</text><text x="102.5" y="16">fd5f320</text></g>
</svg>

Before

Width:  |  Height:  |  Size: 637 B

-6
View File
@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="167" height="24" viewBox="0 0 167 24" role="img" aria-label="gate: manual status">
<title>Open the owner-triggered gate workflow for status; this local badge is a navigation link.</title>
<defs><clipPath id="rounded"><rect width="167" height="24" rx="5"/></clipPath></defs>
<g clip-path="url(#rounded)"><rect width="167" height="24" fill="#1e293b"/><rect x="52" width="115" height="24" fill="#b45309"/></g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,sans-serif" font-size="11"><text x="26" y="16">gate</text><text x="109.5" y="16">manual status</text></g>
</svg>

Before

Width:  |  Height:  |  Size: 644 B

-6
View File
@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="209" height="24" viewBox="0 0 209 24" role="img" aria-label="languages: Haskell + Rust">
<title>ChuSQL implementation languages: Haskell and Rust.</title>
<defs><clipPath id="rounded"><rect width="209" height="24" rx="5"/></clipPath></defs>
<g clip-path="url(#rounded)"><rect width="209" height="24" fill="#1e293b"/><rect x="87" width="122" height="24" fill="#6d28d9"/></g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,sans-serif" font-size="11"><text x="43.5" y="16">languages</text><text x="148" y="16">Haskell + Rust</text></g>
</svg>

Before

Width:  |  Height:  |  Size: 617 B

-6
View File
@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="174" height="24" viewBox="0 0 174 24" role="img" aria-label="source lines: 37,149">
<title>Physical source lines; includes tests, comments and blank lines.</title>
<defs><clipPath id="rounded"><rect width="174" height="24" rx="5"/></clipPath></defs>
<g clip-path="url(#rounded)"><rect width="174" height="24" fill="#1e293b"/><rect x="108" width="66" height="24" fill="#0369a1"/></g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,sans-serif" font-size="11"><text x="54" y="16">source lines</text><text x="141" y="16">37,149</text></g>
</svg>

Before

Width:  |  Height:  |  Size: 619 B

-6
View File
@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="139" height="24" viewBox="0 0 139 24" role="img" aria-label="release: browse">
<title>Open GitHub releases to view published versions.</title>
<defs><clipPath id="rounded"><rect width="139" height="24" rx="5"/></clipPath></defs>
<g clip-path="url(#rounded)"><rect width="139" height="24" fill="#1e293b"/><rect x="73" width="66" height="24" fill="#6d28d9"/></g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,sans-serif" font-size="11"><text x="36.5" y="16">release</text><text x="106" y="16">browse</text></g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

-6
View File
@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="160" height="24" viewBox="0 0 160 24" role="img" aria-label="tests: view status">
<title>Open the test workflow for live status; this local badge is a navigation link.</title>
<defs><clipPath id="rounded"><rect width="160" height="24" rx="5"/></clipPath></defs>
<g clip-path="url(#rounded)"><rect width="160" height="24" fill="#1e293b"/><rect x="59" width="101" height="24" fill="#0369a1"/></g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,sans-serif" font-size="11"><text x="29.5" y="16">tests</text><text x="109.5" y="16">view status</text></g>
</svg>

Before

Width:  |  Height:  |  Size: 633 B

+13
View File
@@ -45,6 +45,7 @@ csql [options]
| `-f, --format FORMAT` | 输出格式:`table`、`json` 或 `csv` |
| `-e, --execute SQL` | 执行一条 SQL 后退出 |
| `--history FILE` | 指定历史记录文件 |
| `--sudo` | 使用本机 root / 提升管理员身份认证;需配置映射并启用身份属性,见 [用户与角色](users-and-roles.md#本机-sudo-认证) |
| `--config FILE` | 指定配置文件 |
| `-h, --help` | 显示帮助 |
@@ -99,6 +100,18 @@ chusql-web [options]
静态资源目录固定为 `static`。其余请求大小、会话、登录限制、分页等参数通常通过 `settings.toml` 配置即可。
### 表格编辑与事务
首次打开业务数据表时建立编辑快照。同一页面内、同一数据库的跨表修改统一暂存;“提交全部”在服务端事务中执行,全部成功才持久化。逐项撤销只移除本地编辑项,下次提交从原快照重放剩余编辑;“撤销全部”执行 ROLLBACK 并重新加载表和统计。
途中发生约束错误时,整批语句退回编辑保存点,完整编辑内容仍可修正。写写冲突不会覆盖其他会话,必须撤销并重新加载后重新编辑。提交响应断线时冻结编辑集;再次点击提交使用同一标识确认或重试同一批修改,已成功的提交不会重复执行。
编辑事务绑定登录会话、页面和数据库;待提交修改存在时,先提交或撤销才能换库或运行 SQL。关闭最后一个表标签且没有待提交修改时释放快照。关闭页面会请求回滚,退出登录会关闭连接并丢弃快照;若页面断线、回滚请求未送达,后台每分钟按配置的会话空闲与最长寿命清理连接。重新登录不能继续旧事务。
SQL 控制台使用另一条服务端连接;SQL 的 BEGIN / COMMIT / ROLLBACK 绑定发起它的页面与查询标签。事务尚未结束时,其他控制台和普通 REST 写操作不能占用该连接;关闭所属查询标签或页面时请求回滚。系统身份修改不支持数据事务,账号表只读,使用 SQL 用户与角色命令管理。
Web 模块测试入口为 `stack test chusql-web --fast`,其中同时运行 `node --test test/transactions.mjs`;测试环境需要 Node.js。
## chusql-server
基本用法:
+2
View File
@@ -53,6 +53,8 @@ ChuSQL 使用统一配置文件 `settings.toml`。默认位置:
| `max_message` | `1048576` | 单条请求最大大小(字节) |
| `max_rows` | `1000` | 单次查询最大返回行数 |
本机 sudo 认证额外使用 `[server] sudo_auth_user`,默认空字符串,表示关闭。设置为一个已启用 `ALLOW_SUDO_AUTH` 的数据库身份后,必须以 Unix root 或 Windows 提升后的管理员运行 `chusql-server`。这是明确的本机身份映射,与启动参数 `--user` 和最高权限属性分别设置。配置变更后重启服务生效,Web 设置页面不提供此项。操作步骤见 [用户与角色](users-and-roles.md#本机-sudo-认证)。
修改 Server 相关配置后需要重启 `chusql-server`。
## 账号与安全
+18
View File
@@ -33,6 +33,17 @@ ChuSQL 支持三种安装方式:
完成系统目录初始化后,安装程序默认会启动 `chusql-server`;如不希望自动启动,可使用 `--no-start`(Windows 为 `-NoStart`)。
Linux 默认注册 `chusql-server.service` 为当前操作系统用户的 systemd 服务,并启用 linger 和开机启动,无需登录即可运行,异常退出后自动重启。需要可用的 systemd 用户会话;若启用 linger 时权限不足,按安装提示执行 `sudo loginctl enable-linger "$(id -un)"` 后重试。数据库管理员 `--user` 与运行服务的操作系统用户是两个独立身份。
`--no-start` 仅跳过本次启动,仍启用开机启动。非 systemd 环境可使用 `--no-service` 跳过服务注册,此时保留原有后台启动方式。
```sh
systemctl --user status chusql-server.service
systemctl --user restart chusql-server.service
systemctl --user stop chusql-server.service
journalctl --user -u chusql-server.service
```
## 包内安装
解压发行包后,在包根目录执行:
@@ -56,6 +67,7 @@ Windows:
| `--data-dir DIR` | `-DataDir` | 指定数据目录 |
| `--user NAME` | `-RootUser` | 指定管理员账号名 |
| `--no-start` | `-NoStart` | 安装后不自动启动服务 |
| `--no-service` | — | 跳过 Linux 服务注册和开机启动 |
| `--version TAG` | `-Version` | 指定发行版本 |
| `--list-versions` | `-ListVersions` | 列出可安装版本 |
| `--from-source` | — | 从源码构建并安装 |
@@ -108,3 +120,9 @@ Windows:
```
如需保留数据,可使用 `--keep-data`(Windows 为 `-KeepData`);如需保留配置,可使用 `--keep-config`(Windows 为 `-KeepConfig`)。
Linux 卸载时会停止服务、取消开机启动并移除服务文件。用户的 linger 设置保留,其他用户服务仍可使用它。
## 发布真实性与后续签名方案
当前 SHA256 校验用于检测产物损坏,发布签名尚未实现。密钥管理、可信根、轮换、在线与离线验证的设计见[发布签名与验证](release-signing.md);设计中的验证器和签名安装流程目前不可用。
+143
View File
@@ -0,0 +1,143 @@
# 数据库范式分析与迁移
引擎模块 `ChuSQL.Core.Engine.Normalization` 提供只读范式分析。输入只有 schema 与用户声明,不读取样本数据,不访问或修改存储。
## 接口与示例
```haskell
analyzeNormalization
:: [(String, Column)]
-> Declaration
-> Either String Analysis
```
调用示例:
```haskell
analyzeNormalization [("student", TInt), ("course", TInt), ("name", TStr)]
(Declaration
{ declaredKeys = [["student", "course"]]
, dependencies = [Dependency ["student"] ["name"]]
, dependenciesComplete = True
, atomicAttributes = Just True
})
```
这个声明违反 2NF、3NF、BCNF:非主属性 `name` 依赖复合候选键的真子集 `student`。
报告的 `witnesses` 给出相关字段,`condition` 给出范式条件。建议分为 `(student, name)` 与 `(course, student)`,无损连接并保持声明依赖。
## 声明与输入校验
`declaredKeys` 中每个键作为决定全部字段的依赖加入公理推导;检查器验证其最小性,并枚举所有最小超键。空键允许表达只有一行的关系约束。
字段名称使用 schema 中的确切名称,不自行处理 SQL 标识符解析。以下输入返回明确错误:
- 未知字段或重复字段。
- 空依赖右部。
- 非最小候选键。
- 空 schema。
## 公理推导与范式条件
属性闭包以输入属性实现自反性,逐轮加入已满足左部的依赖右部,得到阿姆斯特朗增广和传递规则蕴含的闭包。检查器枚举字段子集及其闭包,检查完整的依赖投影。
| 范式 | 检查条件 |
| --- | --- |
| 1NF | 根据用户声明判断领域值原子性。 |
| 2NF | 检查所有候选键的部分依赖。 |
| 3NF | 检查非超键左部与非主属性右部。 |
| BCNF | 检查非超键左部。 |
理论定义可参考[数据库课程讲义](https://documents.uow.edu.au/~jrg/235/slides/03databasenormalization/03databasenormalization.html)。
## 信息完整性与判定
### 领域值原子性
`atomicAttributes` 是用户关于领域值原子性的声明:
| 声明值 | 1NF 判定 |
| --- | --- |
| `Just True` | 满足。 |
| `Just False` | 违反。 |
| `Nothing` | 信息不足。 |
schema 类型不证明业务意义上的原子性。不满足 1NF 时更高范式也判违反;原子性未知时更高范式信息不足。
### 依赖完整性
只有 `dependenciesComplete = True` 才对更高范式给出满足或违反结论。这表示用户承诺给出了完整业务依赖的一个覆盖,而非必须列出所有推导依赖。完整的空依赖集有效。
未承诺完整时,更高范式返回 `Insufficient`,不输出违反证据或分解建议。此时 `candidateKeys` 仅是相对已知声明求得的键,不能视为完整业务候选键清单。
分析始终以声明为条件,不能证明数据库实际数据满足声明。
## 分解建议
分解建议仅针对第一条 BCNF 违反依赖 `X -> Y`,输出两个子关系:
- `X ∪ Y`
- `R − (Y − X)`
该二元分解由依赖保证无损;依赖保持通过两个子关系的完整依赖投影之并是否蕴含原依赖集判定。
建议只是一轮分解,子关系可能仍需继续分析,不承诺一次达到 BCNF。依赖不保持时需跨关系检查原约束。
## 第一阶段限制
- 最多接受 12 列,超过返回错误,避免指数枚举无界增长。
- 仅提供 Haskell 只读 API。
- CLI / Web 入口和自动迁移尚未实现。
## 后续入口设计(尚未实现)
以下 TCP、CLI 和 Web 接口尚未实现,拟议命令与端点目前不可用。实施状态以 project-plan.txt 为准。
### 协议与鉴权
在 `ChuSQL.Interface.Protocol` 增加专用客户端请求 `normalization_analyze`,由 server 调用 engine API;不经最高权限专用的原始 `ReqStorage` 入口。请求包含 `api_version=1`、数据库与表标识、声明,以及可选的预期对象编号和 schema 指纹。server 在现有权限读门内以 catalog 解析现存对象,检查 CONNECT 与 SELECT;系统对象拒绝访问。事务内取该连接的 schema 快照,返回视图来源和对象编号,不能混入其他连接的快照。
声明字段固定为 `declared_keys`、`dependencies`(`lhs` / `rhs`)、`dependencies_complete`、`atomic_attributes`。完整性缺省 false,原子性缺省 null;不把 PRIMARY KEY、UNIQUE 或样本自动当作完整业务依赖。前端可以展示实际 schema 约束供用户选择,确认后才成为声明。候选键的 nullable 与 SQL UNIQUE 语义单独提示,不自动证明经典关系模型的键。
响应包含对象编号、schema 指纹、规范化声明、候选键、四项 findings 和一次分解建议。稳定标签使用 `1nf/2nf/3nf/bcnf`、`satisfied/violated/insufficient`;条件说明与机器字段分开。所有结论标明“以声明为条件”,无损连接标签不代表现有 SQL 数据或袋语义已经验证。
字段名称必须与返回 schema 的确切列名一致;对象名称使用现有标识符规则,数据库限定名必须与请求数据库一致。指纹只覆盖列名、类型和约束等语义结构,排除行数与直方图;定义确定性编码并同时绑定稳定对象编号,避免同名重建复用报告。
拒绝未知协议版本、未知字段、重复字段、空右部、非最小声明键、空 schema 和超过 12 列。拟定请求上限 64 KiB、依赖条数 256、键声明 256;预算超限返回明确错误,不截断后给出完整报告。分析在有界工作队列运行,取消或超时释放权限读门与会话资源;输出也受传输预算约束,超限返回错误。实施时用最坏 12 列声明确定默认时间预算。
### CLI 与 Web
CLI 拟议元命令为 `\normalize TABLE --declaration FILE --format text|json`,通过当前登录连接和工作库请求。声明文件为 UTF-8 JSON,错误写 stderr;成功分析退出码 0,即使结论违反或信息不足;协议、权限、输入与运行错误退出非零。批处理增加独立 `--normalize` 模式,与 `--execute` 互斥,不把元命令发给 SQL 解析器。
Web 拟议 `POST /api/normalization/analyze`,复用登录、CSRF 和 Cookie 串行规则。入口位于表结构页:展示 schema、编辑声明、提交分析、显示证据和分解字段列表。完整性与原子性不预先勾选;没有自动执行分解按钮。编辑页分析必须携带既有页面事务标识,不能自动开启另一事务。对象或 schema 改变返回冲突并要求重新分析。
### 入口验收
验收覆盖 engine 报告的协议往返、CLI 文本/JSON 同值、Web 与 TCP 越权、跨库同名、删表重建、事务快照、超限及取消。沿用现有 14 项算法回归,不复制算法到客户端。
## 数据迁移设计(尚未实现)
先实现只读入口,再在 [DOMAIN 约束](types.md#domain-约束设计尚未实现)、依赖目录和原子结构发布具备后实施迁移。
### 规划条件
迁移首版限定单业务表、二元分解、显式目标名,保留原表,不自动改名或删除。请求必须引用报告对象编号、schema 指纹及声明摘要;报告本身不是执行授权。要求来源 SELECT、数据库 CREATE;切换旧表需要来源所有权。每个目标取得新编号和执行者所有权,原 ACL 不自动复制。
经典 FD 与无损分解适用于关系集合,当前 SQL 保留重复行且有 NULL。首版只接受所有参与字段 NOT NULL、全行无重复且声明确实成立的数据;否则明确拒绝,不能通过 DISTINCT 悄悄丢行。候选键也必须验证唯一和非空,包括空键最多一行。FD 验证按引擎规范值相等规则分组,禁止使用文本序列化代替值比较。
目标数据为各字段投影的集合。验证每条声明 FD、全部候选键、目标约束及两目标连接与来源的完整多重集相等;无损性理论证明和实际重建检查均须通过。若投影依赖不能保持所有原依赖,首版拒绝迁移;后续有跨表约束执行器后再开放,不只弹提示继续。
### 结构依赖与发布
规划器枚举索引、默认值、CHECK、DOMAIN 引用、外键及其他已登记依赖,逐项生成保留或拒绝理由。单目标可表达的约束绑定后迁移;跨目标 CHECK、无法保存的索引/唯一约束及任何无法枚举的受支持依赖阻止发布。不得根据 SQL 文本替换列名。客户端保存的任意 SQL 无法枚举,保留来源表可避免静默改坏;首版不提供透明替换。
执行流程为 prepare → validate → publish:prepare 创建不对业务 catalog 可见的暂存对象;validate 固定来源视图并生成目标、验证重建;publish 在短结构锁内复查身份、权限、对象编号、schema 与数据版本,原子登记全部目标。来源变化返回 serialization_failure,不自动重试。暂存对象需要空间预算、作业编号和取消清理。
现有事务禁止 DDL,不能拼接 CREATE/INSERT 伪装成原子迁移。实施前必须加入包含 catalog、页文件、索引和所有权声明的迁移 WAL 发布组;提交前暂存文件可清理,提交后故障进入 recovery_required 并重放到完整目标组。对象编号可以跳号但不得复用。恢复仅依据作业状态与 WAL 清理暂存对象,不能按名称猜测。
后续替换来源需独立设计:稳定对象引用、授权重绑定、完整依赖图与原子交换。保留原对象及其编号直到该方案验收;当前阶段不提供多轮自动 BCNF 化。
### 迁移验收
验收包含重复/NULL/不成立 FD 的拒绝、依赖不保持、跨目标约束、并发修改、撤权、名称占用、取消、空间不足,以及每个 WAL/文件发布边界的崩溃恢复;失败不得留下部分可见目标。
+22
View File
@@ -103,6 +103,8 @@ stack exec -- benchmark4csql 10000 50000
## 性能瓶颈分析
本节描述首次基准时的实现;事务差分优化后的结果见文末复测。
### 1. 事务提交的整库差分是首要排查点
本次 200 条事务 INSERT 比 200 次独立持久化 INSERT 慢约 60 倍。事务阶段开始时,数据库已有 10,000 用户、50,000 订单及 5,000 宽行,事务只修改 writes 表。事务内一次 400 行 UPDATE 仅耗时 0.5 ms,说明不能将整个事务阶段的 67.55 秒简单归因于 SQL 更新计算或 fsync。
@@ -132,3 +134,23 @@ stack exec -- benchmark4csql 10000 50000
## 使用范围
这是单机回环、主要为热缓存、管理员身份的单次基准,不是生产容量承诺。未覆盖远程网络、鉴权角色开销、崩溃恢复耗时、并发写冲突或大型宽表。七次读采样和单次写测量不足以建立统计置信区间,后续优化应使用相同参数、多次独立运行和分阶段 profile 比较。
## 2026-10-05 事务差分优化复测
使用相同机器、优化构建及 10,000 用户、50,000 订单规模运行完整基准,所有场景及结果校验通过(`BENCHMARK_OK`)。日志为 `logs/repair-txn-performance.log`。
COMMIT 跳过未修改表,以 IntMap / IntSet 查询整数键差分,冲突检查与操作生成共享差分;保持原行顺序及重复 id 的首行查找语义。
| 指标 | 耗时(ms) |
| --- | ---: |
| 200 次独立持久化 INSERT | 1,134.7 |
| 事务 200 条 INSERT 总计 | 687.355 |
| BEGIN | 269.806 |
| 事务内 200 条 INSERT | 41.129 |
| COMMIT | 376.345 |
事务总耗时从首次的 67,549.539 ms 降至 687.355 ms,约快 98 倍,吞吐从 2.96 升至 290.97 行/秒;本次事务写入约比逐条持久化快 1.65 倍。两次独立运行未建立统计置信区间,倍数仅描述这两次测量。
BEGIN 与 COMMIT 仍获取整库快照,合计约占事务总耗时的 94%;COMMIT 计时也包含差分、冲突检查和持久化,不能全部归因于快照。整库快照传输、修改表比较及刷盘仍需进一步分阶段测量。
20 个不同绑定的相关 EXISTS 平均 2,135.218 ms(首次 2,061.770 ms),该瓶颈仍存在,本轮没有改变子查询执行方式。
+31
View File
@@ -0,0 +1,31 @@
# 发布签名与验证设计
本文定义尚未实现的发行安全方案。当前操作见[安装文档](install.md),实施状态以 project-plan.txt 为准。先实现离线验证,再接入在线安装;该工作可独立于查询和存储扩展推进。
## 选择与信任入口
采用标准 TUF root/targets/snapshot/timestamp 元数据和 Ed25519 签名,使用经过维护的实现与标准测试向量,不自行定义签名 JSON 规范。Ed25519 算法依据 [RFC 8032](https://datatracker.ietf.org/doc/rfc8032/),角色、轮换、版本及过期验证依据 [TUF 规范](https://theupdateframework.github.io/specification/v1.0.36/)。SHA256 保留为产物散列,签名覆盖其可信元数据来源。
可信初始 root 随独立取得的安装器/验证器发布,记录版本与公钥;不能从同一个未认证下载响应接受任意公钥。首次下载安装器仍依赖用户可信取得该安装器这一入口,不能宣称脚本内公钥解决首次下载脚本被替换。升级客户端保存可信元数据的版本与过期状态,拒绝回退、混配和过期;状态缺失不等同于已完成反回退验证。
targets 绑定确切文件名、长度、SHA256、平台/架构、版本、提交号、组件清单与最低数据格式要求。snapshot 固定元数据版本/散列,timestamp 指向 snapshot。禁止通配匹配产物、跨平台替代和同版本静默覆盖;签名通过后才允许解包或执行 bootstrap。
## 密钥与发布操作
root 使用离线 2-of-3 阈值密钥,分开保管及加密备份;CI 不持有 root 私钥。targets 发布密钥限定于受保护发布环境,仅受信任标签与人工发布流程可调用;PR 构建、普通日志、构建缓存和发行包均不能读取私钥。snapshot/timestamp 使用独立在线密钥,权限和有效期按角色设置;拟定 targets/snapshot 30 天、timestamp 7 天、root 1 年,并通过到期监控续签。
轮换 root 按连续版本发布,同时满足旧 root 与新 root 阈值;离线备份定期做恢复演练。在线密钥泄露时停止发布,用 root 撤销并更换受影响角色密钥,重新生成元数据;所有 root 信任丢失时需要经独立可信渠道重新引导,不能靠损坏的 root 自证恢复。
发布顺序为构建与模块/所有者门禁 → 装配确切产物 → 计算散列 → 签 targets/snapshot → 上传产物和版本化元数据 → 最后发布 timestamp。任何产物字节变化均需重新签署。签名不证明构建内容正确,平台真机验收仍单列。
## 安装与兼容性
新增小型跨平台验证器,打包随安装器提供;验证器不可作为首个未验证的临时下载再立即执行。在线安装先验证元数据链、版本、有效期与目标平台,再下载并验长度/散列,之后才解包。签名缺失、密钥未知、元数据过期、系统时间无法可信校验等均明确失败,不降级成仅 SHA256。
本地离线安装使用随包 root 链、targets 和 snapshot 验证真实性与完整性;缺少在线 timestamp 时明确说明不能证明最新版本。离线模式必须显式选择且不联网;已有可信版本仍禁止回退,元数据过期仍失败,需要重新取得有效离线元数据。历史无签名包不纳入新安装器的可信安装路径。
系统原生代码签名分别设计接入 Windows Authenticode 和 macOS Developer ID/notarization;TUF 不能替代操作系统签名,第一阶段也不声称消除 SmartScreen/Gatekeeper 提示。Linux $ORIGIN、在线下载与 macOS 发行继续按现有实机待办验收。
## 验收条件
验收覆盖合法包、字节篡改、错误平台、截断、签名替换、重复键/非法元数据、未知密钥、过期、回退、元数据混配、连续 root 轮换/阈值不足、撤销、断网离线、下载中断与私钥日志检查;三平台从干净安装目录验证拒绝路径不会执行包内程序。
+65
View File
@@ -0,0 +1,65 @@
# 运行时类型与函数基础
阶段 A 提供固定身份的内建类型、函数注册表、schema 驱动的绑定和类型化查询入口。
完整 capability、运算符注册、转换、用户 ADT 与 Extension 留在后续阶段。
## SQL 使用
```sql
CREATE TABLE typed_values (
id Int,
name String,
numbers [Int],
label Maybe String,
pair (Int, String)
);
INSERT INTO typed_values (id, name, numbers, label, pair)
VALUES (1, 'abc', LIST<Int>(1, 2), MAYBE<String>(), TUPLE<Int, String>(3, 'x'));
SELECT length(name), numbers, label, pair FROM typed_values;
UPDATE typed_values SET numbers = LIST<Int>(9), label = MAYBE<String>('ready') WHERE id = 1;
SELECT LIST<Maybe Int>(MAYBE<Int>(), MAYBE<Int>(42));
SELECT not(TRUE), length('你好');
```
类型语法支持 `Int`、`Double`、`Bool`、`String`、`[a]` / `List a`、`Maybe a` 和 `(a, b, ...)`。
构造器中的类型参数是完整逻辑类型;空列表和 Nothing 也有确定类型。
`MAYBE<a>()` 表示 Nothing,`MAYBE<a>(value)` 表示 Just;不能用 SQL NULL 代替复合值内部的 Nothing。
SQL 可空列仍接受 NULL,和 Maybe 的 Nothing 保持区别。
构造器按完整类型校验,不隐式转换 List 的元素类型。
内建签名为 `length :: String -> Int` 和 `not :: Bool -> Bool`,名称不区分大小写。
SQL 参数为 NULL 时结果为 NULL;具体非空参数仍校验类型。
尚无运行时 Eq / Ord / Hash 字典,复合类型的比较、排序、分组、极值和索引不会自动取得能力。
Web 显示复合值及 SQL 构造器预览,复合字段通过 SQL 控制台修改。
## 身份与编码
`ChuSQL.Core.Runtime` 是共享模型中的权威定义;Engine 模块导出同一组身份。
内建构造器编号固定为 Int=1、Double=2、Bool=3、String=4、List=5、Maybe=6、Tuple=7。
TypeId 包含已校验的完整构造器应用,比较和恢复不依赖注册顺序或随机哈希。
FunctionId 固定为 length=1、not=2;内建元数据可直接重建注册表。
逻辑 TypeExpr 与 PhysicalType 分开;List、Maybe、Tuple 使用递归 sequence、optional、product 表示。
schema 保存版本化的 TypeId,协议值保存版本、类型身份和 payload;包括 String 在内的运行时值共用编码。
Rust 的通用行、目录和 WAL 路径保存该表示,读取时由共享模型验证版本、类型、元素和字段个数。
未知版本、身份溢出、类型不符、非有限浮点数及损坏 payload 明确返回错误。
内建类型不可删除,复合类型的依赖完整包含在 TypeExpr 中。
旧标量 schema 和协议继续读取;包含运行时类型的数据不能交给不支持该编码的旧客户端。
## 绑定与执行
`Runtime.Binding` 提供独立的 TypedExpression 和 TypedPlan,绑定只接受 catalog schema。
表达式和计划构造器隐藏,调用者必须经过 Binder;执行使用已解析的 TypeId / FunctionId,并校验存储行。
该 API 覆盖单行、表扫描、投影和过滤,可构造嵌套复合值及函数调用。
SQL SELECT 通过 `Runtime.Query.bindQuery` 生成不可直接构造的 BoundQuery,再由 `executeQueryM` 执行。
绑定计划保存输出 schema 与类型身份;现有 RelOp 作为关系执行和优化的兼容桥,输出按位置校验,保留重复别名。
类型化函数和构造器节点也进入 INSERT、UPDATE、CHECK、聚合参数和子查询。
日期、时间戳、Blob 等阶段 A 外的类型保留明确的 CompatibilityType。
旧 ColumnType 和运算符实现尚未整体移除;统一 capability、cast 和 rewrite 将在后续阶段收拢。
测试覆盖稳定编码、kind / 参数校验、共享协议、SQL 调用和复合列、实际存储重载、HTTP/TCP 与客户端显示。
+13 -3
View File
@@ -136,13 +136,21 @@ REVOKE reviewer FROM alice;
DROP ROLE analyst;
```
当前表级权限包括:`SELECT`、`INSERT`、`UPDATE`、`DELETE` 和 `ALL`。
表级权限包括 `SELECT`、`INSERT`、`UPDATE`、`DELETE`;数据库权限包括 `CONNECT`、`CREATE`。`ALL` 按对象类型展开。未限定的表名及 `*` 使用当前数据库,`库名.表名` 和 `库名.*` 可指定其他数据库;系统对象不能通过业务 GRANT 授权。
```sql
REVOKE CONNECT ON DATABASE sales FROM PUBLIC;
GRANT CONNECT, CREATE ON DATABASE sales TO analyst;
GRANT SELECT ON TABLE sales.orders TO reviewer WITH GRANT OPTION;
GRANT SELECT ON sales.* TO analyst;
REVOKE SELECT ON sales.orders FROM reviewer;
```
角色可以互相继承:`GRANT analyst TO reviewer` 让 `reviewer` 成为 `analyst` 的成员并取得其授权;继承会沿成员关系传递,成环的授权被拒绝(报 `conflict`),自继承同样拒绝。用户也可以直接接受表级授权或作为其他角色的成员。
管理员拥有完整权限;数据库、表、索引等结构管理操作仅允许管理员执行。
最高权限身份拥有完整权限。数据库默认向 PUBLIC 开放 CONNECT,不默认授予 CREATE 或数据权限。具有数据库 CREATE 的身份可建表,并成为新表所有者;表所有者可管理表、字段及索引,并授予该表权限。数据库所有者可删除自己的数据库,创建数据库与管理 DOMAIN 仍要求最高权限。
`GRANT ... WITH GRANT OPTION` 让拿到的授权可以再转授:普通身份只有手里那条授权带 grant option 时才能执行 `GRANT`,报错是 `grant option required: <权限> ON <表>`;`REVOKE` 仍然只允许管理员,收回授权会连它的 grant option 一起收掉,且不会级联收回别人转授出去的授权。转授权随角色继承一起传递。
`GRANT ... WITH GRANT OPTION` 允许再转授;所有者也可授予自身对象的权限。普通授予者可撤销自己的授予,所有者及最高权限身份可撤销对象上的其他授予。撤销时收回相应 grant option,并级联清理失去合法来源的下游授权;仍有独立来源的授权保留。转授权能力随启用的角色继承,循环转授不能独立维持权限。REVOKE 不移除所有者的隐含权限。
系统目录访问还受目录管理属性控制,具体能力见 [用户与角色](users-and-roles.md)。
@@ -152,4 +160,6 @@ DROP ROLE analyst;
```sql
ALTER USER alice SYSTEM_CATALOG_MANAGER;
ALTER USER alice NOSYSTEM_CATALOG_MANAGER;
ALTER USER alice ALLOW_SUDO_AUTH;
ALTER USER alice NOALLOW_SUDO_AUTH;
```
+20
View File
@@ -46,3 +46,23 @@ DOMAIN 按数据库保存,保留名称并复用基础类型的校验与运算
该目录是受保护的系统元数据,普通 SQL 不直接展示或修改它。类型执行语义由引擎实现,不能通过手工增加目录行获得新的基础类型。安装登记不会创建任何预定义 DOMAIN;DOMAIN 定义另存于各工作库的 catalog。
首次安装完成管理员初始化后登记类型。重复引导保留一致的记录并补齐缺失项;同名定义冲突会报错,停止安装启动流程,不覆盖现有定义。
## DOMAIN 约束设计(尚未实现)
以下约束与 ALTER 能力是后续设计,当前 DOMAIN 仍只提供类型命名。实施依赖 [bound IR](architecture.md#bound-ir),先固化类型、列身份与表达式求值规则。
### 约束与默认值语义
定义扩展为基础类型引用、可选 DEFAULT、NOT NULL、命名 CHECK 及定义版本。CHECK 只接受绑定的单值 `VALUE` 表达式和确定性内置函数,禁止子查询、表引用和自定义副作用函数;CHECK 的 false 拒绝,unknown 允许,NOT NULL 独立拒绝 NULL。嵌套领域从底层向外校验,约束取交集;显式列默认优先,否则使用最近外层有定义的领域默认,只在省略列值时使用,不替换显式 NULL。
### 引擎与存储校验
server/engine 负责绑定;Rust 存储根据版本化约束 IR 在全部写入口再次验证,包含批写、原始请求和迁移。两层必须共享运算符标识、类型及 NULL 规则并用相同测试向量验证;不能让 Rust 重新解析 SQL 或只依赖客户端验证。解析诊断不得吞错。
### 结构变更与持久化
首版仅支持 CREATE 带约束与 ALTER 添加经全量验证的约束、修改默认;不支持 NOT VALID 或 CASCADE。ALTER 在稳定 schema/数据视图中校验所有直接和间接使用列,短锁发布前复查版本;失败不改变定义。类型定义、依赖版本和受影响 schema 同一 WAL 组提交。现有 DOMAIN 空目录表升级为明确版本元数据,旧编码读入并转换,拒绝无法解析的定义。
### 验收条件
验收覆盖嵌套默认优先级、三值 CHECK、NULL、批写全回退、领域改变并发写、循环/间接依赖、内存事务提交与 WAL 重放。新约束加入既有事务后,提交必须按最新定义重新验证或报 schema 冲突。
+66 -3
View File
@@ -10,6 +10,7 @@ ChuSQL 的用户和角色共用一套身份目录及名称空间。用户是允
| --- | --- | --- |
| `can_login` | `true` | 可以使用账号及口令登录 |
| `is_superuser` | `true` | 具有系统管理及数据访问的最高权限 |
| `allow_sudo_auth` | `false` | 允许经本机提升身份验证后免数据库口令登录;不赋予权限 |
| `system_catalog_manager` | `false` | 独立目录管理属性;SUPERUSER 本身已具有有效目录管理能力 |
| `enabled` | `true` | 身份处于启用状态 |
@@ -29,9 +30,9 @@ ChuSQL 的用户和角色共用一套身份目录及名称空间。用户是允
身份创建与修改语法见 [SQL 语法](syntax.md#账号与权限)。
`ALTER USER` 与 `ALTER ROLE` 的属性、口令修改语法通用。可使用 `LOGIN / NOLOGIN`、`SUPERUSER / NOSUPERUSER`、`ENABLED / DISABLED`、`SYSTEM_CATALOG_MANAGER / NOSYSTEM_CATALOG_MANAGER`;同一条语句不能重复指定同一属性。
`ALTER USER` 与 `ALTER ROLE` 的属性、口令修改语法通用。可使用 `LOGIN / NOLOGIN`、`SUPERUSER / NOSUPERUSER`、`ENABLED / DISABLED`、`SYSTEM_CATALOG_MANAGER / NOSYSTEM_CATALOG_MANAGER`、`ALLOW_SUDO_AUTH / NOALLOW_SUDO_AUTH`;同一条语句不能重复指定同一属性。
SUPERUSER 可设置目录管理属性。该属性默认为 false,不经成员关系继承;只有自身启用且允许登录的身份能使用它。目录管理者可以进入 system 库、查看身份目录、创建普通身份、修改普通身份属性与口令及删除普通用户身份。它不能管理 SUPERUSER 或其他目录管理者,不能授予这两个特权属性,不能执行 `DROP ROLE`,也不会因此获得创建业务数据库、直接修改系统表或读取业务数据的权限。受限身份变更在存储写锁内再次检查目标权限。
SUPERUSER 可设置目录管理属性。该属性默认为 false,不经成员关系继承;只有自身启用且允许登录的身份能使用它。目录管理者可以进入 system 库、查看身份目录、创建普通身份、修改普通身份属性与口令及删除普通用户身份。它不能管理 SUPERUSER、其他目录管理者或启用 sudo 认证的身份,不能授予这三个特权属性,不能执行 `DROP ROLE`,也不会因此获得创建业务数据库、直接修改系统表或读取业务数据的权限。受限身份变更在存储写锁内再次检查目标权限。
## 全部身份及凭据字段
@@ -44,6 +45,7 @@ SUPERUSER 可设置目录管理属性。该属性默认为 false,不经成员
| `can_login` | 是否允许直接登录;仅允许登录仍不足以保证登录成功,还需启用及正确口令 | `LOGIN / NOLOGIN` |
| `is_superuser` | 是否具有最高权限;不通过角色成员关系继承 | `SUPERUSER / NOSUPERUSER` |
| `system_catalog_manager` | 是否具有受限的系统目录管理能力;不继承、不替代数据访问授权 | `SYSTEM_CATALOG_MANAGER / NOSYSTEM_CATALOG_MANAGER`,仅 SUPERUSER 可设置 |
| `allow_sudo_auth` | 是否接受受信任本机提升身份认证;默认 false,不继承,不改变业务权限 | `ALLOW_SUDO_AUTH / NOALLOW_SUDO_AUTH`,仅 SUPERUSER 可设置 |
| `enabled` | 是否启用身份;禁用后不能登录,也不再贡献角色继承权限 | `ENABLED / DISABLED` |
| `identity_version` | 身份目录格式与旧数据迁移标记;旧记录可为 0,当前统一身份格式为 1 | 系统维护;不是会话版本 |
| `registered_at` | 身份创建时间;旧记录缺失时由迁移补齐 | 系统记录 |
@@ -55,7 +57,68 @@ SUPERUSER 可设置目录管理属性。该属性默认为 false,不经成员
| `id` | 对应身份目录中的编号 |
| `password_hash` | 口令哈希,不是明文口令;通过口令修改命令更新 |
内部账号协议可将身份字段和 `password_hash` 合并为账号对象,但哈希不属于公开的角色展示内容。`SHOW ROLES` 展示全部身份的编号、名称、登录能力、最高权限、启用状态、目录管理属性和创建时间,不返回口令哈希,也不展示全部内部字段。系统表由身份管理接口维护,不应直接修改。
内部账号协议可将身份字段和 `password_hash` 合并为账号对象,但哈希不属于公开的角色展示内容。`SHOW ROLES` 展示全部身份的编号、名称、登录能力、最高权限、启用状态、目录管理属性、sudo 认证开关和创建时间,不返回口令哈希,也不展示全部内部字段。系统表由身份管理接口维护,不应直接修改。
## 本机 sudo 认证
此入口适用于 Linux、macOS 和 Windows,默认关闭。Unix 有效 UID 0(通常通过 sudo 获得)映射到 `[server] sudo_auth_user`;Windows 提升令牌中的 Administrators 成员映射到同一配置身份。不会使用客户端上报的操作系统用户名、`SUDO_USER`、数据库用户名或角色属性作为操作系统身份凭据。
先用数据库 SUPERUSER 配置目标身份,例如:
```sql
ALTER USER local_operator ALLOW_SUDO_AUTH;
```
目标身份仍需 `LOGIN` 与 `ENABLED`;按需要另行授予业务权限。即使它是普通身份,sudo 登录也只获得该身份已有的权限。`SUPERUSER` 和 `SYSTEM_CATALOG_MANAGER` 不会隐含允许 sudo 登录。
在服务端与 CLI 使用的配置中显式设置映射:
```toml
[server]
sudo_auth_user = "local_operator"
```
停止原服务后,以 Unix root 或 Windows 提升后的管理员重启 `chusql-server`,使用受管理员保护的配置文件、数据目录和程序路径。以相同提升身份运行 CLI;Unix 示例:
```sh
sudo csql --config /absolute/path/settings.toml --sudo -e "SELECT 1"
```
Windows 在提升的终端中运行 `csql --config C:\absolute\path\settings.toml --sudo -e "SELECT 1"`。`--sudo` 不读取数据库口令,省略 `--user` 时使用配置映射。显式指定其他账号也不能绕过服务端映射。普通 TCP/CLI 口令登录与 Web 登录保持原有口令认证;远程连接无法请求此入口,CLI 也校验实际连接对端为回环地址。
服务启动时产生独立随机凭据。Unix 放在 `/var/run/chusql-sudo-<实际端口>/credential`,目录 root 所有且为 0700,文件为 0600;Windows 放在系统目录的 `config\ChuSQL-Sudo-<实际端口>\credential`,使用不继承的 Administrators / SYSTEM 专属 DACL。读取入口检查当前进程提升身份;Unix 还验证文件所有权、权限和类型,拒绝链接,Windows 拒绝重解析路径。凭据不会传输到网络,CLI 使用 HMAC-SHA256 回应绑定连接、账号和服务实例的一次性随机挑战;挑战 60 秒后过期,每次认证尝试都会消费挑战,失败按账号限流。
服务端在 stderr 记录启用映射和认证结果,不记录凭据或证明。成功登录更新身份目录的 `last_login_at`。`ALTER USER local_operator NOALLOW_SUDO_AUTH`、`DISABLED`、`NOLOGIN`、修改口令或删除身份都会使既有连接在下一次请求时失效。映射配置清空并重启服务后,本机入口关闭。
正常停止服务时清理凭据。异常退出可能留下端口专属目录;再次启用会明确报错,不读取或覆盖旧凭据。确认对应服务已停止后,由提升身份清理该目录再启动;不要把凭据复制到普通账号可访问的目录。
## 对象授权
业务授权绑定数据库编号与表编号,身份与所有者绑定身份编号。删除后重建同名数据库、表或身份会获得新编号,旧授权不会随名称恢复。对象存在性以 catalog 为准;不同数据库的同名表互不共享授权。
| 对象范围 | 权限 | 默认与所有者能力 |
| --- | --- | --- |
| DATABASE 库名 | CONNECT、CREATE | PUBLIC 默认 CONNECT;CREATE 允许建表;数据库所有者可管理该数据库 |
| TABLE 库名.表名 | SELECT、INSERT、UPDATE、DELETE | 默认无数据权限;表所有者拥有四项数据权限、结构管理及转授权能力 |
| 库名.* | 四项表级数据权限 | 覆盖该数据库内现有及未来的表;不包含数据库 CREATE |
`ALL` 按对象范围展开。未限定表名与 `*` 绑定当前数据库。数据库所有权不自动赋予其他身份所建表的数据权限;启用的所有者角色可以通过成员关系贡献所有权,最高权限属性仍不继承。创建数据库及管理 DOMAIN 仍要求 SUPERUSER;所有权不能通过 REVOKE 移除,当前没有所有权转移命令。
```sql
REVOKE CONNECT ON DATABASE sales FROM PUBLIC;
GRANT CONNECT, CREATE ON DATABASE sales TO analyst;
GRANT SELECT ON TABLE sales.orders TO reader WITH GRANT OPTION;
GRANT SELECT ON sales.* TO analyst;
REVOKE SELECT ON sales.orders FROM reader;
```
PUBLIC 是所有身份共享的虚拟角色,不能新建同名用户或角色。已有同名旧身份仍按真实身份编号解析,删除该身份后才使用虚拟 PUBLIC。撤销 PUBLIC CONNECT 会关闭数据库的默认连接权限,其他显式 CONNECT 或数据库所有权仍有效;重新授予会恢复默认连接权限。
每次授予记录授予者。普通身份须持有有效 grant option 或对象所有权,可撤销自己的授予;所有者和 SUPERUSER 可撤销对象上的其他来源。撤销、删除身份或成员变更会清理失去合法来源的转授权链;其他独立来源保留,循环转授不能维持失去根来源的权限。禁用身份暂停权限贡献,保留所有权及管理员直接授权;失去启用授予者的下游转授权会被清理。
旧名称授权在首次启动时一次性绑定现存对象和身份编号。旧记录不含授予者,迁移为管理来源;旧全局 `*` 仅转换成当时现存表的明确授权,不扩展到未来对象。已经不存在的对象和身份记录被清理。迁移后不再读取旧名称授权作为鉴权依据。
权限快照位于受保护系统目录,经 WAL 原子保存;普通存储请求不能访问该目录。SQL、TCP 和 Web 使用同一服务端检查;TCP 原始存储入口仅允许 SUPERUSER。查询与授权变更通过共享读写锁排序,建表前持久化创建者声明供重启恢复所有权。事务提交重新检查权限和稳定对象编号,权限撤销或对象替换后不能提交旧事务。
## 属性、授权与会话的关系