Commit 39f5a4fb authored by Data Governance Dev's avatar Data Governance Dev

docs(worklog): Oracle 自定义规则踩坑(ORA-01036/00933)+ 多 tab UX 改造

新增 2026-08-13 条目,覆盖 6 块改动:

1) ORA-01036: illegal variable name/number
   - sql_builder 历史 Oracle 路径输出 qmark ?,但 python-oracledb 不支持
   - Bind.next_idx 加 numbered :1/:2/... 占位符

2) ORA-00933: SQL command not properly ended
   - step9 无条件 LIMIT 200,Oracle 11g 不支持
   - 截断子句分支:Oracle 走 WHERE ROWNUM <= N

3) 中间 bug:Oracle 分支漏 WHERE 关键字
   - 静态验证三方言 SQL 时暴露,立即补 WHERE
   - 教训:跨方言分支必须同一组测试数据验证

4) evalPathExpr 不支持 dict 索引
   - 设计 data.matches_rule_<idx> 扁平字段避开 this.x[key] 语法
   - 教训:协议路径表达式先看支持语法再设计数据结构

5) flatResultRows 折叠表聚合踩坑
   - 顶层 summary 是 section 共享的,按 tab key 前缀识别 custom_value_check_<idx>

6) UX:默认名「自定义规则N」
   - nextDefaultRuleName() 扫已用名,找最小可用 N
   - 后端兜底回落 <table>.<column>(不再用 (未命名))
