Skip to content

[spec] SqlDriver 将 $field 编译为列对列比较(cross-field comparison push-down) #5222

Description

@xuyushun441-sys

一句话

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions