diff --git a/packages/db/src/services/chart-sql.test.ts b/packages/db/src/services/chart-sql.test.ts index f9c997a7e..1758ea69e 100644 --- a/packages/db/src/services/chart-sql.test.ts +++ b/packages/db/src/services/chart-sql.test.ts @@ -302,6 +302,22 @@ describe('chart.service / getChartSql', () => { await explain(sql); }); + itCH( + 'one_event_per_user + property breakdown resolves the events alias', + async () => { + const sql = await getChartSql({ + event: event({ segment: 'one_event_per_user' }), + breakdowns: [breakdown('properties.linked')], + interval: 'day', + startDate: START, + endDate: END, + projectId: PROJECT_ID, + timezone: 'UTC', + }); + await explain(sql); + }, + ); + // Regressions from HyperDX 2026-05-14 → 2026-05-17 ClickHouse error log. // Saved reports / older clients send field names that don't match the events // schema; the chart service used to inline them verbatim, crashing parse. @@ -416,6 +432,39 @@ describe('chart.service / getAggregateChartSql', () => { expect(sql).toContain("e.properties['__query.utm_source']"); await explain(sql); }); + + itCH( + 'one_event_per_user + group filter drops the out-of-scope WHERE', + async () => { + const sql = await getAggregateChartSql({ + event: event({ + segment: 'one_event_per_user', + filters: [{ name: 'group.plan', operator: 'is', value: ['pro'] }], + }), + breakdowns: [], + startDate: START, + endDate: END, + projectId: PROJECT_ID, + timezone: 'UTC', + }); + await explain(sql); + }, + ); + + itCH( + 'one_event_per_user + property breakdown resolves the events alias', + async () => { + const sql = await getAggregateChartSql({ + event: event({ segment: 'one_event_per_user' }), + breakdowns: [breakdown('properties.linked')], + startDate: START, + endDate: END, + projectId: PROJECT_ID, + timezone: 'UTC', + }); + await explain(sql); + }, + ); }); describe('overview.service / getRawWhereClause (UTM remapping)', () => { diff --git a/packages/db/src/services/chart.service.ts b/packages/db/src/services/chart.service.ts index 3e121c11e..967d3a5e8 100644 --- a/packages/db/src/services/chart.service.ts +++ b/packages/db/src/services/chart.service.ts @@ -817,12 +817,13 @@ export async function getChartSql({ ' AND ' )} ORDER BY profile_id, created_at DESC - ) as subQuery`; + ) as e`; sb.joins = {}; // Filters were already applied inside the subquery, and the outer query - // selects from `subQuery` — the `e` alias used in sb.where is no longer - // in scope, so re-emitting WHERE would produce - // "Unknown identifier `e.name`". Clear it. + // selects from the subquery aliased `e` — the `e` alias used in sb.where + // still resolves to it (the subquery does `SELECT * FROM events e`), but + // re-emitting WHERE here would just re-apply the same filters a second + // time. Clear it. sb.where = {}; const sql = rewriteProfilePropertyRefs( @@ -1166,8 +1167,14 @@ export async function getAggregateChartSql({ ' AND ' )} ORDER BY profile_id, created_at DESC - ) as subQuery`; + ) as e`; sb.joins = {}; + // Filters were already applied inside the subquery. A profile or group + // filter's join (and any ARRAY JOIN alias like _group_id) is scoped to + // it and is gone now that sb.joins is cleared, so re-emitting WHERE here + // would produce "Unknown identifier `_group_id`"/`profile.*`. Clear it, + // matching getChartSql. + sb.where = {}; const sql = rewriteProfilePropertyRefs(getSql(), profileProps.keys); console.log('-- Aggregate Chart --');