parent 90e89ed5
...@@ -2,6 +2,92 @@ ...@@ -2,6 +2,92 @@
> 任务做完一次记一次。最近的在最上面。 > 任务做完一次记一次。最近的在最上面。
## 2026-08-13 · Oracle 自定义规则踩坑(ORA-01036 + ORA-00933)+ 多 tab UX 改造
> 接续上面「Oracle 端到端打通」之后的两件事:
> 1) 把 Oracle 数据真正跑通自定义规则 —— 暴露 2 个方言坑
> 2) 用户反馈:每条自定义规则单独一个 tab、默认名「自定义规则N」
### 1. ORA-01036: illegal variable name/number —— sql_builder 给 Oracle 用了 qmark ?
- **现象**:在 Oracle 上跑「自定义规则(字段值包含关键字)」,日志报 `ORA-01036: illegal variable name/number`。
- **根因**:[web/core/sql_builder.py](web/core/sql_builder.py) `_placeholder()` 历史实现给所有非 MySQL 方言统一输出 `?`。但 `python-oracledb`(thin 和 thick 都一样)**不支持 qmark 占位符**,必须显式给 numbered(`:1, :2, ...`)或 named(`:name`)。早期注释里写的「oracledb thin 自动转 :1」是错的,实际驱动层根本不转。
- **修复**:
- `Bind` dataclass 加 `next_idx: int = 1`
- Oracle 路径按递增编号输出 `:1, :2, ...`,每发一次 `+1`
- MySQL / 达梦 不需要编号(`%s` / `?` 按位置对应)
- 注释里写明错误历史 + 不要再走 qmark
- **达梦为什么用 `?` 没事**:`dmPython` 走 DB-API 2.0 标准 qmark,跟 `sqlite3` 一样原生支持。
### 2. ORA-00933: SQL command not properly ended —— Oracle 11g 不支持 LIMIT
- **现象**:上一条占位符修好后跑自定义规则,继续报 `ORA-00933: SQL command not properly ended`。
- **根因**:[web/core/step_impl/step9_custom_rules.py](web/core/step_impl/step9_custom_rules.py) 无条件拼 `LIMIT 200`。Oracle 11g **不支持 LIMIT / OFFSET**(12c 引入 `FETCH FIRST n ROWS ONLY`),唯一等价语法是 `WHERE ROWNUM <= N`。
- **修复**:截断子句按 `db_type` 分支:
```python
if cfg.db_type == "oracle":
sql = f"... WHERE {where_clause} AND ROWNUM <= {_LIMIT}"
else:
sql = f"... WHERE {where_clause} LIMIT {_LIMIT}"
```
`WHERE ROWNUM <= N` 在 12c+ 仍支持(不破坏未来升 Oracle 12c 的兼容)。
### 3. 中间 bug:Oracle 分支漏写 `WHERE` 关键字(静态验证脚本暴露)
- **现象**:第一次写 Oracle 分支时拼成:
```
SELECT "TJYHMC" FROM "B_TJYH" "TJYHMC" LIKE :1 ESCAPE '!' AND ROWNUM <= 200
```
注意 `FROM "B_TJYH"` 后直接是 `"TJYHMC"`(重复引用表别名?实际是漏 `WHERE`)—— 这条 SQL 在 Oracle 上会再抛一个 ORA-00933。
- **根因**:MySQL/达梦分支显式拼 `WHERE {where_clause}`,Oracle 分支手抖漏了关键字。
- **破局**:写了一个静态验证脚本,对三方言各跑一遍 `compile_rule`,对比输出。Python 端三方言各打 3 条 SQL(简单 / 复杂 / 含 gt+lt),肉眼一行行比对立刻发现 Oracle 输出异常。
- **修复**:Oracle 分支补 `WHERE {where_clause} AND ROWNUM <= {_LIMIT}`。
- **教训**:跨方言分支必须用同一组测试数据验证,不能靠「改完跑跑看」—— 三个方言只有 Oracle 漏 `WHERE`,其他两个都对,自测根本发现不了。
### 4. 设计踩坑:evalPathExpr 不支持 dict 索引语法 → 扁平字段命名
- **背景**:把每条自定义规则拆成独立 tab 后,需要给每个 tab 的 `TableSpec.source` 一个能唯一定位数据的路径表达式。
- **第一版想法**:`source = "this.by_rule['rule_1']"`,由后端给 `data['by_rule']` 一个 dict,key 用 `rule_<idx>`。
- **失败**:[web/static/index.html](web/static/index.html) `evalPathExpr()` 只识别 `this.x.y.z`(属性链),不识别 `this.x[key]`(dict 索引)。如果后端真按这格式返,前端 `resolveRows` 会拿空数组,渲染空表 —— 用户看到「啥也没有」以为是 bug。
- **修复**:把每个规则的数据直接挂到 data 顶层扁平字段:
```python
data[f"matches_rule_{idx}"] = rule_matches # list[dict]
data[f"error_rule_{idx}"] = first_error or ""
data[f"matched_rule_{idx}"] = len(rule_matches)
```
`source = "this.matches_rule_1"`,`summary.vars = {"error": "this.error_rule_1"}` —— 全是简单路径。
- **教训**:协议驱动渲染的所有路径表达式都要走 `evalPathExpr`,**先看一眼支持的语法再设计数据结构**,不要想当然用 dict 索引。
### 5. flatResultRows 折叠表聚合踩坑 —— 顶层 summary 是 section 共享的
- **现象**:每条规则拆成独立 tab 后,折叠列表里每行都显示「违规 = total_matched_rows / 检查 = fields_checked」(顶层聚合),不是「违规 = 本规则命中 / 检查 = 1」。
- **根因**:[web/static/index.html](web/static/index.html) `flatResultRows` 直接读 `t.data.summary.violations`,所有 tab 共享顶层 section summary。
- **修复**:加一个分支识别 `custom_value_check_<idx>` 前缀的 tab,从 `t.data.matched_rule_<idx>` 取本规则命中数、检查数固定 1;其它 step 沿用原逻辑。
- **设计权衡**:折叠表只是「快速跳转到 tab」,tab 内的 alert / KPI 才承载细节,所以折叠表里「违规 N / 检查 1」的数字精度可以放宽(每条规则就 1 个字段)。
### 6. UX 改进:新建自定义规则默认名「自定义规则N」
- **用户反馈**:输入框留空看着别扭,希望默认填「自定义规则1」「自定义规则2」递增。
- **实现**:[web/static/index.html](web/static/index.html) 新增 `nextDefaultRuleName()`:
- 扫描 `customRules.value` 里所有非空 `rule_name`,组成 `used` 集合
- 从 1 开始找最小不在 used 里的 N
- 用户手动改名后 N 仍递增不会撞名
- **后端兜底**:如果前端漏了 / 旧 API 调用送来空 `rule_name`,后端 step9 仍然按 `<table>.<column>` 回落,**不**回落到 `(未命名)` —— 因为 `(未命名)` 在 UI 上更难看。
### 验证
提交:`f4903d8`(ORA-01036 + ORA-00933)、`90e89ed`(多 tab + 默认名)。
静态验证脚本输出(三方言 SQL 对比,已贴在前面的对话里):
```
MySQL : SELECT `COL` FROM `TBL` WHERE `COL` LIKE %s ESCAPE '!' LIMIT 200
达梦 : SELECT "COL" FROM "TBL" WHERE "COL" LIKE ? ESCAPE '!' LIMIT 200
Oracle : SELECT "COL" FROM "TBL" WHERE "COL" LIKE :1 ESCAPE '!' AND ROWNUM <= 200
```
mock `_build_result` 输入 2 条规则(1 ok + 1 ORA-00933 失败)输出:
```
tabs: [
{key:custom_value_check_1, title:'自定义规则1(B_TJYH.TJYHMC)', type:info, vars:{matched:'this.matched_rule_1'}},
{key:custom_value_check_2, title:'自定义规则2(B_YHZ.ZHYE)', type:error, vars:{error:'this.error_rule_2'}}
]
error_rule_1: ''
error_rule_2: 'ORA-00933: SQL command not properly ended'
```
## 2026-08-13 · Oracle 端到端打通:health SQL 分方言 + UI 隐藏路径 + 11g ALL_TABLES 兼容 + schema 名修正 ## 2026-08-13 · Oracle 端到端打通:health SQL 分方言 + UI 隐藏路径 + 11g ALL_TABLES 兼容 + schema 名修正
> 接续上面 5 个条目(放开 Oracle / connect_timeout / DPY-3010 / 路径外置 / DPI-1047 PE 解析)。 > 接续上面 5 个条目(放开 Oracle / connect_timeout / DPY-3010 / 路径外置 / DPI-1047 PE 解析)。
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment