修订记录(相对初版):
- 优先级重排——按"收益高、贴现有 TCP 驱动架构的先做,引入新传输架构的集中一期,模型不匹配的延后"排序。
- §二别名机制拍板:走 mariadb 先例(Lua 侧归一化),Rust 零改动;废弃"Rust 侧加 name 映射"方案。
- 修正事实错误:官方
clickhousecrate 是 HTTP-only,不是"HTTP + native TCP"。- §3.2 测试计划按传输方式拆分,消除"HTTP 方言
dialect_for返回 Some"与"HTTP 仍返回 None"的自相矛盾。- 补全 Lua 触点清单:
SYSTEM_SCHEMAS、ident.lua/dml.lua引用分支、dialect_icons、ai/system_prompt.lua方言清单、双端to_url()同步。- 容器方案修正:MSSQL arm64/健康检查、Trino tpcds 需挂 properties 且 tpch 只读、ClickHouse 连接端口语义 pin 为 HTTP 8123。
- 措辞修正:URL 是"构造"不是"解析";验收标准的"建库/建表"改为"列库/列表"。
实施状态:Phase 0(别名)、Phase 1(MSSQL)、Phase 2(HTTP 传输 + ClickHouse,含 Lua/Rust/容器/playground/diag) poste.nvim main 直接演进(原
../poste-for-dbworktree 流程已废弃)。
poste-db 目前支持的 dialect:postgres(含 postgresql 及 PG 系别名)、mysql(含 mariadb 及 MySQL 系别名)、sqlite、mssql。
实现成本分布(触点按现状逐一核对过,完整清单见文末附录):
- Lua 侧(poste-db.nvim):
constants.lua:SUPPORTED_DIALECTS白名单 +SYSTEM_SCHEMAS+DIALECT_ALIASES归一表 +URL_SCHEMES/dialect_from_url嗅探 +DIALECT_DEFAULT_PORTSconnections.lua:URL 构造(消费default_port,scheme 无关)+dialect_iconsident.lua/dml.lua:标识符引用与值转义的方言分支completion/data.lua:tables_db_flag的 schema/database 归类 + 方言关键字snippets.lua:pick_template模板变体source_format.lua:格式化器dialect_mapai/system_prompt.lua:方言清单提示词
- Rust 侧(poste.nvim,直接在 main 改):
poste-core的Protocol枚举加变体sql_connection.rs的ConnectionConfig::to_url()——与 Lua 的resolve_connection_url是镜像实现,必须双端同步- 驱动分发(
sql_executor/、exec_file.rs、session.rs、introspect.rs的 scheme/dialect 链) sql_dialect.rs的Dialecttrait 实现(introspection SQL、引号、端口、类型映射)
mariadb走 MySQL 线协议,Lua 侧归一化为mysql(DIALECT_ALIASES先例)。
架构要点:Dialect trait 只封装 SQL 方言差异(introspection SQL、引号、端口、类型映射),与传输方式无关。HTTP 是传输属性,不是 dialect_for 的排除理由——ClickHouse/Trino 这类"SQL over HTTP"的数据库可以有自己的 Dialect 实现,同时由传输层路由到 HTTP 执行器;dialect_for 对 Redis 等非 SQL 协议仍返回 None。
排序原则:收益高、能贴现有 TCP 驱动架构的先做;引入新传输架构(HTTP)的集中到一期做,摊薄基建成本;数据/连接模型不匹配的最后,可延后。
| 优先级 | Dialect | 驱动/传输 | 工作量 | 风险 | 说明 |
|---|---|---|---|---|---|
| 0 | PG/MySQL 兼容系别名 | 复用现有 TCP 驱动 | ≈0 | 无 | ✅ 已落地(§二) |
| 1 | MSSQL / SQL Server | tiberius(纯 Rust TCP) |
中 | 低-中 | ✅ 已落地。容器侧的坑(arm64 模拟、健康检查)见 §四 |
| 2 | ClickHouse | reqwest 直连 HTTP | 中 | 中 | ✅ 已落地。实现注记:官方 clickhouse crate 是 SELECT 取向(把 FORMAT x 拼进 SQL、强制 readonly=1),无法跑 DDL/DML/临时表/SET——改用 reqwest 裸 HTTP(default_format=JSON,meta 带列类型),即 poste-exec 首个 HTTP 传输,Trino 直接复用 |
| 3 | Trino | 复用 reqwest HTTP 传输 | 中 | 中 | 数据湖联邦查询主流;只做 Trino,Presto 不在范围(协议已分叉,不做别名) |
| 4 | DuckDB | duckdb crate(链接 libduckdb 原生库) |
高 | 高 | 仅文件模式(见 §四);链接 libduckdb 构建重,且与服务器连接模型不一致。可延后,延后不阻塞任何验收 |
机制:走 mariadb 先例,Lua 侧归一化。 constants.lua 的 DIALECT_ALIASES(alias → base 方言)+ connections.lua 在白名单检查前归一化。Rust 零 diff——别名在到达 Rust 之前已经变成 postgres/mysql。
设计后果(接受的 trade-off):
- 图标、completion、AI prompt 一律按 base 方言呈现,别名不引入任何专属分支。每个别名的持续成本 = 一个 map 条目。
- 展示层的 provider 区分(如 supabase/neon 角标)不做,留待有真实需求再加
provider字段。 - 归一化后
resolve_connection_url自然构造出postgres:///mysql://,杜绝"二元 scheme 兜底把别名静默构造成mysql://"的误路由。
cockroachdb、yugabyte、aurora-postgres、neon、supabase、timescaledb(实质是 pg 扩展)
tidb、singlestore、aurora-mysql、vitess、planetscale
新增 dialect 的测试分三层,全部纳入现有 tests/run.sh 体系。
纯 stub 测试,跟随现有 tests/sql/*_spec.lua 惯例:
| 文件 | 要补的用例 |
|---|---|
sql_constants_spec.lua |
SUPPORTED_DIALECTS/is_sql_dialect;DIALECT_ALIASES 归一断言;dialect_from_url 各前缀;default_port;SYSTEM_SCHEMAS 新增条目 |
sql_connections_spec.lua |
断言构造出的 URL(代码路径是构造,不解析用户输入的 scheme):新方言前缀、默认端口正确;别名断言落到 base 协议 URL(防二元兜底误路由) |
sql_ident_spec.lua + dml |
ident.quote/quote_literal、dml.lua 值转义的新方言分支 |
sql_completion_spec.lua |
方言关键字/内置函数;tables_db_flag 归类 |
sql_snippets_spec.lua |
pick_template 命中新方言变体 |
sql_source_format_spec.lua |
各格式器 dialect_map 补条目 |
tables_db_flag 归类(现状):postgres/postgresql/sqlite/mssql → --schema;mysql/mariadb(及未知)→ --database。
SYSTEM_SCHEMAS(semantic_diagnostics.lua 在消费):现含 pg 系 + mysql 系 + sys + guest;ClickHouse/Trino 落地时加 system。
按传输方式拆分:
- TCP 驱动方言(如 MSSQL):在
crates/poste-exec/src/sql_dialect.rs写Dialectimpl + 测试模块,镜像现有test_postgres_dialect()/test_mysql_dialect()/test_sqlite_dialect(),覆盖 trait 全部方法(含type_mapping);dialect_for断言 Some;Redis 等非 SQL 协议仍 None;introspection SQL 快照断言 - SQL over HTTP 方言(ClickHouse/Trino):同样在
sql_dialect.rs实现Dialect并做快照测试;dialect_for返回 Some(Dialect 与传输解耦);传输分发单测断言路由到 HTTP 执行器
tests/diag/diag_<dialect>.lua(参照diag_sql.lua/diag_mssql.lua):环境变量 gate(如MSSQL_TEST_URL+POSTE_BINARY),未设则 SKIP;连真实容器跑 introspection 全链路 + 特性 SQL- Busted 单测必须保持无 Docker 可运行
- 只做 Lua 侧测试:
connections.toml能写别名、归一化后 URL 落到postgres:///mysql://;复用现有容器
在 playground/docker-compose.yml 追加服务(宿主端口避开已用的 15432/13306/13307/11433/18080/18123/19000):
| 服务 | 镜像 | 宿主端口 | 健康检查 |
|---|---|---|---|
mssql ✅ |
mcr.microsoft.com/mssql/server:2022-latest(amd64-only,声明 platform: linux/amd64) |
11433:1433 |
/opt/mssql-tools18/bin/sqlcmd ... -C -b(必须 -C 信任自签证书) |
clickhouse |
clickhouse/clickhouse-server:latest |
18123:8123(HTTP,连接层只用这个)/ 19000:9000(native,备用) |
wget -qO- http://localhost:8123/ping 返回 Ok |
trino |
trinodb/trino:latest |
18080:8080 |
curl -f http://localhost:8080/v1/info 且 starting=false |
duckdb |
官方 CLI 构建的文件模式(无服务进程) | 无需端口 | 文件模式无探活;HTTP 服务模式明确不做 |
初始化方式差异与坑:
- MSSQL(已踩平,见
playground/init/mssql/entrypoint.sh):无官方 arm64 构建,Apple Silicon 靠 Rosetta/QEMU,启动慢需start_period: 120s;不读docker-entrypoint-initdb.d,自定义 entrypoint 启动sqlservr后 sqlcmd 灌种子;种子用 GO 分批(sqlcmd 消费),GENERATE_SERIES不能用AS s(n)列别名(列名固定value),datetime2不支持- 整数算术;每次重启重播种子(数据重置),与其它服务不同 - ClickHouse:原生支持
/docker-entrypoint-initdb.d/*.sql。连接语义 pin:clickhouse://user:pass@host:18123/db走 HTTP 8123 - Trino:镜像默认只启用
tpch(+system);tpcds 需挂etc/catalog/tpcds.properties;tpch只读,自建核心表进memorycatalog(只支持 CTAS、重启即失,需启动后置脚本重建) - DuckDB:入口脚本用 duckdb CLI 生成种子
.duckdb(同 sqlite 的test.db模式);URLduckdb:<path>沿sqlite:前缀先例;Rust 驱动duckdbcrate(链接 libduckdb,构建重)
在 playground/init/<dialect>/ 放种子脚本,遵守现有约定:单文件 <8KB、手工 3-7 行真实样例 + 批量生成、依赖库无关。
- 跨引擎核心表(
users/orders/order_items)保证同一份查询全容器跑通;Trino 例外:核心表放memorycatalog 启动重建,tpch/tpcds只读联邦查 - README 补各库表/行数清单(参照 mssql 小节格式)
playground/queries/<dialect>.sql:头注释列特性 + -- @connection/-- @database 头。段落分隔用 -- 注释,不要用 ### 行(exec-file 只剥离独立的 ### 行)。参照 mssql.sql。
- 新方言可写入
connections.toml并正常连接 - 连接后 introspection(列库/列表/列/索引)正常
- completion 与 snippets 对新方言生效
source_format.lua映射补齐,格式化不报错- 3.1/3.2 单元测试无 Docker 全绿;3.3 diag 全绿
- docker-compose 新增服务健康检查通过,初始化数据就位
- playground SQL 脚本完整跑通
- 兼容系别名:Rust 零 diff
- Lua 触点清单全覆盖(含
ai/system_prompt.lua) - 双端
to_url()同步,无静默误路由
别名铺开✅MSSQL✅- HTTP 传输层 + ClickHouse(下一步:poste-exec 首个 HTTP 传输在此消化)
- Trino(复用 HTTP 传输)
- DuckDB(可选,延后)
每步完成标准:3.1/3.2 测试先行并全绿 → docker 服务健康检查 → init 数据就位 → playground 跑通 → diag 全绿 → ai/system_prompt.lua 已更新。
| # | 文件 | 要动什么 |
|---|---|---|
| 1 | constants.lua |
SUPPORTED_DIALECTS;别名进 DIALECT_ALIASES;系统 catalog 进 SYSTEM_SCHEMAS;URL_SCHEMES 加前缀;DIALECT_DEFAULT_PORTS 加端口 |
| 2 | connections.lua |
URL 构造与显示走 default_port()(通常零改动);dialect_icons 加图标 |
| 3 | ident.lua |
quote 标识符引用分支;quote_literal 值转义 |
| 4 | dml.lua |
编辑流 DML 生成中的方言分支 |
| 5 | completion/data.lua |
tables_db_flag(--schema vs --database)归类 |
| 6 | snippets.lua |
CATEGORIES 加方言变体(pick_template 自动生效) |
| 7 | source_format.lua |
sqlfluff / sql-formatter 的 dialect_map |
| 8 | ai/system_prompt.lua |
方言清单句 |
| 9 | specs | sql_constants / sql_connections(URL 构造断言,防静默误路由)/ sql_ident / sql_snippets / sql_completion(tables_db_flag) |
| # | 位置 | 要动什么 |
|---|---|---|
| 1 | poste-core/src/request.rs |
Protocol 变体 |
| 2 | poste-exec/src/sql_connection.rs |
to_url() dialect 分支 + test_connection 探活。与 Lua resolve_connection_url 是镜像实现,双端必须同步 |
| 3 | poste-cli/src/exec_file.rs |
scheme→protocol 链 + run_statements 分发 + exec_<dialect> + summary dialect 字符串 |
| 4 | poste-cli/src/session.rs |
scheme 链 + session_<dialect>(会话语义随驱动而异:temp table/事务,参考 sql_executor/mssql.rs 的批量路径注记) |
| 5 | poste-cli/src/introspect.rs |
dialect_name 链。此链静默 fallback 到 postgres——新方言必须显式加分支 |
| 6 | poste-exec/src/sql_dialect.rs |
Dialect impl + dialect_for 分支 + 快照测试(参考 test_mssql_dialect) |
| 7 | poste-exec/src/sql_executor/{mod.rs,<dialect>.rs} |
驱动模块 + 分发 match + make_response 协议名 |
| 8 | poste-exec/src/sql_introspect/{mod.rs,<dialect>.rs} |
introspect 驱动 + 分发(item 形状对齐 postgres.rs) |
| 9 | poste-exec/src/sql_ddl.rs |
DdlGenerator + ddl_for 分支 |
| 10 | poste-cli/src/connection.rs |
CLI 连接列表 emoji(cosmetic) |
- TDS 临时表作用域:
CREATE TABLE #t必须走批量路径(simple_query),RPC prepare/execute 作用域退出即丢弃——见mssql_execute的is_ddl_stmt路由 - MERGE 必须以分号结尾,而语句切分会剥掉尾分号——执行层补回
- 事务无需专门 API:BEGIN/COMMIT/ROLLBACK 走批量路径,服务器以 ENVCHANGE 回报描述符
- 值转换参考
mssql_value_to_json(tiberiustry_get返回Option<T>,借用型&str/&[u8],数值/日期走value.rs共享 fallback) - HTTP 型方言:
clickhousecrate 是 HTTP-only(RowBinary over HTTP);Dialect与传输解耦,参考"背景"一节