一句话
让 FieldReferenceSchema({ $field: '...' })在 SQL 下推路径上真正执行 —— 编译为列对列比较(where "amount" > "budget"),补齐 #5041 裁决 C 明确挂起的那一半。
背景:本单是 #5041 的下半场
#5041 的维护者裁决(2026-08-04)选了 C —— 最低限度:SqlDriver 遇到 { $field } 值形态时按 ADR-0112 响亮拒绝(INVALID_FILTER + httpStatus 400),消除裸 TypeError 与 $in/$between 列表里的静默零行;保留 spec 声明;真正的 SQL 实现另立本单。
也就是说,现状是已知且诚实的半态,不是遗漏:
| 执行路径 |
$field 行为 |
内存求值 matchesFilter(packages/formula/src/matches-filter.ts) |
✅ 按记录解析引用,支持点号路径 |
SQL 下推 driver-sql / driver-sqlite-wasm |
❌ INVALID_FILTER 响亮拒绝(#5041 已落地) |
生产者是真实的:compileCelToFilter(packages/formula/src/cel-to-filter.ts)在把含字段间比较的 CEL 权限 / RLS 规则转译为 filter 时会产出 { $field: path }。所以今天这类规则只有内存路径能跑,一旦下推到 SQL 就是一个 400,而不是结果。
本单要做的事
在 packages/plugins/driver-sql 的比较发射点(applyFilterCondition / applyAstComparison)把 { $field: 'col' } 编译成列引用而非绑定值 —— SQL 层面 where ?? > ?? 是平凡的,难点全在语义边界(见下)。落地时把 #5041 装的那道拒绝闸门收窄到「仍然编译不了的形态」,而不是整个删掉。
验收面:
两个待决语义点(本单一并议,勿默认)
1. 点号关联路径的语义
内存求值器的 getPath 对 { $field: 'account.owner_id' } 是沿对象逐层取属性。SQL 侧同一个字符串有至少三种读法:
选哪个直接决定 FieldReferenceSchema 的 $field 是「列名」还是「路径」,属于契约面,不应由实现顺手定。
2. 被引用列名的校验边界
$field 的值最终要进入 SQL 标识符位置。cloud#1051 讨论过:放行不校验等于拆护栏。需要明确:
- 是否只允许对象已声明的字段(
initObjects 已知列),未知列一律 INVALID_FILTER?
- 系统列(
created_at / space / 租户列)是否可被引用?尤其租户隔离列若可被引用,是否构成越权比较面?
- 外部表 / 联邦表(ADR-0015,列集未知)上如何取舍 —— 拒绝,还是放行由数据库报错?
倾向:声明即枚举,只接受已声明字段,未知列在编译期响亮拒绝 —— 与本仓「declared = enforced」的一贯姿态一致,也让 AI 产出的元数据在写的时候就错不了,而不是在数据库里错。但这是需要维护者拍板的契约决定。
Refs
一句话
让
FieldReferenceSchema({ $field: '...' })在 SQL 下推路径上真正执行 —— 编译为列对列比较(where "amount" > "budget"),补齐 #5041 裁决 C 明确挂起的那一半。背景:本单是 #5041 的下半场
#5041 的维护者裁决(2026-08-04)选了 C —— 最低限度:SqlDriver 遇到
{ $field }值形态时按 ADR-0112 响亮拒绝(INVALID_FILTER+httpStatus 400),消除裸TypeError与$in/$between列表里的静默零行;保留 spec 声明;真正的 SQL 实现另立本单。也就是说,现状是已知且诚实的半态,不是遗漏:
$field行为matchesFilter(packages/formula/src/matches-filter.ts)driver-sql/driver-sqlite-wasmINVALID_FILTER响亮拒绝(#5041 已落地)生产者是真实的:
compileCelToFilter(packages/formula/src/cel-to-filter.ts)在把含字段间比较的 CEL 权限 / RLS 规则转译为 filter 时会产出{ $field: path }。所以今天这类规则只有内存路径能跑,一旦下推到 SQL 就是一个 400,而不是结果。本单要做的事
在
packages/plugins/driver-sql的比较发射点(applyFilterCondition/applyAstComparison)把{ $field: 'col' }编译成列引用而非绑定值 —— SQL 层面where ?? > ??是平凡的,难点全在语义边界(见下)。落地时把 #5041 装的那道拒绝闸门收窄到「仍然编译不了的形态」,而不是整个删掉。验收面:
{ amount: { $gt: { $field: 'budget' } } }在 SqlDriver 上返回正确行集,与内存求值器对同一条 filter 的结果一致(建议做成跨路径 conformance 测试,和filter-logic-conformance/temporal-conformance同形);INVALID_FILTER信封;sql-driver-cross-field-reference.test.ts([spec]$field跨字段比较:spec 声明 + cel-to-filter 产出,但无任何 SQL 执行层实现 —— enforce-or-remove 裁决位 #5041 装的)随之改写,不是删除。两个待决语义点(本单一并议,勿默认)
1. 点号关联路径的语义
内存求值器的
getPath对{ $field: 'account.owner_id' }是沿对象逐层取属性。SQL 侧同一个字符串有至少三种读法:$field跨字段比较:spec 声明 + cel-to-filter 产出,但无任何 SQL 执行层实现 —— enforce-or-remove 裁决位 #5041 想消除的那类分裂);选哪个直接决定
FieldReferenceSchema的$field是「列名」还是「路径」,属于契约面,不应由实现顺手定。2. 被引用列名的校验边界
$field的值最终要进入 SQL 标识符位置。cloud#1051 讨论过:放行不校验等于拆护栏。需要明确:initObjects已知列),未知列一律INVALID_FILTER?created_at/space/ 租户列)是否可被引用?尤其租户隔离列若可被引用,是否构成越权比较面?倾向:声明即枚举,只接受已声明字段,未知列在编译期响亮拒绝 —— 与本仓「declared = enforced」的一贯姿态一致,也让 AI 产出的元数据在写的时候就错不了,而不是在数据库里错。但这是需要维护者拍板的契约决定。
Refs
$field跨字段比较:spec 声明 + cel-to-filter 产出,但无任何 SQL 执行层实现 —— enforce-or-remove 裁决位 #5041(裁决 C + 完整探针证据)INVALID_FILTER信封与错误码词表)