feat(analytics): cross-object grouping via FK-expand + generateSql scope preview (#3654 residual 2 + capability)#3672
Closed
os-zhuang wants to merge 2 commits into
Closed
feat(analytics): cross-object grouping via FK-expand + generateSql scope preview (#3654 residual 2 + capability)#3672os-zhuang wants to merge 2 commits into
os-zhuang wants to merge 2 commits into
Conversation
…#3654 residual 2) generateSql (the representative statement echoed to /analytics/sql and dataset responses) rendered the query's own filters but NOT the tenant/RLS read scope that execute() injects via withReadScope — so the previewed WHERE understated what actually runs. It now renders the base object's scope too (parameterized, $n placeholders), ANDed with the query filters. For consistency generateSql also applies the #3654 cross-object guard, so /analytics/sql no longer previews a dotted-column statement for a query execute() refuses to run — same clear error instead of misleading SQL. Display-only (no data leak — the string is documentation, values stay placeholders); no change when no read-scope provider is configured. Tests: generateSql now renders "opportunity"."organization_id" = $n and ANDs it with the query filter; a cross-object preview rejects; the no-provider case is unchanged. Reverting the change turns the three load-bearing assertions red. Co-Authored-By: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 6 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…#3654) The ObjectQL fallback path (date-granularity bucketing, in-memory driver, federated) could not join, so cross-object grouping like `revenue by account.region` was rejected (#3664 stopgap). It now serves the common case by FK-expand: 1. group the base aggregate on the lookup FK column (`account`) — which the engine CAN do — scoped to the base object; 2. resolve each FK id to the related attribute (`region`) with a read of the referenced object scoped to THAT object's own RLS; 3. re-bucket by the resolved attribute in memory, recombining measures (sum/count add, min/max take the extremum). A base row whose referenced record the caller cannot read buckets under an explicit `(restricted)` group: the measure still counts (grand totals preserved) but the hidden attribute never appears — no leak (ADR-0021 D-C / #3602). `/analytics/sql` renders the equivalent LEFT JOIN. Bounded — still rejected LOUD (never silently wrong): cross-object in a MEASURE or FILTER, multi-hop dims, and non-recombinable measures (avg/count_distinct) with a cross-object dim. NativeSQLStrategy (the normal SQL path) is unchanged. The pure re-bucketing step is isolated in cross-object-rebucket.ts and exhaustively unit-tested (recombination, restricted-total conservation, null-vs-restricted, multi-dim). Integration tests drive the real AnalyticsService with a two-call aggregate stub, asserting both the base and the FK-resolution are scoped and the restricted bucket forms; reverting the strategy turns all three red. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#3654 收尾:残留 2(SQL 预览)+ 能力项(跨对象分组)。两个独立 commit。
Commit 1 — 残留 2:generateSql 渲出 read scope(展示层)
ObjectQLStrategy.generateSql(回显给/analytics/sql)#3588 后已渲 WHERE,但只含查询 filter,漏了 execute 注入的 tenant/RLS scope —— 预览低估真实约束。现在把 base 对象 scope 也渲进 WHERE(参数化,$and)。不执行、不泄露。Commit 2 — 能力项:信封内跨对象分组走 FK 展开(#3654 主体)
引擎不会 join,所以 ObjectQL 回退路径(日期分桶 / in-memory / federated)此前拒绝跨对象分组如
revenue by account.region(#3664 止血)。现在直接服务常见情形:account)分组跑 base 聚合(引擎能做),加 base 对象 scope;region);受限行归
(restricted)桶(你拍板的语义):读不到被引用记录的行,度量仍计入、总计守恒,但隐藏记录的属性名绝不出现 —— 无泄露(ADR-0021 D-C / #3602 类)。/analytics/sql渲出等价LEFT JOIN。刻意有界 —— 信封外继续 LOUD 拒绝(绝不静默错答):跨对象出现在度量/过滤器(需真 join)、多跳(
a.b.c)、avg/count_distinct(跨桶不可重组)配跨对象维。NativeSQLStrategy(正常 SQL 路径)不变。测试
AnalyticsService+ 两次 aggregate stub,断言 base 与 FK 解析都加 scope、restricted 桶成形、/analytics/sql渲 LEFT JOIN。回退策略 → 三个全红。$and、无 provider 不变、信封外预览拒绝。tsc无新增错误。边界(诚实)
未加真栈 dogfood e2e:能力的正确性核心(重聚合)已被纯函数测穷举,编排 + 双 scope 已被真实 AnalyticsService 集成测覆盖,真实引擎 FK 分组与被引用 scoped 读均由既有合并工作在栈内证过。若需端到端 capstone 可后补。
🤖 Generated with Claude Code