修 #5325 时给第三个编译器(ObjectQLStrategy.renderFilterNodeSql,即回显给浏览器的展示
SQL)补布尔常量 kind,顺手核对它的算子覆盖面,发现这一条。不属于 #5325 的范围面,
按 Prime Directive #10 单独记在这里,unassigned。
位置
packages/services/service-analytics/src/strategies/objectql-strategy.ts 的
buildFilterClauseSql / SCALAR_SQL_OPS。
现象(实测)
buildFilterClauseSql 显式处理 set / notSet / in / notIn / contains /
notContains,其余落到
const op = SCALAR_SQL_OPS[operator]; // { equals, notEquals, gt, gte, lt, lte }
if (!op) return null; // ← 静默丢掉整条谓词
startsWith / endsWith 既不在 SCALAR_SQL_OPS 里,也不在上面的分支里,于是返回 null,
被调用方当作「这个节点没有约束」丢掉。同一个 query 走 NativeSQLStrategy 时是有谓词的:
where |
NativeSQLStrategy.generateSql(真正执行的) |
ObjectQLStrategy.generateSql(回显的) |
{ stage: { $startsWith: 'w' } } |
WHERE stage LIKE $1,['w%'] |
没有 WHERE,params 为空 |
{ stage: { $endsWith: 'n' } } |
WHERE stage LIKE $1,['%n'] |
没有 WHERE,params 为空 |
{ stage: { $contains: 'w' } } |
WHERE stage LIKE $1 |
WHERE stage LIKE $1 ✅ |
为什么是 bug
这个字符串的存在理由就是复现执行。objectql-strategy.ts 自己在渲染块顶上写着:
a rendering that contradicts execution is worse than no rendering
以及后来补 $or / 时间窗口时那句「omitting it here would be the lie in the other direction」。
一个作者带着「为什么这张图少了几行」来看回显,拿到的是一条没有该筛选条件的语句 ——
跑一遍返回更多行,于是结论是「筛选器没生效」,而实际执行是生效的。这与 #3601 / #3602 /
#3650 修的是同一类「回显与执行不一致」。
不涉及越权或错行:该字符串从不执行(execute() 的 echo 会丢弃 params),
损害限于可调试性。
建议
把 startsWith / endsWith 补进 buildFilterClauseSql(与 contains 同一分支,
pattern 分别是 `${v}%` / `%${v}`,与 NativeSQLStrategy.buildFilterClause 的
likePattern 表一致)。
更根本的一条:return null 这个「渲染不了就静默丢」的出口本身是缺陷形状 —— 与
filter-normalizer.ts 对未知算子直接 THROW 的姿态相反。normalizer 的算子词汇表是
封闭的(不支持的算子在那里就抛了),所以回显端遇到映射不到的算子只可能意味着两张表
漂移了,抛错比静默丢更符合这个文件其它地方的立场(参考 convertFilter 的 default: 分支,
它正是为同一个理由从「当成等值」改成抛错的,#4128)。
关联:#5325(同函数、同一轮里发现)、#4128(同类「算子静默丢失」)、#3602 / #3650
(回显必须忠实于执行)。
修 #5325 时给第三个编译器(
ObjectQLStrategy.renderFilterNodeSql,即回显给浏览器的展示SQL)补布尔常量 kind,顺手核对它的算子覆盖面,发现这一条。不属于 #5325 的范围面,
按 Prime Directive #10 单独记在这里,unassigned。
位置
packages/services/service-analytics/src/strategies/objectql-strategy.ts的buildFilterClauseSql/SCALAR_SQL_OPS。现象(实测)
buildFilterClauseSql显式处理set/notSet/in/notIn/contains/notContains,其余落到startsWith/endsWith既不在SCALAR_SQL_OPS里,也不在上面的分支里,于是返回null,被调用方当作「这个节点没有约束」丢掉。同一个 query 走
NativeSQLStrategy时是有谓词的:whereNativeSQLStrategy.generateSql(真正执行的)ObjectQLStrategy.generateSql(回显的){ stage: { $startsWith: 'w' } }WHERE stage LIKE $1,['w%']params为空{ stage: { $endsWith: 'n' } }WHERE stage LIKE $1,['%n']params为空{ stage: { $contains: 'w' } }WHERE stage LIKE $1WHERE stage LIKE $1✅为什么是 bug
这个字符串的存在理由就是复现执行。
objectql-strategy.ts自己在渲染块顶上写着:以及后来补
$or/ 时间窗口时那句「omitting it here would be the lie in the other direction」。一个作者带着「为什么这张图少了几行」来看回显,拿到的是一条没有该筛选条件的语句 ——
跑一遍返回更多行,于是结论是「筛选器没生效」,而实际执行是生效的。这与 #3601 / #3602 /
#3650 修的是同一类「回显与执行不一致」。
不涉及越权或错行:该字符串从不执行(
execute()的 echo 会丢弃params),损害限于可调试性。
建议
把
startsWith/endsWith补进buildFilterClauseSql(与contains同一分支,pattern 分别是
`${v}%`/`%${v}`,与NativeSQLStrategy.buildFilterClause的likePattern表一致)。更根本的一条:
return null这个「渲染不了就静默丢」的出口本身是缺陷形状 —— 与filter-normalizer.ts对未知算子直接 THROW 的姿态相反。normalizer 的算子词汇表是封闭的(不支持的算子在那里就抛了),所以回显端遇到映射不到的算子只可能意味着两张表
漂移了,抛错比静默丢更符合这个文件其它地方的立场(参考
convertFilter的default:分支,它正是为同一个理由从「当成等值」改成抛错的,#4128)。
关联:#5325(同函数、同一轮里发现)、#4128(同类「算子静默丢失」)、#3602 / #3650
(回显必须忠实于执行)。