Skip to content

Commit 6f963a7

Browse files
committed
support granularities sql() in tesseract
1 parent 5a514ff commit 6f963a7

File tree

10 files changed

+96
-30
lines changed

10 files changed

+96
-30
lines changed

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/dimension_definition.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,8 @@ use serde::{Deserialize, Serialize};
1111
use std::any::Any;
1212
use std::rc::Rc;
1313

14-
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash)]
15-
pub struct GranularityDefinition {
16-
pub interval: String,
17-
pub origin: Option<String>,
18-
pub offset: Option<String>,
19-
}
2014
#[derive(Serialize, Deserialize, Debug)]
21-
pub struct DimenstionDefinitionStatic {
15+
pub struct DimensionDefinitionStatic {
2216
#[serde(rename = "type")]
2317
pub dimension_type: String,
2418
#[serde(rename = "ownedByCube")]
@@ -31,7 +25,7 @@ pub struct DimenstionDefinitionStatic {
3125
pub propagate_filters_to_sub_query: Option<bool>,
3226
}
3327

34-
#[nativebridge::native_bridge(DimenstionDefinitionStatic)]
28+
#[nativebridge::native_bridge(DimensionDefinitionStatic)]
3529
pub trait DimensionDefinition {
3630
#[nbridge(field, optional)]
3731
fn sql(&self) -> Result<Option<Rc<dyn MemberSql>>, CubeError>;

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::cube_definition::{CubeDefinition, NativeCubeDefinition};
22
use super::dimension_definition::{
3-
DimensionDefinition, GranularityDefinition, NativeDimensionDefinition,
3+
DimensionDefinition, NativeDimensionDefinition,
44
};
55
use super::measure_definition::{MeasureDefinition, NativeMeasureDefinition};
66
use super::member_sql::{MemberSql, NativeMemberSql};
@@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize};
1919
use std::any::Any;
2020
use std::collections::HashMap;
2121
use std::rc::Rc;
22+
use crate::cube_bridge::granularity_definition::{GranularityDefinition, NativeGranularityDefinition};
2223

2324
#[derive(Serialize, Deserialize, Debug)]
2425
pub struct CubeEvaluatorStatic {
@@ -54,7 +55,7 @@ pub trait CubeEvaluator {
5455
cube_name: String,
5556
sql: Rc<dyn MemberSql>,
5657
) -> Result<Vec<CallDep>, CubeError>;
57-
fn resolve_granularity(&self, path: Vec<String>) -> Result<GranularityDefinition, CubeError>;
58+
fn resolve_granularity(&self, path: Vec<String>) -> Result<Rc<dyn GranularityDefinition>, CubeError>;
5859
#[nbridge(vec)]
5960
fn pre_aggregations_for_cube_as_array(
6061
&self,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use super::member_sql::{MemberSql, NativeMemberSql};
2+
use cubenativeutils::wrappers::serializer::{
3+
NativeDeserialize, NativeDeserializer, NativeSerialize,
4+
};
5+
use cubenativeutils::wrappers::NativeContextHolder;
6+
use cubenativeutils::wrappers::NativeObjectHandle;
7+
use cubenativeutils::CubeError;
8+
use serde::{Deserialize, Serialize};
9+
use std::any::Any;
10+
use std::rc::Rc;
11+
12+
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash)]
13+
pub struct GranularityDefinitionStatic {
14+
pub interval: String,
15+
pub origin: Option<String>,
16+
pub offset: Option<String>,
17+
}
18+
19+
#[nativebridge::native_bridge(GranularityDefinitionStatic)]
20+
pub trait GranularityDefinition {
21+
#[nbridge(field, optional)]
22+
fn sql(&self) -> Result<Option<Rc<dyn MemberSql>>, CubeError>;
23+
}

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ pub mod segment_definition;
2929
pub mod sql_templates_render;
3030
pub mod sql_utils;
3131
pub mod struct_with_sql_member;
32+
pub mod granularity_definition;

rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::query_tools::QueryTools;
2-
use super::sql_evaluator::{MemberSymbol, TimeDimensionSymbol};
2+
use super::sql_evaluator::{Compiler, MemberSymbol, TimeDimensionSymbol};
33
use super::BaseDimension;
44
use super::Granularity;
55
use super::GranularityHelper;
@@ -96,6 +96,7 @@ impl BaseTimeDimension {
9696
pub fn try_new_required(
9797
query_tools: Rc<QueryTools>,
9898
member_evaluator: Rc<MemberSymbol>,
99+
compiler: &mut Compiler,
99100
granularity: Option<String>,
100101
date_range: Option<Vec<String>>,
101102
) -> Result<Rc<Self>, CubeError> {
@@ -116,14 +117,15 @@ impl BaseTimeDimension {
116117

117118
let granularity_obj = GranularityHelper::make_granularity_obj(
118119
query_tools.cube_evaluator().clone(),
120+
compiler,
119121
query_tools.timezone().clone(),
120122
&dimension.cube_name(),
121123
&dimension.name(),
122124
granularity.clone(),
123125
)?;
124126

125127
let date_range_tuple = if let Some(date_range) = &date_range {
126-
assert!(date_range.len() == 2);
128+
assert_eq!(date_range.len(), 2);
127129
Some((date_range[0].clone(), date_range[1].clone()))
128130
} else {
129131
None
@@ -150,15 +152,19 @@ impl BaseTimeDimension {
150152
&self,
151153
new_granularity: Option<String>,
152154
) -> Result<Rc<Self>, CubeError> {
155+
let evaluator_compiler_cell = self.query_tools.evaluator_compiler().clone();
156+
let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut();
157+
153158
let new_granularity_obj = GranularityHelper::make_granularity_obj(
154159
self.query_tools.cube_evaluator().clone(),
160+
&mut *evaluator_compiler,
155161
self.query_tools.timezone(),
156162
&self.dimension.name(),
157163
&self.dimension.cube_name(),
158164
new_granularity.clone(),
159165
)?;
160166
let date_range_tuple = if let Some(date_range) = &self.date_range {
161-
assert!(date_range.len() == 2);
167+
assert_eq!(date_range.len(), 2);
162168
Some((date_range[0].clone(), date_range[1].clone()))
163169
} else {
164170
None

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ impl QueryProperties {
169169
BaseTimeDimension::try_new_required(
170170
query_tools.clone(),
171171
evaluator,
172+
&mut evaluator_compiler,
172173
d.granularity.clone(),
173174
d.date_range.clone(),
174175
)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ impl<'a> DependenciesBuilder<'a> {
172172
let granularity = &call_deps[*child_ind].name;
173173
if let Some(granularity_obj) = GranularityHelper::make_granularity_obj(
174174
self.cube_evaluator.clone(),
175+
self.compiler,
175176
self.timezone.clone(),
176177
cube_name,
177178
&dep.name,

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/time_dimension.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ impl SqlNode for TimeDimensionNode {
4444
match node.as_ref() {
4545
MemberSymbol::TimeDimension(ev) => {
4646
let res = if let Some(granularity_obj) = ev.granularity_obj() {
47+
if let Some(calendar_sql) = granularity_obj.calendar_sql() {
48+
return calendar_sql.eval(
49+
visitor,
50+
node_processor.clone(),
51+
query_tools.clone(),
52+
templates,
53+
);
54+
}
55+
4756
let converted_tz = if self
4857
.dimensions_with_ignored_timezone
4958
.contains(&ev.full_name())

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1+
use std::rc::Rc;
12
use super::{GranularityHelper, QueryDateTime, SqlInterval};
23
use chrono_tz::Tz;
34
use cubenativeutils::CubeError;
45
use itertools::Itertools;
56
use std::str::FromStr;
7+
use crate::planner::sql_evaluator::SqlCall;
68

7-
#[derive(Clone, Debug)]
9+
#[derive(Clone)]
810
pub struct Granularity {
911
granularity: String,
1012
granularity_interval: String,
1113
granularity_offset: Option<String>,
1214
origin: QueryDateTime,
1315
is_predefined_granularity: bool,
1416
is_natural_aligned: bool,
17+
calendar_sql: Option<Rc<SqlCall>>,
1518
}
1619

1720
impl Granularity {
@@ -26,6 +29,7 @@ impl Granularity {
2629
origin,
2730
is_predefined_granularity: true,
2831
is_natural_aligned: true,
32+
calendar_sql: None,
2933
})
3034
}
3135
pub fn try_new_custom(
@@ -34,7 +38,21 @@ impl Granularity {
3438
origin: Option<String>,
3539
granularity_interval: String,
3640
granularity_offset: Option<String>,
41+
calendar_sql: Option<Rc<SqlCall>>,
3742
) -> Result<Self, CubeError> {
43+
// sql() is mutual exclusive with interval and offset/origin
44+
if calendar_sql.is_some() {
45+
return Ok(Self {
46+
granularity,
47+
granularity_interval,
48+
granularity_offset: None,
49+
origin: Self::default_origin(timezone)?,
50+
is_predefined_granularity: false,
51+
is_natural_aligned: false,
52+
calendar_sql,
53+
})
54+
}
55+
3856
let origin = if let Some(origin) = origin {
3957
QueryDateTime::from_date_str(timezone, &origin)?
4058
} else if let Some(offset) = &granularity_offset {
@@ -68,6 +86,7 @@ impl Granularity {
6886
origin,
6987
is_predefined_granularity: false,
7088
is_natural_aligned,
89+
calendar_sql,
7190
})
7291
}
7392

@@ -78,6 +97,10 @@ impl Granularity {
7897
pub fn granularity_offset(&self) -> &Option<String> {
7998
&self.granularity_offset
8099
}
100+
101+
pub fn calendar_sql(&self) -> &Option<Rc<SqlCall>> {
102+
&self.calendar_sql
103+
}
81104

82105
pub fn granularity(&self) -> &String {
83106
&self.granularity

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

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use itertools::Itertools;
88
use lazy_static::lazy_static;
99
use std::collections::HashMap;
1010
use std::rc::Rc;
11+
use crate::planner::sql_evaluator::Compiler;
1112

1213
pub struct GranularityHelper {}
1314

@@ -93,7 +94,7 @@ impl GranularityHelper {
9394
}
9495

9596
pub fn granularity_parents(granularity: &str) -> Result<&Vec<String>, CubeError> {
96-
if let Some(parents) = Self::standard_granularity_parents().get(granularity) {
97+
if let Some(parents) = Self::standard_granularity_hierarchy().get(granularity) {
9798
Ok(parents)
9899
} else {
99100
Err(CubeError::user(format!(
@@ -104,12 +105,12 @@ impl GranularityHelper {
104105
}
105106

106107
pub fn is_predefined_granularity(granularity: &str) -> bool {
107-
Self::standard_granularity_parents().contains_key(granularity)
108+
Self::standard_granularity_hierarchy().contains_key(granularity)
108109
}
109110

110-
pub fn standard_granularity_parents() -> &'static HashMap<String, Vec<String>> {
111+
pub fn standard_granularity_hierarchy() -> &'static HashMap<String, Vec<String>> {
111112
lazy_static! {
112-
static ref STANDARD_GRANULARITIES_PARENTS: HashMap<String, Vec<String>> = {
113+
static ref STANDARD_GRANULARITY_HIERARCHIES: HashMap<String, Vec<String>> = {
113114
let mut map = HashMap::new();
114115
map.insert(
115116
"year".to_string(),
@@ -179,7 +180,7 @@ impl GranularityHelper {
179180
map
180181
};
181182
}
182-
&STANDARD_GRANULARITIES_PARENTS
183+
&STANDARD_GRANULARITY_HIERARCHIES
183184
}
184185

185186
pub fn parse_date_time_in_tz(date: &str, timezone: &Tz) -> Result<DateTime<Tz>, CubeError> {
@@ -217,26 +218,32 @@ impl GranularityHelper {
217218

218219
pub fn make_granularity_obj(
219220
cube_evaluator: Rc<dyn CubeEvaluator>,
221+
compiler: &mut Compiler,
220222
timezone: Tz,
221223
cube_name: &String,
222224
name: &String,
223225
granularity: Option<String>,
224226
) -> Result<Option<Granularity>, CubeError> {
225227
let granularity_obj = if let Some(granularity) = &granularity {
226-
if !Self::is_predefined_granularity(&granularity) {
227-
let path = vec![
228-
cube_name.clone(),
229-
name.clone(),
230-
"granularities".to_string(),
231-
granularity.clone(),
232-
];
233-
let granularity_definition = cube_evaluator.resolve_granularity(path)?;
228+
let path = vec![
229+
cube_name.clone(),
230+
name.clone(),
231+
"granularities".to_string(),
232+
granularity.clone(),
233+
];
234+
let granularity_definition = cube_evaluator.resolve_granularity(path)?;
235+
let gran_eval_sql = if let Some(gran_sql) = granularity_definition.sql()? {
236+
Some(compiler.compile_sql_call(&cube_name, gran_sql)?)
237+
} else { None };
238+
239+
if gran_eval_sql.is_some() || !Self::is_predefined_granularity(&granularity) {
234240
Some(Granularity::try_new_custom(
235241
timezone.clone(),
236242
granularity.clone(),
237-
granularity_definition.origin,
238-
granularity_definition.interval,
239-
granularity_definition.offset,
243+
granularity_definition.static_data().origin.clone(),
244+
granularity_definition.static_data().interval.clone(),
245+
granularity_definition.static_data().offset.clone(),
246+
gran_eval_sql
240247
)?)
241248
} else {
242249
Some(Granularity::try_new_predefined(

0 commit comments

Comments
 (0)