1. 页面概览
1.1 是什么
SQL 工作台是 LightFoundry 提供的一块「对象名直查的受限只读 SQL」查询界面(V5 Stage 1 B1-4 交付)。用户可以用业务对象名(api_name / object_ 前缀 / 物理表名)直接编写 SELECT 语句,由后端将其翻译为真实的物理 SQL 再执行,从而:
- 不需要知道物理库的表名与列名:
FROM orders会被后端解析成真实的物理表(或对应数据源表),amount会被映射为物理列。 - 天然获得安全闸门:执行前自动注入 RLS(行级安全)与 CLS(列级安全),非授权的行与列不会出现在结果中;只读强制、标识符白名单、LIMIT 钳制全部由后端保证,前端无法绕过。
- 能看到「翻译」过程:通过「翻译预览」Tab,用户可以查看对象 SQL 被翻译成的物理参数化 SQL、绑定参数、生效的 LIMIT 与安全注入状态,便于审计口径。
1.2 核心价值
| 维度 | 说明 |
|---|---|
| 低门槛取数 | 面向会写 SQL 的用户,用对象名取数,无需接触物理表 |
| 安全兜底 | 后端强制只读、白名单、RLS/CLS 注入、LIMIT 钳制,前端不可绕过 |
| 可解释 | 翻译预览清晰回显「对象→物理表、属性→物理列、字面量→?」的映射 |
| 编辑态可见 | 若数据存在「编辑态叠加」(草稿修改未合入),查询结果会绿色高亮叠加值并提示 |
1.3 一句话总结
2. 访问入口
2.1 路由与菜单
- 路由:
/foundry/sql - 路由名称:
FoundrySqlWorkbench - 菜单位置:Foundry 左侧边栏「SQL 工作台」(FoundryLayout 菜单第 9 项)
- 源码文件:
action/web/src/views/SqlWorkbenchPage.vue(352 行)
2.2 认证与权限
- 需要登录:是(路由
meta.requiresAuth = true) - 登录方式:AIP 统一登录,
localStorage.aip_token作为 Bearer Token - 页面本身无角色限制,但查询结果受后端 RLS/CLS 约束:普通用户只能看到自己被授权的行与列;RLS/CLS 配置在「数据打标 / 安全治理」等页面完成。
- 404 排错:若访问
/foundry/sql出现 404,先确认 Foundry 后端(端口 18081)已启动且前端路由已注册。
2.3 端口与 API 前缀
- 后端端口:18081(Foundry)
- 前端 API 前缀:
/api/v1(queryApi.js,独立 axios 实例,不是client.js) - 请求超时:默认 60 秒;执行查询(execute)单独放宽到 120 秒,适合大表查询。
3. 界面布局
页面纵向分为 6 个板块(从上到下):
┌──────────────────────────────────────────────────────────────┐ │ ① 页头:标题「SQL 工作台」+ 一句话说明 │ │ ② 操作结果提示条(alert,成功后绿色 / 失败后红色,可关闭) │ │ ③ 查询编辑卡片 │ │ ├─ 对象下拉(点击注入模板) ── LIMIT 输入框(可空) │ │ ├─ Ontology SQL 多行编辑框(textarea) │ │ └─ [▶ 运行查询] [翻译预览] [导出 CSV] 三个按钮 │ │ ④ Tab 切换(有结果或翻译后才出现):[查询结果] [翻译预览] │ │ ⑤ 查询结果卡片(结果表格 + 元信息注记 + 编辑态叠加高亮) │ │ ⑥ 翻译预览卡片(物理 SQL + 翻译元信息表 + 绑定参数) │ └──────────────────────────────────────────────────────────────┘
各板块职责:
- ① 页头:说明本页能力边界(受限只读 SQL)。
- ② 提示条:页面内唯一的消息反馈区,展示查询/翻译的成功或失败信息。
- ③ 查询编辑:所有查询入口。对象下拉用于快速注入模板 SQL,LIMIT 可覆盖默认值。
- ④ Tab 切换:仅当存在查询结果或翻译结果时才出现,避免空 Tab 干扰。
- ⑤ 查询结果:结果表格、行数/耗时、安全注记、编辑态叠加列高亮。
- ⑥ 翻译预览:物理参数化 SQL 与翻译元信息(对象、物理表、列、LIMIT、RLS/CLS、叠加注记),是排查「查询结果不对」的第一现场。
4. 交互元素详解
4.1 对象下拉(点击注入模板)
| 属性 | 值 |
|---|---|
| 控件 | <select> 下拉框 |
| 数据来源 | GET /api/v1/ontology/objects?status=merged(页面加载时 onMounted(fetchObjects) 拉取) |
| 加载态 | 下拉置灰并显示「加载对象中...」,由 loadingObjects 控制 |
| 选项格式 | 显示名(N 属性),value 为对象 api_name |
| 空态 | 无对象时显示占位「选择对象注入模板」 |
交互效果:选中任意对象后,编辑器被覆盖注入为模板 SQL:
SELECT *
FROM {api_name}
LIMIT 100
注入后下拉框自身 value 立即重置为空(e.target.value = ''),避免重复选中同一项不触发 change 事件。
console.warn)。4.2 LIMIT 输入框(可空)
| 属性 | 值 |
|---|---|
| 类型 | type="number",min=1,绑定 v-model.number |
| 默认 | 空(limitInput = null) |
| 前端处理 | 空值时传 0 给后端(limitInput.value || 0) |
| 后端处理 | 传 0 或未写 LIMIT 时,后端补默认 500;SQL 自带 LIMIT 超 5000 时钳制到 5000 |
4.3 Ontology SQL 编辑框
| 属性 | 值 |
|---|---|
| 控件 | <textarea rows=6>,等宽字体,spellcheck=false |
| 写法 | FROM 后写对象名(api_name / object_ 前缀 / 物理表名);SELECT 写属性名(映射为物理列) |
| 字符串 | 自动参数化为 ?(绑定参数,防注入) |
| 示例 | SELECT order_id, amount FROM order WHERE amount > 100 ORDER BY amount DESC LIMIT 50 |
| 限制 | 仅单条只读 SELECT;不支持 linkName.prop 关联属性写法(需显式 JOIN 目标对象后用 别名.属性) |
编辑器占位提示里给出的经典示例:SELECT name, amount FROM orders LIMIT 10。
4.4 三个操作按钮
| 按钮 | 触发函数 | 触发条件 | 效果 |
|---|---|---|---|
| ▶ 运行查询 | handleRun | 编辑器非空且非 busy | 调 executeOntologySQL,成功切到「查询结果」Tab 并弹绿色提示(行数与生效 LIMIT),失败弹红色提示 |
| 翻译预览 | handleTranslate | 编辑器非空且非 busy | 调 translateOntologySQL,切到「翻译预览」Tab 展示物理 SQL 与元信息 |
| 导出 CSV | exportCSV | result.rows.length > 0 | 纯前端把当前结果表格导出为 CSV 文件(带 BOM,Excel 可识别 UTF-8),无后端调用 |
- 三个按钮在
busy状态下均禁用;运行/翻译按钮在 SQL 为空时禁用。 - 按钮文案在运行中变为「执行中...」。
4.5 Tab 切换
- 出现条件:
translated非空 或result.columns.length > 0(即至少有一种结果)。 - 两个 Tab:查询结果(默认)与 翻译预览。
- 切换仅改前端
activeTab,不发请求。
4.6 查询结果卡片
- 标题:
结果(N 行,X ms);若存在编辑态叠加字段,右侧追加绿色 tag:编辑态叠加: field1, field2。 - 元信息注记(
metaNote,蓝色条):按优先级拼接——RLS 已注入 / CLS 已注入 / 安全注入未生效(security_note)/ 编辑态叠加注记(overlay_note)。 - 表格:首行为列名,单元格
null显示为NULL;被编辑态叠加覆盖的列以绿色高亮(edited-cell类)区分。 - 空态:无结果时显示「尚无结果:编写 SQL 后点击"运行查询"」。
4.7 翻译预览卡片
| 区块 | 内容 |
|---|---|
| 物理 SQL | <pre> 深色块展示 physical_sql(参数化,字面量以 ? 占位) |
| 翻译元信息表 | 涉及对象 / 物理表 / 查询属性列 / 生效 LIMIT(含 limit_mode 说明)/ RLS / CLS / 编辑态叠加注记 |
| 绑定参数 | args JSON 数组(按 ? 顺序),有参数时才显示 |
limit_mode 取值(前端 limitModeLabel 映射):
| 值 | 含义 |
|---|---|
defaulted | 未写 LIMIT,后端补默认(500) |
clamped | 超上限,已钳制到 5000 |
user | 用户明确指定 |
5. 后端关联
5.1 依赖的 API 客户端
action/web/src/api/queryApi.js(独立 axios 实例):
baseURL: '/api/v1',Content-Type: application/json- 请求拦截器:
localStorage.aip_token→Authorization: Bearer <token> - 响应拦截器:401 时清空
aip_token/aip_username并跳转/login - 导出三个函数:
listObjects/translateOntologySQL/executeOntologySQL
5.2 调用的后端端点
| 方法 | 路径 | 用途 | 请求体 | 超时 |
|---|---|---|---|---|
| GET | /ontology/objects | 对象列表(页面加载拉对象下拉) | ?status=merged | 60s |
| POST | /ontology/sql/translate | 翻译预览(不执行) | {"sql": "..."} | 60s |
| POST | /ontology/sql/execute | 翻译并执行受限只读 SQL | {"sql": "...", "limit": 0} | 120s |
5.3 响应结构
translate 成功({code:0, data:{...}}):
{
"code": 0,
"data": {
"physical_sql": "SELECT \"order_id\", \"amount\" FROM \"orders\" WHERE \"amount\" > ? ORDER BY \"amount\" DESC LIMIT 50",
"args": ["100"],
"meta": {
"objects": ["order"],
"base_tables": ["orders"],
"columns": ["order_id", "amount"],
"limit_applied": 50,
"limit_mode": "user",
"rls_injected": true,
"cls_injected": false,
"security_note": "",
"overlay_note": ""
}
}
}
execute 成功({code:0, data:{...}}):
{
"code": 0,
"data": {
"columns": ["order_id", "amount"],
"rows": [["O-1001", "259.00"]],
"edited_fields": ["amount"],
"row_count": 1,
"elapsed_ms": 12,
"meta": { "limit_applied": 50, "limit_mode": "user", "rls_injected": true, "cls_injected": false }
}
}
失败(统一错误结构):{ "code": "<错误码>", "error": "<错误详情>" },HTTP 状态码由后端 ierr 错误映射(如 400/403/500)。
5.4 关联的后端模块与服务
| 模块 | 位置 | 职责 |
|---|---|---|
| Ontology SQL 服务 | action/products/foundry/query/ | OntologySQLService.Translate/Execute,SQL 解析、白名单映射、安全注入、LIMIT 钳制 |
| REST 入口 | action/products/foundry/query/rest.go | 两个 POST 端点,RegisterRoutes 挂载到鉴权路由组 |
| 语义查询层 | action/products/foundry/semantic/ | 属性级安全(RLS/CLS 注入执行)、编辑态叠加(edit_overlay.go,ontology_edits 表) |
| 宏解析 | action/products/foundry/query/translate.go | V5 Stage 6 新增 MacroResolver 钩子,Execute 入口翻译前展开宏(SQL 工作台与 Notebook sql 单元格两处生效) |
5.5 后端强制安全口径
| 规则 | 说明 |
|---|---|
| 只读 SELECT | DML/DDL 黑名单,非 SELECT 一律拒绝 |
| 单语句 | 不允许 ; 分隔多语句 |
| 标识符白名单 | 对象名/属性名须经本体映射,物理列名白名单 |
| 字符串参数化 | 字面量转绑定参数 ?,杜绝拼接注入 |
| RLS 注入 | 基于当前 user_id 在 LIMIT 之前 注入行级守卫(防「先分页后过滤」越权) |
| CLS 注入 | 基于密级/列授权隐藏列(NULL AS col),属性级安全合并 Marking 隐藏 |
| LIMIT 钳制 | 未写补 500,超上限钳制到 5000 |
6. 核心流程详解
6.1 一次完整查询的主流程
页面加载
└─ onMounted(fetchObjects)
└─ GET /ontology/objects?status=merged → 填充对象下拉(失败仅 console.warn,不阻断)
用户操作
1. 可选:选择对象下拉 → 注入模板 SQL `SELECT * FROM {name} LIMIT 100`
2. 编辑 SQL(可改 LIMIT 输入框)
3. 点击「▶ 运行查询」
├─ busy=true,按钮禁用,文案「执行中...」
├─ POST /ontology/sql/execute {sql, limit}
│ ├─ 后端:校验只读/单语句/白名单 → 翻译 → RLS/CLS 注入 → LIMIT 钳制 → 执行 → 叠加编辑态
│ └─ 返回 columns/rows/edited_fields/elapsed_ms/meta
├─ 前端:计算 editedColumns 下标集合,拼 metaNote,切「查询结果」Tab
└─ busy=false,alert 绿色「查询完成:N 行(LIMIT X)」
6.2 翻译预览流程
点击「翻译预览」
├─ POST /ontology/sql/translate {sql}(不执行,纯翻译回显)
└─ 切「翻译预览」Tab:物理 SQL / 元信息表 / 绑定参数
翻译与执行共用同一套翻译逻辑,因此「先翻译、后执行」可预判结果是否合规(如 RLS 是否注入、LIMIT 是否被钳制)。
6.3 编辑态叠加(Edited Fields)机制
- 后端在返回结果前,会探测
ontology_edits表中当前对象类型是否存在未合入的草稿修改(编辑态叠加层)。 - 叠加逻辑在分页/排序之后对返回行生效(
LIMIT/ORDER后叠加),保证编辑值不会因分页而错位。 - 前端将
edited_fields映射为列下标(editedColumnsSet),对命中列做绿色高亮;标题区显示「编辑态叠加: 字段名」。 - 注意:叠加的是展示层结果,
row_count仍是物理行数。
6.4 CSV 导出流程(纯前端)
点击「导出 CSV」(需已有结果)
├─ 取当前 result.columns + result.rows(仅当前结果页,不做后端分页)
├─ 每个单元格转字符串;含逗号/引号/换行时用双引号包裹并转义("")
├─ 首行 BOM \uFEFF + 表头 + 数据行,\n 连接
└─ Blob(text/csv;charset=utf-8) → URL.createObjectURL → <a download> 触发下载
文件名:query_result_YYYY-MM-DD-HH-mm-ss.csv
7. 权限与安全
7.1 认证
- 使用
aip_token(JWT)Bearer 认证;401 会自动清理登录态并跳转/login。 - 后端
authMiddleware从 token 解出user_id注入 gin 上下文,供 RLS/CLS 使用。
7.2 数据级安全(查询阶段生效)
- RLS 行级:查询自动追加基于
user_id的守卫条件,且位于 LIMIT 之前,防止越权行进入分页结果。 - CLS 列级:未授权的列以
NULL AS col隐藏;若结合 V5 Stage 5 的 Markings 密级,属性级安全块会合并 marking 隐藏(任一来源隐藏即隐藏)。 - 用户在翻译预览的元信息表中可确认「RLS/CLS 是否已注入」;若显示「安全注入未生效」,通常是未配置 RLS/CLS 策略(属正常配置态,不是错误)。
7.3 写操作防护
- 页面没有任何写入口;后端对
SELECT之外的所有语句(INSERT/UPDATE/DELETE/DDL 等)直接拒绝。 - 「修改数据」必须走 Foundry 的 Action 唯一写路径(Action 测试页 / 工作流),本页只读。
8. 常见问题与排错
8.1 点击「运行查询」报错,无任何结果
现象:alert 红色「查询失败:...」,结果区空。
排查步骤:
- 先点「翻译预览」,若翻译也失败,说明 SQL 语法或对象名有问题——查看报错
error字段(如invalid SQL、object not found: xxx)。 - 若翻译成功但执行失败:确认对象状态是否
merged(草稿对象不可查询),或物理表/列是否存在。 - 确认后端 18081 已启动:浏览器开发者工具 Network 面板看请求是否 502/404。
- 查看 Foundry 后端日志定位服务层错误(
query包与semantic包)。
8.2 结果被截断,行数不对
现象:row_count 正好是 500 或 5000。
原因:LIMIT 钳制——未写 LIMIT 补默认 500,超上限钳到 5000。这是安全设计,不是 bug。
处理:在 SQL 中显式写 LIMIT N(≤5000),或观察翻译预览「生效 LIMIT(limit_mode)」字段确认实际生效值。
8.3 某些列显示为 NULL,或整列消失
现象:翻译元信息显示 CLS 已注入,列值全为 NULL。
原因:当前用户无该列权限(CLS 列级安全),或该列被 Markings 密级隐藏。
处理:
- 确认为「安全行为」而非数据问题:换管理员账号验证列数据存在。
- 需要查看列:请管理员在「数据打标 / 安全治理」中调整 CLS/密级授权。
8.4 查询结果里有绿色高亮列,数值与库里不一致
现象:某列绿色高亮,标题区有「编辑态叠加」tag。
原因:该对象存在未合入的草稿编辑(编辑态叠加层),查询结果展示的是叠加后的值。
处理:这是设计行为。若希望看到物理库原始值,需等待编辑态合入/回滚(见 Foundry 本体工作台版本状态机)。
8.5 导出 CSV 用 Excel 打开中文乱码
原因:CSV 已带 UTF-8 BOM,正常情况下 Excel 可识别;若仍乱码,多为编辑器/其它程序未按 UTF-8 读取。
处理:用 Excel「数据→自文本/CSV」导入并选择 UTF-8;或先用记事本另存为带 BOM 的 UTF-8。
8.6 想用 link 关联属性(linkName.prop)取数
现象:写 linkName.prop 报不支持。
原因:本版不支持 link 属性隐式解析。
处理:显式 JOIN 目标对象后用 别名.属性,例如:
SELECT o.amount, c.name
FROM orders o
JOIN customers c ON c.id = o.customer_id
LIMIT 100
8.7 查询执行超过 60 秒被中断
现象:请求在 60 秒左右失败。
说明:execute 端点超时放宽到 120 秒;若仍超时,多为数据量大或物理表缺索引。
处理:缩小结果集(LIMIT、加 WHERE 过滤条件);检查物理表索引;必要时在数据管道层做预聚合。
9. 已知缺陷与边界
| 项 | 说明 |
|---|---|
| link 关联属性 | linkName.prop 写法本版不支持,需显式 JOIN(见 8.6) |
| 编辑态叠加范围 | 叠加仅作用于返回行(分页/排序后),超大结果集下的叠加展示以后端为准 |
| 宏解析依赖 Stage 6 | V5 Stage 6 起 Execute 入口会经 MacroResolver 展开宏(宏表优先、未命中原样、迭代深度 ≤5);若配置了宏但未生效,检查 fcr_repos/fcr_files 宏表内容 |
注:对象下拉失败静默已于 2026-09-06 修复(失败改为页面级 alert-error 提示,并说明仍可手写 SQL、「对象注入模板」暂不可用)。
注:CSV 仅导出当前已加载行且无提示的问题已于 2026-09-06 修复(导出前明确提示「仅当前已加载 N 行」;如需全量请调大 LIMIT 或按范围拆分查询)。
10.2 后端相关
- REST:
action/products/foundry/query/rest.go - 服务层:
action/products/foundry/query/(translate.go、execute.go等) - 语义安全:
action/products/foundry/semantic/(RLS/CLS 注入、edit_overlay.go) - 契约测试:
action/products/foundry/server/contract_test.go、query/translate_test.go
10.3 项目文档
- V5 总纲:
action/wiki/upgrade-v5/UPGRADE-PLAN.md(B1-4 Ontology SQL 工作台) - Stage 1 交付:
action/wiki/upgrade-v5/dev-story/stage-1.md - 变更记录:
action/wiki/changelog/2026-08-29-3-v5-stage2.md(B1-4 相关章节) - 产品矩阵:
action/wiki/OVERVIEW.md - 本系列索引:
../index.md(总索引)与index.md(Foundry 索引)
10.4 相邻页面
- 本体工作台(
foundry-home.html):对象/属性/链接定义,SQL 工作台的数据来源 - 数据源(
data-sources.html):物理表来源与元数据导入 - 数据打标(
markings.html):CLS 密级隐藏列与本页 CLS 注入的联动 - 代码仓库(
coderepo.html):SQL 宏仓库,Execute 入口的宏展开来源