ADR-0029:设计器只读查询(探库)设计简报
状态
提议中(设计简报,非实施决策)——2026-08-09;同日三次修订 §5「驱动管理」。本文回答「要不要做、做成什么样」,不授权开工;进入 roadmap 需人工显式拍板并解封 control-matrix.md 中「📋 延期:不扩 JDBC 查询台」的现状。
修订记录(2026-08-09,同日,共三条用户反馈):
- 「不能用编译期的四库 Maven 依赖,数据库类型 、版本太多了」——四库不能是永久天花板,需要不推倒重来的扩展路径
- 「你的所有思考、设计不要出现让用户可能出现阻塞的情况,用着用着需要让用户帮你补齐一些要素才能继续」——任何扩展机制都不得让建模者在使用预览功能时被要求补齐驱动 jar / 驱动类名 / 联系管理员才能继续;这条约束优先级高于「覆盖更多库类型」
- 「我希望编译期除了本项目要的驱动,其他的都不要打入」——
backend模块自身的编译/运行期依赖只应包含应用自举所需的驱动(系统库erd/martin用的 MySQL),PostgreSQL/Oracle/SQL Server 这类服务用户数据源的连接器不应算进backend模块的 compile/runtime scope,即便它们仍需要出现在默认发布镜像里
三条反馈共同推出的结论:系统驱动 + 官方连接器包 + 扩展连接器包三层模型(见 §5)。day-1 对建模者的可见支持面不变(仍是四库,零阻塞),变化的是"这四库如何被组装进最终镜像"这件事的架构归属。
背景
历史上 FE 有一版查询台页面(pages/design/query、pages/dataQuery),W2 阶段已判定 overbuilt 并删除源码(见 product-capability-map.md「死壳与过度建设」);后端 QueryInfoController/SqlGuard.assertReadOnly 仍在,但走遗留 @Dynamic + SqlHelperDsManager(按 dbName 字符串路由全局注册数据源),不经过项目 data_sources 表 + DataSourceAcl 这条现代权限 路径(ADR-0008/R-DATA-02 修复时特意排除在外)。
用户希望重新认真评估这个功能:入口在哪、进去做什么、要不要留痕,并补充一个此前未覆盖的维度——多数据库驱动怎么管理。本文是该评估的结论。
决策要点
0. 先回答「要不要做」
结论:做,但缩到最小、且改名——不做"查询台/SQL Workbench",只做**「表数据预览」**(英文暂定 Data Preview,路由/组件名避免出现 query/sql 字样)。理由:
- vision.md 明确「不做 dbdiagram 复刻」「不在别人生态位内卷」——一个通用 SQL 执行框(DBeaver/Navicat 那种)正是别人的生态位;但从画布选中表一键看真实数据是强绑定在"建模"上下文里的动作,服务的是"我建的模型对不对得上库里的真实数据"这个核心建模疑问,不是通用查询能力
- 命名即产品边界:叫「查询」会不断被用户诉求拉向「能不能加个 WHERE 输入框」「能不能存常用查询」「能不能导出 CSV」——每一步都合理,但走到底就是重建一个被裁掉的查询台。叫「预览」从命名上就先天排斥这些诉求
- 与北极星的关系:不直接产生「版本保存」,但能减少「建完模型却不确定字段/数据对不对」的疑虑,间接降低「建模半途放弃」的流失—— 是支撑性功能,不是杠杆功能,排期优先级应低于任何直接服务北极星的切片
1. 入口(入口要少、要与建模强绑定,不与建模抢注意力)
| 入口 | 决策 | 理由 |
|---|---|---|
| 画布表节点底栏「字段 | 索引 | 元数据 | 触发器」旁加第 5 项「预览数据」 | ❌ 不加 | 底栏已经 4 项快到密度上限(design-principles 表节点密表纪律);且底栏 4 项全是改模型结构的动作,预览数据是看外部真实数据,语义不同类,混进去会让用户误以为预览数据也能编辑结构 |
| 表节点右键菜单「预览数据…」 | ✅ 加 | 上下文即工具(原则 3);不占底栏常驻空间;只有绑定了数据源且该数据源类型当前有可用驱动的表才出现该项,任一条件不满足都不出现(不做灰置死 affordance,也不做「点了才告诉你不支持」)——驱动可用性判定见 §5.3 |
| 左树表节点右键同款「预览数据…」 | ✅ 加 | 与画布对称,键盘漫游用户(左树方向键流)同样可达 |
| 项目菜单 / DesignLayout 侧栏新增一级入口「查询」「数据预览」 | ❌ 不加 | 这正是被裁掉的旧入口的翻版——脱离表上下文的独立入口等于重建通用查询台;且会与「导入/导出/设置」等结构动作抢侧栏位置 |
| Home / 顶栏 | ❌ 不加 | 完全脱离建模上下文,纯粹分心 |
命令面板 Cmd/Ctrl+K 搜表后追加「预览 {表名} 数据」 | ✅(v1 再加,非 v0 必须) | 高手回路一致性;v0 先只做右键,验证需求再扩键盘入口 |
首次接触空态:右键菜单项本身不需要空态设计(菜单项要么出现要么不出现)。预览弹层/面板打开后若表未绑定数据源或数据源未同步该表 → 明确文案「这张表还没连接数据源,去『数据源设置』绑定后可预览真实数据」+ 跳转链接,禁止空白弹层。
2. 进去后做什么(流程)
载体选型:Drawer(侧滑面板),不用整页路由、不用新 Tab 签。理由:预览是"瞅一眼就关"的轻动作,Drawer 关闭即回到画布原状态,不打断建模流;整页路由(旧版做法)意味着离开设计器上下文,与"支撑性功能"定位不符。
Happy path:
- 表节点/左树右键「预览数据…」→ 若该表 ≥1 个绑定数据源,直接用当前项目默认数据源(
profile.defaultDataSourceId,已有字段)打开 Drawer;若表所在项目有多数据源且未设默认 → Drawer 顶部一个Select让用户选,选完记住本次会话选择(不写库,纯前端 state) - Drawer 打开即自动执行按方言生成的
SELECT * FROM {table} LIMIT/TOP/FETCH 100(见下「驱动管理」),不需要用户点两次——右键→预览就该看到数据,多一次「运行」点击是摩擦 - 结果区:只读表格(复用 antd
Table,非 JExcel——JExcel 是可编辑网格,用在这里会暗示"这些数据能改");列头显示字段名 + 从projectJSON读到的类型(如果模型里有这张表) - Drawer 顶部工具条:只有「刷新」「关闭」两个动作 + 一行只读文案「显示前 100 行 · 耗时 xx ms · {数据源名}」;不做 WHERE 输入框、不做排序点击、不做导出——这些都是"查询台"功能,不是"预览"功能,v0/v1 都不做
- 失败态:连接失败/超时/SQL 报错 → Drawer 内联错误文案(同「零静默失败」纪律),提供「重试」;不关闭 Drawer、不弹二次 Modal
与模型树/当前实体选中的关系:预览是"从表出发"的单向动作,不反向影响画布选中态、不影响当前打开的签页。预览 Drawer 与表设计签、字段编辑器可同时开(Drawer 浮层,不抢 Tab 位)。
多数据源/多 schema:一张 entity 只对应"当前选中的一个数据源连接"里的"一张真实表";不做"同时对比多个数据源同名表数据"这种高级功能(v0/v1 非目标)。
大结果集:服务端硬顶 100 行(体验默认)+ 500 行硬上限(PaginationInnerInterceptor.setMaxLimit(500L) 已是全局配置,天然生效);前端不提供"加载更多"/翻页——预览就是"瞅一眼",需要看更多数据是运维/DBA 场景,应该去真正的数据库客户端,不是本产品要接的需求。
超时:当前 connector/queryInfo 全套命令都没有 JDBC 查询超时设置(已实测确认,Statement.setQueryTimeout 零调用点)——这是必须补 的新工作项,不是"顺带"的:预览必须设 5–10s 服务端超时,超时按「失败态」文案处理(「查询超时,可能是表数据量较大或数据源响应慢」),防止一次预览把连接池打满、拖垮设计器其它接口。
3. 留痕 / 审计 / 历史
| 议题 | 决策 | 理由 |
|---|---|---|
要不要记 QueryHistory(已有实体/表) | 要,但目的是审计不是"最近查询"UX | 项目里有数据源账密(哪怕加密落库),谁在什么时候对哪个数据源跑过什么 SQL 是安全审计基本盘,尤其自托管场景管理员要能查"谁看过生产数据源的数据" |
| 记录粒度 | 每次预览记 1 行:projectId / dataSourceId / tableName / 执行的 SQL 文本 / 执行人 / 耗时 / 行数 / 成功或失败 | 现有 QueryHistory 缺 projectId/dataSourceId(只有遗留 dbName/queryId),若复活必须先补这两列(Flyway 加列,遵 schema-migration 规则) |
| UI 是否暴露「历史记录」面板 | v0/v1 不暴露;只落库不做前端列表 | 暴露"历史"= 暗示"这是一个值得回顾管理的功能",会把用户心智从"预览"拉回"查询工作台";等真出现审计需求(比如团队管理员要看谁查了敏感数据)再单独做一个后台审计视图,不挂在设计器 UI 上 |
| 收藏/常用查询(Saved queries) | v0/v1 明确不做 | 这是"查询台"最典型的特征功能之一,做了就等于承认这是查询台 |
| 协作可见性:队友能看到我预览了什么表吗 | 不能,也不应该 | 预览是本地只读探索行为,不是建模变更,不该走协作广播(SocketIO presence/sync);审计日志是给项目所有者/管理员事后查的,不是实时给队友看的 feed |
| 隐私 | 历史记录里的 SQL 文本本身可能含用户手打的 WHERE 条件(哪怕 v0 没有 WHERE 输入框,SELECT * FROM t LIMIT 100 也不含敏感值,风险很低);结果数据本身绝不落审计表,只记 SQL 文本+行数+耗时,不记返回的数据内容 | 避免审计表本身变成第二个敏感数据泄露面 |
4. 用户没问但重要的点
安全(在 ADR-0008/R-DATA-02 已有基础上,预览功能新增的面):
- 必须走
dataSourceId→DataSourceAcl.requireOwned现代路径,禁止复活queryInfo/exec现在用的@Dynamic/dbName遗留路径(那条路径不做项目成员校验,谁都能猜/枚举dbName打别的项目数据源) - 只读白名单沿用现成的
SqlGuard.assertReadOnly(SELECT/EXPLAIN/SHOW/DESC,jsqlparser 校验)——不需要新写 - SSRF/DNS-rebind 防护复用
JdbcUrlGuard.assertAllowedAndPin(连接前把 host 钉成已放行 IP)——预览功能等于新增一条"任意时刻建临时连接跑 SELECT"的路径,必须走这条防护,不能图省事直连
权限:谁能预览:
- 项目成员(
project_user)才能预览,与项目内其它操作同级——不单独设"预览权限"这种新维度(YAGNI;等真出现"我要给某人只读预览不给编辑"这种诉求再拆) - 只读分享(
/s/:token)访客不能预览——ADR-0008 已规定分享响应清空profile.dbs,访客拿不到任何数据源凭证,预览入口在分享壳里天然不出现(前端按shareContext隐藏,与 B 层探测同一套 guard)
版本交互:查的是活库,不是模型快照——必须讲清楚这个错位:
- 预览连接的是当前活库的真实数据,与设计器里的
projectJSON/版本快照是两件独立的事(呼应 ADR-0022 双层一致性:projectJSON↔版本 是 A 层,活库 schema 是 B 层,Data Preview是 B 层的"数据"维度,之前 B 层只做了"schema 结构"探测) - 用户可能在模型里改了字段名/加了字段但还没同步到活库(也可能反过来,活库结构已经改了但模型没重新逆向)——预览这时可能显示"表里没有这一列"或者"这一列在模型里没有",必须用文案讲清楚,不能让用户以为"预览结果 = 模型当前状态"
- 具体做法:Drawer 顶部沿用 B 层探测已有的语义分离色(
dualLayerTokens),若已知该数据源与当前模型处于behind/diverged(SchemaProbeCommand已有判定),预览结果上方加一行 muted 提示「活库结构可能与当前模型不同步,去『版本』页探测详情」——复用已有能力,不重新发明
速率限制/成本/误伤大表:
- 每会话/每用户预留一个轻量节流(比如 5 次/分钟),理由不是"省钱"(自托管无 token 成本),是防止一次误操作(比如快捷键连点)把下游数据源连接池打满
SELECT *误伤大表:v0 固定LIMIT/TOP/FETCH 100,用户在这个功能里不能自己改 SQL、不能去掉 LIMIT——从产品设计上直接消灭"不小心跑全表"的可能性,这也是"不做 WHERE 输入框"决策的另一个理由(一旦允许自定义 SQL,就要操心用户是不是打了个没有 LIMIT 的语句)
移动端:不适用;产品明确不做移动端(vision.md),预览面板也不用考虑响应式深度适配。
命名:已在 §0 定论——「预览数据」/Data Preview,禁用「查询」「探库」「SQL」出现在产品可见文案与路由/组件命名里;仅后端内部实现细节(如 Command 类名)可以叫 PreviewTableDataCommand 之类,不强制忌讳,但 Controller 路径建议 connector/tablePreview 而非 queryInfo/*(彻底斩断与遗留路径的语义联系,防止后续维护者顺手把功能又焊回遗留 @Dynamic 路径)。
成功指标:不是北极星本身,是先行指标:「预览使用次数」与「预览后 24h 内是否发生一次非空 diff 版本保存」的相关性——如果用了预览的用户版本保存率没有显著差异,说明这个功能对北极星没有实际拉动,应该降级维护或砍掉。这个指标不需要专门仪表盘,能从 QueryHistory(补了 projectId 后)+ db_change 表跑一次性 SQL 分析即可,不值得为此建产品化报表。
Kill 条件(dogfood 失败判据):
- 上线 4 周内,预览功能使用次数 < 项目活跃数的 10%,且无用户主动反馈"离不开这个"——降级为「维护但不再投入」,不删代码(沉没成本已付,留着比删更省事),但停止任何后续增强(不做 WHERE、不做导出等诉求一律拒绝)
- 若实测发现预览功能显著增加"活库连接池压力"投诉/故障(尤其自托管小内存部署),先加节流/超时收紧,仍无效则收回入口(右键菜单项移除),后端接口保留但不暴露
5. 驱动管理(修订:核心层 + 部署期扩展包,两条硬约束驱动的重新设计)
5.0 修订动机
初版结论"驱动是编译期 4 库 Maven 依赖,够用,不做插件化"被用户三条反馈依次推翻:
- 不能封顶:数据库类型 + 版本组合太多,把"只支持 4 种"钉成永久架构假设是错的,需要一条不推倒重来的扩展路径
- 不能阻塞建模者:任何扩展机制都不能让"预览数据"这个建模内动作,中途需要用户去补一个东西(上传 jar、找管理员装驱动、填驱动类名解锁)才能继续——这条约束优先于"覆盖更 多库类型",覆盖面扩展必须做成对建模者完全不可见的事,发生在部署/运维时区,不发生在建模者点击时区
- 编译产物要瘦身:
backend模块自身的 Mavencompile/runtimescope 只应包含应用自举所需的驱动;服务用户数据源的连接器(哪怕是官方支持的 PostgreSQL/Oracle/SQL Server)不应该是"这个 Spring Boot 应用编译时就得依赖"的东西——这是"应用需要什么"与"这套自托管栈发布时想覆盖什么"的边界问题,两者不必是同一个 Maven scope
下文 §5.1–§5.11 是满足这三条约束的具体设计。
5.1 现状:驱动今天的两种用途混在同一个 compile scope 里
驱动今天是编译期 Maven 依赖,打进后端 fat jar / Docker 镜像,没有运行时动态加载、没有插件市场、没有"上传 driver jar"这类 DBeaver 式机制。但这些驱动其实服务两种完全不同的需求,目前混在 backend/pom.xml 同一个 <dependencies> 块、同一个 compile/runtime scope 里:
| 驱动 | pom.xml 坐标 | 版本 | 实际用途 | 产品面是否已接入 |
|---|---|---|---|---|
| MySQL(含 MariaDB 兼容) | com.mysql:mysql-connector-j | ${mysql.connector.version} | 双重:① 应用自举——erd/martin 系统库连接(application.yml 硬编码 driver-class-name: com.mysql.cj.jdbc.Driver)+ Flyway;② 用户数据源——最常见的客户 MySQL/MariaDB 库 | ✅ ADR-0006 P0 · JdbcUrlGuard 白名单 · 前端类型 Select |
| PostgreSQL | org.postgresql:postgresql | 42.7.4 | 仅用户数据源——应用自身系统库固定用 MySQL,从不连 PG | ✅ 同上 |
| Oracle | com.oracle.database.jdbc:ojdbc8 | ${oracle.connector.version} | 仅用户数据源 | ✅ 同上(jdbc:oracle:thin|oci) |
| SQL Server | com.microsoft.sqlserver:mssql-jdbc | 12.8.1.jre11 | 仅用户数据源 | ✅ 同上 |
com.ibm.db2:jcc | ${db2.connector.version} | 无 | 本轮已删:核实全仓零使用(不在 JdbcUrlGuard 协议白名单、不在 ReverseDialectRegistry/DialectIds、不在前端数据源类型 Select),pom.xml 纯声明死依赖,随本 ADR 顺手清掉(delete-dead-code,trivial,不必等专门 PR);README 中"DB2 在线管理"仍是历史遗留的不准确营销文案,属于另一条独立债务,本轮不改 |
关键技术事实(已用 grep 核实全仓 backend/src/main/java):应用代码零编译期引用这些驱动的 Java 类(无 import org.postgresql.*/oracle.jdbc.*/com.microsoft.sqlserver.*)——驱动加载走纯反射:JdbcKit.getConnection()/AbstractDBCommand 系列都是 Class.forName(driverClassName) + DriverManager.getConnection(url, props),driverClassName 是运行时字符串(来自 data_sources.driverClassName 或 ConnectorCredentialResolver.defaultDriver(type) 兜底)。这意味着把用户数据源驱动移出 backend 模块的 compile/runtime scope 不会破坏编译——mvn compile 不依赖这些 jar 是否在场,只有真正执行 DriverManager.getConnection 那一刻才需要驱动类在运行时 classpath 上。这条事实是 §5.2 分层方案能落地的技术前提。
方言能力矩阵已有前例:ReverseDialect SPI + DialectCapability + ReverseDialectRegistry(ADR-0006),服务"逆向解析元数据";连接凭据解析已有现代路径:ConnectorCredentialResolver.apply() 按 dataSourceId 经 DataSourceAcl 拿到凭据,defaultDriver(type) 兜底驱动类名;分页方言由 MyBatis-Plus PaginationInnerInterceptor 按实际 JDBC 连接元数据自动探测,不需要预览功能自己处理。这三条现代路径预览功能都直接复用,本轮修订不改变这部分结论。
已知真坑(不变):四库没有统一 EXPLAIN <SQL> 语法——预览功能 v0/v1 不做 explain/执行计划。
5.2 决策:系统驱动 + 官方连接器包 + 扩展连接器包,三层模型
| 层 | 是什么 | 在 backend 模块 compile/runtime scope 里? | 在默认发布镜像里? | 谁来添加 | 建模者感知 |
|---|---|---|---|---|---|
| 系统驱动 | 应用自举必需——目前即 MySQL/MariaDB(erd/martin 系统库 + Flyway),同时天然覆盖最常见的客户 MySQL 数据源 | ✅ 是,且永久是——应用没有它连自己的系统库都连不上,谈不上"移出" | ✅ 是 | 项目维护者,仅在系统库引擎本身变化时才会变(目前无此计划) | 无感知,从来如此 |
| 官方连接器包 | 服务用户数据源、产品已承诺 P0 支持的类型——day-1 即 PostgreSQL、Oracle、SQL Server(与逆向解析/同步/ping 已支持的类型完全一致) | ❌ 目标状态:不在(现状仍在,见 §5.1 表;迁移路径见 §5.2a,本轮未执行) | ✅ 是——镜像构建阶段把这三个驱动 jar 合并进最终产物(BOOT-INF/lib 或 loader.path 扫描目录),与 backend 模块自身的编译产物分离但共同打进同一张默认镜像 | 项目维护者,走正常 PR + 发版 | 无感知——这三型在数据源类型 Select、预览菜单里"从来就在",因为默认镜像从来都含它们 |
| 扩展连接器包 | 面向未来新库类型(如 DB2、ClickHouse、Doris、SQLite……),产品未承诺支持 | ❌ 不在 | ❌ 不在默认镜像;需运维显式选择备选镜像 tag 或挂载驱动目录 | 项目维护者开发 + 发版;自托管运维方决定是否启用(见 §5.5) | 无感知——若运维已启用,该类型"从来就在";若未启用,该类型"从来不出现",不是出现了再报错 |
三层的共同纪律:驱动的出现与消失只发生在部署/发布时刻,从不发生在建模者的一次点击里;"官方连接器包"与"系统驱动"合起来才是建模者眼中"这个产品支持的库",两者对建模者是同一件事,只是维护者视角里分属不同 Maven scope。没有第四层——本 ADR 明确拒绝:
- ❌ 管理员在线上传 driver jar 的 Web 功能(哪怕限定管理员角色)——理由见 §5.6
- ❌ 让建模者/项目成员填写驱动类名/驱动版本来"解锁"某个类型——理由见约束 2
- ❌ 面向终端用户的"驱动市场/插件商店"浏览安装 UI——这是 DBeaver/Navicat 的产品形态,不是"预览"该长出来的东西,也违反 vision.md「不在别人生态位内卷」
5.2a 迁移路径(目标状态,本轮仅记录,未执行)
把 PostgreSQL/Oracle/SQL Server 从"官方连接器包"现状(仍在 backend 模块 compile scope)迁移到目标状态(不在该 scope,只在镜像构建阶段合并),需要:
backend/pom.xml:把这三个依赖移出主<dependencies>,改为独立 Maven 模块(如backend/connector-postgresql、connector-oracle、connector-sqlserver)各自声明依赖,或用 Maven profile 隔离(-Pconnectors-official才编译进产物)——本轮已在pom.xml加注释标注 目标状态与技术依据(§5.1 的反射加载事实),未真正拆模块- Docker 多阶段构建:默认镜像的构建 stage 在打包
backend主产物之外,额外执行一次"只解析这三个连接器模块依赖坐标 → 下载 jar → 拷进BOOT-INF/lib"(mvn dependency:copy-dependencies定向到这三个 artifact,或构建三个独立子模块 jar 后合并进最终镜像 layer) - CI:新增一个"官方连接器包完整性"检查——默认镜像构建产物里必须能找到这三个驱动的 class 文件,防止构建脚本改动时不小心漏打(回归测试,不是运行时探测)
- 验证:迁移后
mvn -pl backend compile应该仍然成功(§5.1 已证明零编译期类引用,这一步理论上不会因为移除依赖而失败);docker compose up -d用默认镜像起栈后,创建 PostgreSQL/Oracle/SQL Server 数据源 + 右键「预览数据」应与迁移前行为完全一致——这是回归验证的核心断言,不是"看起来能编译就算完成"
这条迁移不改变任何用户可见行为,纯粹是维护者侧的构建产物治理;因此不必抢在本 ADR 内完成,可以作为独立的后续实现工作项排期(工作量集中在 Docker 构建脚本 + CI,不涉及产品/安全决策,决策已在本 ADR 定案)。
5.3 不阻塞建模者:能力可见性驱动 UI(回应约束 2,核心新增设计)
- 后端维护一份"当前进程实际可用的连接器类型"清单(哪些类型的驱动类此刻能被加载)——系统驱动 + 官方连接器包四型在任何默认发布镜像里恒为可用,属于静态已知集合,不需要探测(即便迁移到 §5.2a 目标状态、PG/Oracle/SQLServer 不再是
backend模块自身的编译依赖,它们仍随默认镜像一起启动,判定逻辑不变);扩展连接器包类型按@ConditionalOnClass/模块是否在 classpath 上判定,同样在启动时算好,不是每次请求都探测 - 前端两处入口都只在类型出现在该清单时才渲染:
- 数据源创建/编辑表单的类型
Select——不可用类型不出现选项,不存在"选了却连不上"的死选项 - 表节点/左树右键「预览数据…」——已在 §1 更新为双重门槛:绑了数据源 且 该数据源类型当前有可用驱动,两条都满足才出现
- 数据源创建/编辑表单的类型
- 对已存在但当前部署把驱动降级/移除的历史数据源(比如运维把镜像从内置扩展连接器包的 tag 换回默认 tag,非常规操作但要覆盖):这条数据源的其它信息(host/port/连接测试历史等)仍可查看编辑,只是「预览数据」菜单项静默消失——不因为一个动作的驱动缺失而让整条数据源变得不可用,影响面收得最小
- 结果:建模者从始至终只会看到"能用的选项",不会看到"点了才发现不能用"的选项,也不会看到"需要你去装点什么才能用"的提示——这正是约束 2 要求的效果
5.4 版本矩阵:产品自测组合,不是 用户选驱动版本
- 每个系统驱动 / 官方连接器包类型只有一个产品钉死的驱动版本(现状即如此:
pom.xml顶部<properties>各一行),不存在"MySQL 5.7 用驱动 A、MySQL 8 用驱动 B"这种用户需要理解的分支——mysql-connector-j8.x 本身向下兼容到 MySQL 5.7 及主流 MariaDB 版本的只读查询路径,版本差异由驱动自身处理,不暴露给产品层 - SQL 语法层面的方言差异(分页/标识符引用)已由 §5.1 提到的 MP 分页插件自动探测 + §5.8 的 prefill 模板表兜底,不需要"用户选版本"这层概念存在
- 产品自己的质量保证:驱动升级时用 docker 矩阵(如
mysql:5.7/mysql:8.4/mariadb:10.11、postgres:13/16、mssql:2019、Oracle 可再分发的免费版本)跑一遍连接 + 预览 SQL 的集成测试,回归写进 CI,不要求用户自证"我的版本行不行" data_sources.driverClassName这个既有可编辑字段(DatabaseConfigForm.tsx目前允许手填/覆盖)与本决策有张力——记为落地时待办:系统驱动 + 官方连接器包四型应改为按type派生只读展示(不接受用户覆盖,杜绝"填错驱动类名"这种本就不该存在的失败模式),仅扩展连接器包类型在运维接入新类型时可能需要显式声明(那是部署配置的一次性动作,不是建模者会碰到的字段);本 ADR 不在这轮改代码,只记录方 向,避免文档与目标状态脱节
5.5 安全结论:拒绝运行时上传,天然没有新增 RCE 面
- 因为 §5.2 明确拒绝"运行时上传 jar",扩展连接器包只能通过两条运维已有的信任边界进入:
- 选择包含该扩展包的备选镜像 tag(需要拉取/构建镜像的权限——运维本来就有)
- 把驱动 jar 挂载进 compose 声明的目录,随进程启动一次性加入 classpath(需要修改
docker-compose.yml/宿主文件系统——运维本来就有)
- 这两条路径都不新增权限层级:能做这件事的人,已经能对整个自托管栈做任意事(改镜像、改 compose、改环境变量)。不存在"给一个只是想预览数据的项目成员一个上传入口"的攻击面,因为这个入口从设计上就不存在
- 因此不需要签名校验、沙箱 ClassLoader、上传审计这类只有"允许上传"才需要的机制——省掉的不是一个"未来再补"的安全债务,是一整类本可以不引入的攻击面
- 官方连接器包(PG/Oracle/SQLServer)走 §5.2a 的镜像构建期合并,同样不涉及运行时上传,安全结论与扩展包一致;两者唯一区别是"是否随默认镜像自动出现",不是"是否有上传入口"(两者都没有)
- 若未来社区确实想做"下载社区维护的连接器包"