修 #5146(driver-sql 的 $not 改 NULL-safe,PR #5296)时扫到,不在该 PR 范围内
(packages/services/service-analytics/** 是 #5115 的在飞文件面)。这里只记录。
位置
packages/services/service-analytics/src/read-scope-sql.ts 的 compileNode:
} else if (key === '$not') {
const inner = compileNode(value, qAlias, params);
if (inner) clauses.push(`NOT (${inner})`);
}
它是 RLS read scope 的独立 SQL 降解器(与 driver-sql 的 applyFilterCondition
不是同一段代码),被 native-sql-strategy.applyReadScope 与 objectql-strategy 用来
给分析查询加可见性约束。
分叉一:$not 非 NULL-safe
发的是裸 NOT (…)。SQL 三值逻辑下,被比较列为 NULL 的行落进 UNKNOWN 而被排除;
driver-memory / formula 返回这些行。#5146 已由维护者拍板取 NULL-safe,
PR #5296 把 driver-sql 对齐到了 JS 家族(NOT (col IS NOT NULL AND …),守卫按算子
极性下推到叶子)。该 PR 合入后,本文件是仓内最后一个仍按三值逻辑回答 $not 的
SQL 家族实现 —— 也就是说:同一条 read scope,普通查询与分析查询会给出不同的可见
集合。
修的方向已经有现成参照:PR #5296 的 nullSafeNegationOperand(纯函数,逐算子极性
表),照抄到这个编译器上即可,不需要重新裁定语义。
分叉二:{ $not: {} } 编译成空字符串 → 整表
compileNode({}) 返回 '',于是 if (inner) 为假,整条 $not 不产出任何子句;
applyReadScope 又有 if (!sql) return;,所以一条 { $not: {} } 的 read scope
完全不加 WHERE —— 本该零行(NOT TRUE ≡ FALSE),实际整表。
这正是 #5134 / PR #5243 在 driver-sql 上修掉的那条(那边现在编译成 1 = 0),
方向是静默放松,而这里的宿主是 RLS。同一段循环里 $and/$or 的空数组是
fail-closed 抛错的(第 54-56 行),所以缺的只有 $not 这一格。
顺带(方向相反、危害小得多):$or: [{}, {a: 1}] 里的 {} 编译成 '' 后被
.filter((s) => s.length > 0) 丢掉,结果是 (a = 1),而布尔代数上 {} 是 TRUE
析取项、整个 $or 为 TRUE —— 静默收紧。
建议
两处都在同一个函数里,建议一次 PR 修完(与 #5115 的在飞改动错开或合并排期)。
packages/services/service-analytics/src/__tests__/read-scope-sql-conformance.test.ts
已经在跑 FILTER_LOGIC_CASES,但那张表刻意不含 null 处理,所以两条都不会被现有
门禁发现 —— 跨驱动的 null case 归 spec 车道(#5239 / #5146 的 spec 半边)。
关联
#5146($not NULL 语义拍板)、PR #5296(driver-sql 侧实现,可直接参照)、
#5134 / PR #5243(布尔单位元)、#5115(该包在飞)。
修 #5146(driver-sql 的
$not改 NULL-safe,PR #5296)时扫到,不在该 PR 范围内(
packages/services/service-analytics/**是 #5115 的在飞文件面)。这里只记录。位置
packages/services/service-analytics/src/read-scope-sql.ts的compileNode:它是 RLS read scope 的独立 SQL 降解器(与 driver-sql 的
applyFilterCondition不是同一段代码),被
native-sql-strategy.applyReadScope与objectql-strategy用来给分析查询加可见性约束。
分叉一:
$not非 NULL-safe发的是裸
NOT (…)。SQL 三值逻辑下,被比较列为 NULL 的行落进 UNKNOWN 而被排除;driver-memory/formula返回这些行。#5146 已由维护者拍板取 NULL-safe,PR #5296 把 driver-sql 对齐到了 JS 家族(
NOT (col IS NOT NULL AND …),守卫按算子极性下推到叶子)。该 PR 合入后,本文件是仓内最后一个仍按三值逻辑回答
$not的SQL 家族实现 —— 也就是说:同一条 read scope,普通查询与分析查询会给出不同的可见
集合。
修的方向已经有现成参照:PR #5296 的
nullSafeNegationOperand(纯函数,逐算子极性表),照抄到这个编译器上即可,不需要重新裁定语义。
分叉二:
{ $not: {} }编译成空字符串 → 整表compileNode({})返回'',于是if (inner)为假,整条$not不产出任何子句;applyReadScope又有if (!sql) return;,所以一条{ $not: {} }的 read scope完全不加 WHERE —— 本该零行(
NOT TRUE ≡ FALSE),实际整表。这正是 #5134 / PR #5243 在 driver-sql 上修掉的那条(那边现在编译成
1 = 0),方向是静默放松,而这里的宿主是 RLS。同一段循环里
$and/$or的空数组是fail-closed 抛错的(第 54-56 行),所以缺的只有
$not这一格。顺带(方向相反、危害小得多):
$or: [{}, {a: 1}]里的{}编译成''后被.filter((s) => s.length > 0)丢掉,结果是(a = 1),而布尔代数上{}是 TRUE析取项、整个
$or为 TRUE —— 静默收紧。建议
两处都在同一个函数里,建议一次 PR 修完(与 #5115 的在飞改动错开或合并排期)。
packages/services/service-analytics/src/__tests__/read-scope-sql-conformance.test.ts已经在跑
FILTER_LOGIC_CASES,但那张表刻意不含 null 处理,所以两条都不会被现有门禁发现 —— 跨驱动的 null case 归 spec 车道(#5239 / #5146 的 spec 半边)。
关联
#5146(
$notNULL 语义拍板)、PR #5296(driver-sql 侧实现,可直接参照)、#5134 / PR #5243(布尔单位元)、#5115(该包在飞)。