Skip to content

Commit 90875b9

Browse files
committed
fix(tesseract): Dimnesions with granularity in group_by + drivers support
1 parent 36cda9d commit 90875b9

File tree

9 files changed

+233
-20
lines changed

9 files changed

+233
-20
lines changed

packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,18 @@ describe('SQL Generation', () => {
302302
type: 'sum',
303303
group_by: [visitors.source]
304304
},
305+
visitors_revenue_per_year: {
306+
multi_stage: true,
307+
sql: \`\${revenue}\`,
308+
type: 'sum',
309+
group_by: [visitors.created_at.year]
310+
},
311+
visitors_revenue_reduce_day: {
312+
multi_stage: true,
313+
sql: \`\${revenue}\`,
314+
type: 'sum',
315+
reduce_by: [visitors.created_at.day]
316+
},
305317
visitors_revenue_without_date: {
306318
multi_stage: true,
307319
sql: \`\${revenue}\`,
@@ -4434,6 +4446,184 @@ SELECT 1 AS revenue, cast('2024-01-01' AS timestamp) as time UNION ALL
44344446
}]
44354447
));
44364448

4449+
if (getEnv('nativeSqlPlanner')) {
4450+
it('multi stage sum with group by time dim with granularity', async () => runQueryTest(
4451+
{
4452+
measures: ['visitors.visitors_revenue_per_year', 'visitors.revenue'],
4453+
dimensions: ['visitors.source'],
4454+
timeDimensions: [{
4455+
dimension: 'visitors.created_at',
4456+
granularity: 'year',
4457+
dateRange: ['2016-01-01', '2017-01-30']
4458+
}],
4459+
timezone: 'America/Los_Angeles',
4460+
order: [{
4461+
id: 'visitors.source'
4462+
}, {
4463+
id: 'visitors.created_at'
4464+
}],
4465+
},
4466+
4467+
[{
4468+
visitors__source: 'google',
4469+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4470+
visitors__visitors_revenue_per_year: '1500',
4471+
visitors__revenue: '300'
4472+
},
4473+
{
4474+
visitors__source: 'some',
4475+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4476+
visitors__visitors_revenue_per_year: '1500',
4477+
visitors__revenue: '300'
4478+
},
4479+
{
4480+
visitors__source: null,
4481+
visitors__created_at_year: '2016-01-01T00:00:00.000Z',
4482+
visitors__visitors_revenue_per_year: '500',
4483+
visitors__revenue: '500'
4484+
},
4485+
{
4486+
visitors__source: null,
4487+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4488+
visitors__visitors_revenue_per_year: '1500',
4489+
visitors__revenue: '900'
4490+
}]
4491+
));
4492+
} else {
4493+
it.skip('multi stage sum with group by time dim with granularity', async () => {
4494+
// Works only in Tesseract
4495+
});
4496+
}
4497+
4498+
if (getEnv('nativeSqlPlanner')) {
4499+
it('multi stage sum multiple time dims group by time dim with granularity', async () => runQueryTest(
4500+
{
4501+
measures: ['visitors.visitors_revenue_per_year', 'visitors.revenue'],
4502+
dimensions: ['visitors.source'],
4503+
timeDimensions: [{
4504+
dimension: 'visitors.created_at',
4505+
granularity: 'year',
4506+
dateRange: ['2014-01-01', '2018-01-30']
4507+
},
4508+
{
4509+
dimension: 'visitors.created_at',
4510+
granularity: 'day',
4511+
dateRange: ['2014-01-01', '2018-01-30']
4512+
}],
4513+
timezone: 'America/Los_Angeles',
4514+
order: [{
4515+
id: 'visitors.source'
4516+
}, {
4517+
id: 'visitors.created_at'
4518+
}],
4519+
},
4520+
4521+
[
4522+
{
4523+
visitors__source: 'google',
4524+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4525+
visitors__created_at_day: '2017-01-05T00:00:00.000Z',
4526+
visitors__visitors_revenue_per_year: '1500',
4527+
visitors__revenue: '300'
4528+
},
4529+
{
4530+
visitors__source: 'some',
4531+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4532+
visitors__created_at_day: '2017-01-02T00:00:00.000Z',
4533+
visitors__visitors_revenue_per_year: '1500',
4534+
visitors__revenue: '100'
4535+
},
4536+
{
4537+
visitors__source: 'some',
4538+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4539+
visitors__created_at_day: '2017-01-04T00:00:00.000Z',
4540+
visitors__visitors_revenue_per_year: '1500',
4541+
visitors__revenue: '200'
4542+
},
4543+
{
4544+
visitors__source: null,
4545+
visitors__created_at_year: '2016-01-01T00:00:00.000Z',
4546+
visitors__created_at_day: '2016-09-06T00:00:00.000Z',
4547+
visitors__visitors_revenue_per_year: '500',
4548+
visitors__revenue: '500'
4549+
},
4550+
{
4551+
visitors__source: null,
4552+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4553+
visitors__created_at_day: '2017-01-06T00:00:00.000Z',
4554+
visitors__visitors_revenue_per_year: '1500',
4555+
visitors__revenue: '900'
4556+
}
4557+
]
4558+
));
4559+
} else {
4560+
it.skip('multi stage sum multiple time dims group by time dim with granularity', async () => {
4561+
// Works only in Tesseract
4562+
});
4563+
}
4564+
4565+
if (getEnv('nativeSqlPlanner')) {
4566+
it('multi stage sum multiple time dims reduce by time dim with granularity', async () => runQueryTest(
4567+
{
4568+
measures: ['visitors.visitors_revenue_reduce_day', 'visitors.revenue'],
4569+
timeDimensions: [{
4570+
dimension: 'visitors.created_at',
4571+
granularity: 'year',
4572+
dateRange: ['2014-01-01', '2018-01-30']
4573+
},
4574+
{
4575+
dimension: 'visitors.created_at',
4576+
granularity: 'day',
4577+
dateRange: ['2014-01-01', '2018-01-30']
4578+
}],
4579+
timezone: 'America/Los_Angeles',
4580+
order: [{
4581+
id: 'visitors.source'
4582+
}, {
4583+
id: 'visitors.created_at'
4584+
}],
4585+
},
4586+
4587+
[
4588+
4589+
{
4590+
visitors__created_at_year: '2016-01-01T00:00:00.000Z',
4591+
visitors__created_at_day: '2016-09-06T00:00:00.000Z',
4592+
visitors__visitors_revenue_reduce_day: '500',
4593+
visitors__revenue: '500'
4594+
},
4595+
{
4596+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4597+
visitors__created_at_day: '2017-01-02T00:00:00.000Z',
4598+
visitors__visitors_revenue_reduce_day: '1500',
4599+
visitors__revenue: '100'
4600+
},
4601+
{
4602+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4603+
visitors__created_at_day: '2017-01-04T00:00:00.000Z',
4604+
visitors__visitors_revenue_reduce_day: '1500',
4605+
visitors__revenue: '200'
4606+
},
4607+
{
4608+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4609+
visitors__created_at_day: '2017-01-05T00:00:00.000Z',
4610+
visitors__visitors_revenue_reduce_day: '1500',
4611+
visitors__revenue: '300'
4612+
},
4613+
{
4614+
visitors__created_at_year: '2017-01-01T00:00:00.000Z',
4615+
visitors__created_at_day: '2017-01-06T00:00:00.000Z',
4616+
visitors__visitors_revenue_reduce_day: '1500',
4617+
visitors__revenue: '900'
4618+
}
4619+
]
4620+
));
4621+
} else {
4622+
it.skip('multi stage sum multiple time dims reduce by time dim with granularity', async () => {
4623+
// Works only in Tesseract
4624+
});
4625+
}
4626+
44374627
if (getEnv('nativeSqlPlanner')) {
44384628
it('multi stage sum with group by over view', async () => runQueryTest(
44394629
{

rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ impl BaseFilter {
203203
let Some(granularity_obj) = GranularityHelper::make_granularity_obj(
204204
self.query_tools.cube_evaluator().clone(),
205205
&mut evaluator_compiler,
206-
self.query_tools.timezone().clone(),
207206
&symbol.cube_name(),
208207
&symbol.name(),
209208
Some(query_granularity.clone()),

rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ impl MultiStageMemberQueryPlanner {
132132
let Some(granularity_obj) = GranularityHelper::make_granularity_obj(
133133
self.query_tools.cube_evaluator().clone(),
134134
&mut evaluator_compiler,
135-
self.query_tools.timezone().clone(),
136135
&time_dimension.cube_name(),
137136
&time_dimension.name(),
138137
Some(query_granularity.clone()),

rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,6 @@ impl QueryProperties {
172172
let granularity_obj = GranularityHelper::make_granularity_obj(
173173
query_tools.cube_evaluator().clone(),
174174
&mut evaluator_compiler,
175-
query_tools.timezone().clone(),
176175
&base_symbol.cube_name(),
177176
&base_symbol.name(),
178177
d.granularity.clone(),

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,16 @@ impl Compiler {
120120
Ok(collector.extract_result())
121121
}
122122

123+
pub fn timezone(&self) -> Tz {
124+
self.timezone.clone()
125+
}
126+
123127
pub fn compile_sql_call(
124128
&mut self,
125129
cube_name: &String,
126130
member_sql: Rc<dyn MemberSql>,
127131
) -> Result<Rc<SqlCall>, CubeError> {
128-
let dep_builder =
129-
DependenciesBuilder::new(self, self.cube_evaluator.clone(), self.timezone.clone());
132+
let dep_builder = DependenciesBuilder::new(self, self.cube_evaluator.clone());
130133
let deps = dep_builder.build(cube_name.clone(), member_sql.clone())?;
131134
let sql_call = SqlCall::new(member_sql, deps);
132135
Ok(Rc::new(sql_call))

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/dependecy.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use crate::cube_bridge::evaluator::{CallDep, CubeEvaluator};
44
use crate::cube_bridge::member_sql::MemberSql;
55
use crate::planner::sql_evaluator::TimeDimensionSymbol;
66
use crate::planner::GranularityHelper;
7-
use chrono_tz::Tz;
87
use cubenativeutils::CubeError;
98
use std::collections::HashMap;
109
use std::rc::Rc;
@@ -65,19 +64,13 @@ pub enum Dependency {
6564
pub struct DependenciesBuilder<'a> {
6665
compiler: &'a mut Compiler,
6766
cube_evaluator: Rc<dyn CubeEvaluator>,
68-
timezone: Tz,
6967
}
7068

7169
impl<'a> DependenciesBuilder<'a> {
72-
pub fn new(
73-
compiler: &'a mut Compiler,
74-
cube_evaluator: Rc<dyn CubeEvaluator>,
75-
timezone: Tz,
76-
) -> Self {
70+
pub fn new(compiler: &'a mut Compiler, cube_evaluator: Rc<dyn CubeEvaluator>) -> Self {
7771
DependenciesBuilder {
7872
compiler,
7973
cube_evaluator,
80-
timezone,
8174
}
8275
}
8376

@@ -173,7 +166,6 @@ impl<'a> DependenciesBuilder<'a> {
173166
if let Some(granularity_obj) = GranularityHelper::make_granularity_obj(
174167
self.cube_evaluator.clone(),
175168
self.compiler,
176-
self.timezone.clone(),
177169
cube_name,
178170
&dep.name,
179171
Some(granularity.clone()),

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ use crate::cube_bridge::dimension_definition::DimensionDefinition;
44
use crate::cube_bridge::evaluator::CubeEvaluator;
55
use crate::cube_bridge::member_sql::MemberSql;
66
use crate::planner::query_tools::QueryTools;
7+
use crate::planner::sql_evaluator::TimeDimensionSymbol;
78
use crate::planner::sql_evaluator::{sql_nodes::SqlNode, Compiler, SqlCall, SqlEvaluatorVisitor};
89
use crate::planner::sql_templates::PlanSqlTemplates;
10+
use crate::planner::GranularityHelper;
911
use crate::planner::SqlInterval;
1012
use cubenativeutils::CubeError;
1113
use std::rc::Rc;
@@ -287,6 +289,7 @@ impl DimensionSymbol {
287289
pub struct DimensionSymbolFactory {
288290
cube_name: String,
289291
name: String,
292+
granularity: Option<String>,
290293
sql: Option<Rc<dyn MemberSql>>,
291294
definition: Rc<dyn DimensionDefinition>,
292295
cube_evaluator: Rc<dyn CubeEvaluator>,
@@ -302,10 +305,12 @@ impl DimensionSymbolFactory {
302305
.into_iter();
303306
let cube_name = iter.next().unwrap();
304307
let name = iter.next().unwrap();
308+
let granularity = iter.next();
305309
let definition = cube_evaluator.dimension_by_path(full_name.clone())?;
306310
Ok(Self {
307311
cube_name,
308312
name,
313+
granularity,
309314
sql: definition.sql()?,
310315
definition,
311316
cube_evaluator,
@@ -338,6 +343,7 @@ impl SymbolFactory for DimensionSymbolFactory {
338343
let Self {
339344
cube_name,
340345
name,
346+
granularity,
341347
sql,
342348
definition,
343349
cube_evaluator,
@@ -477,9 +483,9 @@ impl SymbolFactory for DimensionSymbolFactory {
477483
&& longitude.is_none()
478484
&& !is_multi_stage);
479485

480-
Ok(MemberSymbol::new_dimension(DimensionSymbol::new(
481-
cube_name,
482-
name,
486+
let symbol = MemberSymbol::new_dimension(DimensionSymbol::new(
487+
cube_name.clone(),
488+
name.clone(),
483489
alias,
484490
sql,
485491
is_reference,
@@ -491,6 +497,32 @@ impl SymbolFactory for DimensionSymbolFactory {
491497
time_shift,
492498
time_shift_pk,
493499
is_self_time_shift_pk,
494-
)))
500+
));
501+
502+
if let Some(granularity) = &granularity {
503+
if let Some(granularity_obj) = GranularityHelper::make_granularity_obj(
504+
cube_evaluator.clone(),
505+
compiler,
506+
&cube_name,
507+
&name,
508+
Some(granularity.clone()),
509+
)? {
510+
let time_dim_symbol = MemberSymbol::new_time_dimension(TimeDimensionSymbol::new(
511+
symbol,
512+
Some(granularity.clone()),
513+
Some(granularity_obj),
514+
None,
515+
));
516+
return Ok(time_dim_symbol);
517+
} else {
518+
return Err(CubeError::user(format!(
519+
"Undefined granularity {} for time dimension {}",
520+
granularity,
521+
symbol.full_name()
522+
)));
523+
}
524+
}
525+
526+
Ok(symbol)
495527
}
496528
}

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ impl TimeDimensionSymbol {
7979
let new_granularity_obj = GranularityHelper::make_granularity_obj(
8080
query_tools.cube_evaluator().clone(),
8181
&mut evaluator_compiler,
82-
query_tools.timezone(),
8382
&&self.base_symbol.cube_name(),
8483
&self.base_symbol.name(),
8584
new_granularity.clone(),

rust/cubesqlplanner/cubesqlplanner/src/planner/time_dimension/granularity_helper.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,11 @@ impl GranularityHelper {
224224
pub fn make_granularity_obj(
225225
cube_evaluator: Rc<dyn CubeEvaluator>,
226226
compiler: &mut Compiler,
227-
timezone: Tz,
228227
cube_name: &String,
229228
name: &String,
230229
granularity: Option<String>,
231230
) -> Result<Option<Granularity>, CubeError> {
231+
let timezone = compiler.timezone();
232232
let granularity_obj = if let Some(granularity) = &granularity {
233233
let path = vec![
234234
cube_name.clone(),

0 commit comments

Comments
 (0)