在 #6212 批 B(aggregate 收窄)实施期间实测发现,记录备查。观察类,不挂 pm:queue,请分诊轮定级。
事实
GroupByNodeSchema 声明的结构化条目有三个键:field / dateGranularity? / alias?,其中 alias 的 describe 是 "Alias for the projected group value"(默认回落到 field)。
读它的只有内存分桶那一条路:
| 位置 |
是否读 alias |
packages/objectql/src/in-memory-aggregation.ts:89 |
✅ const fieldName = typeof g === 'string' ? g : (g.alias ?? g.field); |
packages/drivers/driver-sql/src/sql-driver.ts aggregate() 的 groupBy 循环 |
❌ 两条分支都只用 g.field(带 granularity 的按 g.field 起别名;不带的 groupBy(g.field) + select(g.field)) |
packages/drivers/driver-sqlite-wasm |
❌ 整个继承上面那份 |
packages/drivers/driver-turso 本地 / 远端两面 |
❌ 同上(远端面在 #6212 批 B 之后开始编结构化条目,也刻意不读 alias,以免只在一面读而造出新分叉) |
后果
同一份 groupBy: [{ field: 'closed_at', dateGranularity: 'month', alias: 'qtr' }]:
- 引擎判定可下推(驱动发布了该 granularity)⇒ 走
driver.aggregate ⇒ 结果行的列键是 closed_at;
- 引擎判定不可下推(能力位缺该 granularity、或
timezone 非 UTC 强制走内存)⇒ 走 find() + applyInMemoryAggregation ⇒ 列键是 qtr。
也就是说同一条查询的结果列名取决于引擎当次选了哪条路,而选路依据是驱动能力位与时区 —— 调用方看不见的东西。packages/objectql/src/in-memory-aggregation.test.ts:45 正是拿 alias: 'qtr' 写的,所以内存那一半有覆盖,下推那一半没有,两半也从没被放在一起比过。
这是 ADR-0049 的形状
alias 是已声明、部分执行的键:写了它,一半的执行路径认,另一半静默忽略。enforce-or-remove 两条路都摆着,且分别有代价:
- enforce:三个 SQL 面都读
alias(SELECT … AS "qtr",GROUP BY 仍按 field)。要顺带确认 presentReadColumns 的 presentedOutput 映射改按 alias 记(sql-driver.ts 里那张 map 现在以列名为键),以及 having 的 post-filter(它按「聚合别名 + groupBy 投影」判定)跟着改口径。
- remove:删
GroupByNodeSchema.alias(走 ADR-0087 墓碑 + 转换层),代价是内存那一半现有行为要改,且要先量一量非测试书写者。
⚠️ 我没有量过 alias 的真实生产者(showcase / CRM / 图表配置面是否有人写),定级前需要补这一量 —— 这正是 #5021「能力扩张要有真实业务牵引」那条轴要问的。
批 B 只做了「不加剧」:远端 turso 开始编结构化 groupBy 条目时,刻意不读 alias,与 SqlDriver.aggregate 保持一致 —— 只在一面读会是新分叉而不是修复。本分歧既非批 B 造成、也不在批 B 范围内,故单独记账。
关联:#6212、ADR-0049。
在 #6212 批 B(
aggregate收窄)实施期间实测发现,记录备查。观察类,不挂pm:queue,请分诊轮定级。事实
GroupByNodeSchema声明的结构化条目有三个键:field/dateGranularity?/alias?,其中alias的 describe 是 "Alias for the projected group value"(默认回落到field)。读它的只有内存分桶那一条路:
aliaspackages/objectql/src/in-memory-aggregation.ts:89const fieldName = typeof g === 'string' ? g : (g.alias ?? g.field);packages/drivers/driver-sql/src/sql-driver.tsaggregate()的 groupBy 循环g.field(带 granularity 的按g.field起别名;不带的groupBy(g.field)+select(g.field))packages/drivers/driver-sqlite-wasmpackages/drivers/driver-turso本地 / 远端两面alias,以免只在一面读而造出新分叉)后果
同一份
groupBy: [{ field: 'closed_at', dateGranularity: 'month', alias: 'qtr' }]:driver.aggregate⇒ 结果行的列键是closed_at;timezone非 UTC 强制走内存)⇒ 走find()+applyInMemoryAggregation⇒ 列键是qtr。也就是说同一条查询的结果列名取决于引擎当次选了哪条路,而选路依据是驱动能力位与时区 —— 调用方看不见的东西。
packages/objectql/src/in-memory-aggregation.test.ts:45正是拿alias: 'qtr'写的,所以内存那一半有覆盖,下推那一半没有,两半也从没被放在一起比过。这是 ADR-0049 的形状
alias是已声明、部分执行的键:写了它,一半的执行路径认,另一半静默忽略。enforce-or-remove 两条路都摆着,且分别有代价:alias(SELECT … AS "qtr",GROUP BY 仍按field)。要顺带确认presentReadColumns的presentedOutput映射改按 alias 记(sql-driver.ts里那张 map 现在以列名为键),以及having的 post-filter(它按「聚合别名 + groupBy 投影」判定)跟着改口径。GroupByNodeSchema.alias(走 ADR-0087 墓碑 + 转换层),代价是内存那一半现有行为要改,且要先量一量非测试书写者。alias的真实生产者(showcase / CRM / 图表配置面是否有人写),定级前需要补这一量 —— 这正是 #5021「能力扩张要有真实业务牵引」那条轴要问的。与 #6212 批 B 的关系
批 B 只做了「不加剧」:远端 turso 开始编结构化 groupBy 条目时,刻意不读
alias,与SqlDriver.aggregate保持一致 —— 只在一面读会是新分叉而不是修复。本分歧既非批 B 造成、也不在批 B 范围内,故单独记账。关联:#6212、ADR-0049。