diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts index 9edd2dcd331b9..088f83ed341de 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts @@ -271,6 +271,18 @@ describe('SQL Generation', () => { type: 'sum', group_by: [] }, + visitors_revenue_per_source: { + multi_stage: true, + sql: \`\${revenue}\`, + type: 'sum', + group_by: [visitors.source] + }, + visitors_revenue_without_date: { + multi_stage: true, + sql: \`\${revenue}\`, + type: 'sum', + reduce_by: [visitors.created_at] + }, percentage_of_total: { multi_stage: true, sql: \`(100 * \${revenue} / NULLIF(\${visitors_revenue_total}, 0))::int\`, @@ -887,7 +899,6 @@ SELECT 1 AS revenue, cast('2024-01-01' AS timestamp) as time UNION ALL async function runQueryTest(q, expectedResult) { await compiler.compile(); const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, q); - const res = await dbRunner.testQuery(query.buildSqlAndParams()); console.log(JSON.stringify(res)); @@ -3759,6 +3770,186 @@ SELECT 1 AS revenue, cast('2024-01-01' AS timestamp) as time UNION ALL }] )); + it('multi stage sum with group by', async () => runQueryTest( + { + measures: ['visitors.visitors_revenue_per_source', 'visitors.revenue'], + dimensions: ['visitors.source', 'visitors.created_at'], + order: [{ + id: 'visitors.source' + }, { + id: 'visitors.created_at' + }], + }, + [{ + visitors__source: 'google', + visitors__created_at: '2017-01-06T00:00:00.000Z', + visitors__visitors_revenue_per_source: '300', + visitors__revenue: '300' + }, + { + visitors__source: 'some', + visitors__created_at: '2017-01-03T00:00:00.000Z', + visitors__visitors_revenue_per_source: '300', + visitors__revenue: '100' + }, + { + visitors__source: 'some', + visitors__created_at: '2017-01-05T00:00:00.000Z', + visitors__visitors_revenue_per_source: '300', + visitors__revenue: '200' + }, + { + visitors__source: null, + visitors__created_at: '2016-09-07T00:00:00.000Z', + visitors__visitors_revenue_per_source: '1400', + visitors__revenue: '500' + }, + { + visitors__source: null, + visitors__created_at: '2017-01-07T00:00:00.000Z', + visitors__visitors_revenue_per_source: '1400', + visitors__revenue: '900' + }] + )); + + if (getEnv('nativeSqlPlanner')) { + it('multi stage sum with group by over view', async () => runQueryTest( + { + measures: ['visitors_multi_stage.visitors_revenue_per_source', 'visitors_multi_stage.revenue'], + dimensions: ['visitors_multi_stage.source', 'visitors_multi_stage.created_at'], + order: [{ + id: 'visitors_multi_stage.source' + }, { + id: 'visitors_multi_stage.created_at' + }], + }, + [{ + visitors_multi_stage__source: 'google', + visitors_multi_stage__created_at: '2017-01-06T00:00:00.000Z', + visitors_multi_stage__visitors_revenue_per_source: '300', + visitors_multi_stage__revenue: '300' + }, + { + visitors_multi_stage__source: 'some', + visitors_multi_stage__created_at: '2017-01-03T00:00:00.000Z', + visitors_multi_stage__visitors_revenue_per_source: '300', + visitors_multi_stage__revenue: '100' + }, + { + visitors_multi_stage__source: 'some', + visitors_multi_stage__created_at: '2017-01-05T00:00:00.000Z', + visitors_multi_stage__visitors_revenue_per_source: '300', + visitors_multi_stage__revenue: '200' + }, + { + visitors_multi_stage__source: null, + visitors_multi_stage__created_at: '2016-09-07T00:00:00.000Z', + visitors_multi_stage__visitors_revenue_per_source: '1400', + visitors_multi_stage__revenue: '500' + }, + { + visitors_multi_stage__source: null, + visitors_multi_stage__created_at: '2017-01-07T00:00:00.000Z', + visitors_multi_stage__visitors_revenue_per_source: '1400', + visitors_multi_stage__revenue: '900' + }] + )); + } else { + it.skip('multi stage sum with reduce by over view', async () => { + // Works only in Tesseract + }); + } + + it('multi stage sum with reduce by', async () => runQueryTest( + { + measures: ['visitors.visitors_revenue_without_date', 'visitors.revenue'], + dimensions: ['visitors.source', 'visitors.created_at'], + order: [{ + id: 'visitors.source' + }, { + id: 'visitors.created_at' + }], + }, + [{ + visitors__source: 'google', + visitors__created_at: '2017-01-06T00:00:00.000Z', + visitors__visitors_revenue_without_date: '300', + visitors__revenue: '300' + }, + { + visitors__source: 'some', + visitors__created_at: '2017-01-03T00:00:00.000Z', + visitors__visitors_revenue_without_date: '300', + visitors__revenue: '100' + }, + { + visitors__source: 'some', + visitors__created_at: '2017-01-05T00:00:00.000Z', + visitors__visitors_revenue_without_date: '300', + visitors__revenue: '200' + }, + { + visitors__source: null, + visitors__created_at: '2016-09-07T00:00:00.000Z', + visitors__visitors_revenue_without_date: '1400', + visitors__revenue: '500' + }, + { + visitors__source: null, + visitors__created_at: '2017-01-07T00:00:00.000Z', + visitors__visitors_revenue_without_date: '1400', + visitors__revenue: '900' + }] + )); + + if (getEnv('nativeSqlPlanner')) { + it('multi stage sum with reduce by over view', async () => runQueryTest( + { + measures: ['visitors_multi_stage.visitors_revenue_without_date', 'visitors_multi_stage.revenue'], + dimensions: ['visitors_multi_stage.source', 'visitors_multi_stage.created_at'], + order: [{ + id: 'visitors_multi_stage.source' + }, { + id: 'visitors_multi_stage.created_at' + }], + }, + [{ + visitors_multi_stage__source: 'google', + visitors_multi_stage__created_at: '2017-01-06T00:00:00.000Z', + visitors_multi_stage__visitors_revenue_without_date: '300', + visitors_multi_stage__revenue: '300' + }, + { + visitors_multi_stage__source: 'some', + visitors_multi_stage__created_at: '2017-01-03T00:00:00.000Z', + visitors_multi_stage__visitors_revenue_without_date: '300', + visitors_multi_stage__revenue: '100' + }, + { + visitors_multi_stage__source: 'some', + visitors_multi_stage__created_at: '2017-01-05T00:00:00.000Z', + visitors_multi_stage__visitors_revenue_without_date: '300', + visitors_multi_stage__revenue: '200' + }, + { + visitors_multi_stage__source: null, + visitors_multi_stage__created_at: '2016-09-07T00:00:00.000Z', + visitors_multi_stage__visitors_revenue_without_date: '1400', + visitors_multi_stage__revenue: '500' + }, + { + visitors_multi_stage__source: null, + visitors_multi_stage__created_at: '2017-01-07T00:00:00.000Z', + visitors_multi_stage__visitors_revenue_without_date: '1400', + visitors_multi_stage__revenue: '900' + }] + )); + } else { + it.skip('multi stage sum with reduce by over view', async () => { + // Works only in Tesseract + }); + } + it('multiplied sum and count no dimensions through view', async () => runQueryTest( { measures: ['visitors_visitors_checkins_view.revenue', 'visitors_visitors_checkins_view.visitor_checkins_count'], diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs index a65aaa0f37dff..7cad1c4e66f54 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs @@ -341,13 +341,7 @@ impl MultiStageMemberQueryPlanner { let dimensions = if !reduce_by.is_empty() { dimensions .into_iter() - .filter(|d| { - if reduce_by.iter().any(|m| d.full_name() == m.full_name()) { - false - } else { - true - } - }) + .filter(|d| !reduce_by.iter().any(|m| d.has_member_in_reference_chain(m))) .collect_vec() } else { dimensions @@ -355,13 +349,7 @@ impl MultiStageMemberQueryPlanner { let dimensions = if let Some(group_by) = group_by { dimensions .into_iter() - .filter(|d| { - if group_by.iter().any(|m| d.full_name() == m.full_name()) { - true - } else { - false - } - }) + .filter(|d| group_by.iter().any(|m| d.has_member_in_reference_chain(m))) .collect_vec() } else { dimensions diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs index 5d01eca2d1fa0..c88a782544156 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs @@ -163,6 +163,21 @@ impl MemberSymbol { current } + pub fn has_member_in_reference_chain(&self, member: &Rc) -> bool { + if self.full_name() == member.full_name() { + return true; + } + + let mut current = self.reference_member(); + while let Some(reference) = current { + if reference.full_name() == member.full_name() { + return true; + } + current = reference.reference_member(); + } + false + } + pub fn owned_by_cube(&self) -> bool { match self { Self::Dimension(d) => d.owned_by_cube(),