Skip to content

Commit 916b926

Browse files
committed
patched measures
1 parent 866e024 commit 916b926

File tree

12 files changed

+366
-105
lines changed

12 files changed

+366
-105
lines changed
Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,62 @@
11
use super::member_sql::{MemberSql, NativeMemberSql};
2+
use super::struct_with_sql_member::{NativeStructWithSqlMember, StructWithSqlMember};
23
use cubenativeutils::wrappers::serializer::{
34
NativeDeserialize, NativeDeserializer, NativeSerialize,
45
};
6+
use cubenativeutils::wrappers::NativeArray;
57
use cubenativeutils::wrappers::{NativeContextHolder, NativeObjectHandle};
68
use cubenativeutils::CubeError;
79
use serde::{Deserialize, Serialize};
810
use std::any::Any;
911
use std::rc::Rc;
1012

13+
#[derive(Serialize, Deserialize, Debug, Clone)]
14+
pub struct ExpressionStructStatic {
15+
#[serde(rename = "type")]
16+
pub expression_type: String,
17+
#[serde(rename = "sourceMeasure")]
18+
pub source_measure: Option<String>,
19+
#[serde(rename = "replaceAggregationType")]
20+
pub replace_aggregation_type: Option<String>,
21+
}
22+
23+
#[nativebridge::native_bridge(ExpressionStructStatic)]
24+
pub trait ExpressionStruct {
25+
#[nbridge(field, optional, vec)]
26+
fn add_filters(&self) -> Result<Option<Vec<Rc<dyn StructWithSqlMember>>>, CubeError>;
27+
}
28+
29+
pub enum MemberExpressionExpressionDef {
30+
Sql(Rc<dyn MemberSql>),
31+
Struct(Rc<dyn ExpressionStruct>),
32+
}
33+
34+
impl<IT: InnerTypes> NativeDeserialize<IT> for MemberExpressionExpressionDef {
35+
fn from_native(native_object: NativeObjectHandle<IT>) -> Result<Self, CubeError> {
36+
match NativeMemberSql::from_native(native_object.clone()) {
37+
Ok(sql) => Ok(Self::Sql(Rc::new(sql))),
38+
Err(_) => match NativeExpressionStruct::from_native(native_object) {
39+
Ok(expr) => Ok(Self::Struct(Rc::new(expr))),
40+
Err(_) => Err(CubeError::user(format!(
41+
"Member sql or expression struct expected for member expression expression field"
42+
))),
43+
},
44+
}
45+
}
46+
}
47+
1148
#[derive(Serialize, Deserialize, Debug, Clone)]
1249
pub struct MemberExpressionDefinitionStatic {
1350
#[serde(rename = "expressionName")]
1451
pub expression_name: Option<String>,
52+
pub name: Option<String>,
1553
#[serde(rename = "cubeName")]
1654
pub cube_name: Option<String>,
1755
pub definition: Option<String>,
1856
}
1957

20-
#[nativebridge::native_bridge(MemberExpressionDefinitionStatic)]
58+
#[nativebridge::native_bridge(MemberExpressionDefinitionStatic, without_imports)]
2159
pub trait MemberExpressionDefinition {
2260
#[nbridge(field)]
23-
fn expression(&self) -> Result<Rc<dyn MemberSql>, CubeError>;
61+
fn expression(&self) -> Result<MemberExpressionExpressionDef, CubeError>;
2462
}

rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::query_tools::QueryTools;
22
use super::sql_evaluator::{MemberExpressionSymbol, MemberSymbol, SqlCall};
33
use super::{evaluate_with_context, BaseMember, BaseMemberHelper, VisitorContext};
44
use crate::cube_bridge::dimension_definition::DimensionDefinition;
5+
use crate::planner::sql_evaluator::MemberExpressionExpression;
56
use crate::planner::sql_templates::PlanSqlTemplates;
67
use cubenativeutils::CubeError;
78
use std::rc::Rc;
@@ -126,7 +127,7 @@ impl BaseDimension {
126127
let member_expression_symbol = MemberExpressionSymbol::try_new(
127128
cube_name.clone(),
128129
name.clone(),
129-
expression,
130+
MemberExpressionExpression::SqlCall(expression),
130131
member_expression_definition.clone(),
131132
)?;
132133
let full_name = member_expression_symbol.full_name();

rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs

Lines changed: 27 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use super::query_tools::QueryTools;
2-
use super::sql_evaluator::{MeasureTimeShifts, MemberExpressionSymbol, MemberSymbol, SqlCall};
2+
use super::sql_evaluator::{MeasureTimeShifts, MemberExpressionSymbol, MemberSymbol};
33
use super::{evaluate_with_context, BaseMember, BaseMemberHelper, VisitorContext};
4-
use crate::cube_bridge::measure_definition::{MeasureDefinition, RollingWindow};
4+
use crate::cube_bridge::measure_definition::RollingWindow;
5+
use crate::planner::sql_evaluator::MemberExpressionExpression;
56
use crate::planner::sql_templates::PlanSqlTemplates;
67
use cubenativeutils::CubeError;
78
use std::fmt::{Debug, Formatter};
@@ -11,7 +12,6 @@ pub struct BaseMeasure {
1112
measure: String,
1213
query_tools: Rc<QueryTools>,
1314
member_evaluator: Rc<MemberSymbol>,
14-
definition: Option<Rc<dyn MeasureDefinition>>,
1515
#[allow(dead_code)]
1616
member_expression_definition: Option<String>,
1717
cube_name: String,
@@ -84,7 +84,6 @@ impl BaseMeasure {
8484
measure: s.full_name(),
8585
query_tools: query_tools.clone(),
8686
member_evaluator: evaluation_node.clone(),
87-
definition: Some(s.definition().clone()),
8887
member_expression_definition: None,
8988
cube_name: s.cube_name().clone(),
9089
name: s.name().clone(),
@@ -101,7 +100,6 @@ impl BaseMeasure {
101100
measure: full_name,
102101
query_tools: query_tools.clone(),
103102
member_evaluator: evaluation_node,
104-
definition: None,
105103
cube_name,
106104
name,
107105
member_expression_definition,
@@ -127,7 +125,7 @@ impl BaseMeasure {
127125
}
128126

129127
pub fn try_new_from_expression(
130-
expression: Rc<SqlCall>,
128+
expression: MemberExpressionExpression,
131129
cube_name: String,
132130
name: String,
133131
member_expression_definition: Option<String>,
@@ -146,28 +144,19 @@ impl BaseMeasure {
146144
measure: full_name,
147145
query_tools,
148146
member_evaluator,
149-
definition: None,
150147
cube_name,
151148
name,
152149
member_expression_definition,
153150
default_alias,
154151
}))
155152
}
156153

157-
pub fn can_used_as_addictive_in_multplied(&self) -> Result<bool, CubeError> {
158-
let measure_type = self.measure_type();
159-
let res = if measure_type == "countDistinct" || measure_type == "countDistinctApprox" {
160-
true
161-
} else if measure_type == "count" {
162-
if let Some(definition) = &self.definition {
163-
!definition.has_sql()?
164-
} else {
165-
false
166-
}
154+
pub fn can_used_as_addictive_in_multplied(&self) -> bool {
155+
if let Ok(measure_symbol) = self.member_evaluator.as_measure() {
156+
measure_symbol.can_used_as_addictive_in_multplied()
167157
} else {
168158
false
169-
};
170-
Ok(res)
159+
}
171160
}
172161

173162
pub fn member_evaluator(&self) -> &Rc<MemberSymbol> {
@@ -182,29 +171,11 @@ impl BaseMeasure {
182171
&self.cube_name
183172
}
184173

185-
pub fn reduce_by(&self) -> Option<Vec<String>> {
186-
self.definition
187-
.as_ref()
188-
.and_then(|d| d.static_data().reduce_by_references.clone())
189-
}
190-
191-
pub fn add_group_by(&self) -> Option<Vec<String>> {
192-
self.definition
193-
.as_ref()
194-
.and_then(|d| d.static_data().add_group_by_references.clone())
195-
}
196-
197-
pub fn group_by(&self) -> Option<Vec<String>> {
198-
self.definition
199-
.as_ref()
200-
.and_then(|d| d.static_data().group_by_references.clone())
201-
}
202-
203-
//FIXME dublicate with symbol
204174
pub fn is_calculated(&self) -> bool {
205-
match self.measure_type() {
206-
"number" | "string" | "time" | "boolean" => true,
207-
_ => false,
175+
if let Ok(measure_symbol) = self.member_evaluator.as_measure() {
176+
measure_symbol.is_calculated()
177+
} else {
178+
true
208179
}
209180
}
210181

@@ -216,15 +187,19 @@ impl BaseMeasure {
216187
}
217188

218189
pub fn is_multi_stage(&self) -> bool {
219-
self.definition
220-
.as_ref()
221-
.is_some_and(|d| d.static_data().multi_stage.unwrap_or(false))
190+
if let Ok(measure_symbol) = self.member_evaluator.as_measure() {
191+
measure_symbol.is_multi_stage()
192+
} else {
193+
false
194+
}
222195
}
223196

224197
pub fn rolling_window(&self) -> Option<RollingWindow> {
225-
self.definition
226-
.as_ref()
227-
.and_then(|d| d.static_data().rolling_window.clone())
198+
if let Ok(measure_symbol) = self.member_evaluator.as_measure() {
199+
measure_symbol.rolling_window().clone()
200+
} else {
201+
None
202+
}
228203
}
229204

230205
pub fn is_rolling_window(&self) -> bool {
@@ -239,11 +214,12 @@ impl BaseMeasure {
239214
self.is_rolling_window() || self.is_running_total()
240215
}
241216

242-
//FIXME dublicate with symbol
243217
pub fn measure_type(&self) -> &str {
244-
self.definition
245-
.as_ref()
246-
.map_or("number", |d| &d.static_data().measure_type)
218+
if let Ok(measure_symbol) = self.member_evaluator.as_measure() {
219+
measure_symbol.measure_type()
220+
} else {
221+
"number"
222+
}
247223
}
248224

249225
pub fn is_multi_stage_ungroupped(&self) -> bool {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::planner::query_tools::QueryTools;
2-
use crate::planner::sql_evaluator::{MemberExpressionSymbol, MemberSymbol, SqlCall};
2+
use crate::planner::sql_evaluator::{MemberExpressionExpression, MemberExpressionSymbol, MemberSymbol, SqlCall};
33
use crate::planner::sql_templates::PlanSqlTemplates;
44
use crate::planner::{evaluate_with_context, VisitorContext};
55
use cubenativeutils::CubeError;
@@ -28,7 +28,7 @@ impl BaseSegment {
2828
query_tools: Rc<QueryTools>,
2929
) -> Result<Rc<Self>, CubeError> {
3030
let member_expression_symbol =
31-
MemberExpressionSymbol::try_new(cube_name.clone(), name.clone(), expression, None)?;
31+
MemberExpressionSymbol::try_new(cube_name.clone(), name.clone(), MemberExpressionExpression::SqlCall(expression), None)?;
3232
let full_name = full_name.unwrap_or(member_expression_symbol.full_name());
3333
let member_evaluator = Rc::new(MemberSymbol::MemberExpression(member_expression_symbol));
3434

rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::logical_plan::DimensionSubQuery;
33
use crate::plan::{FilterItem, QualifiedColumnName};
44
use crate::planner::query_tools::QueryTools;
55
use crate::planner::sql_evaluator::collectors::collect_sub_query_dimensions;
6+
use crate::planner::sql_evaluator::MemberExpressionExpression;
67
use crate::planner::QueryProperties;
78
use crate::planner::{BaseDimension, BaseMeasure, BaseMember};
89
use cubenativeutils::CubeError;
@@ -71,7 +72,7 @@ impl DimensionSubqueryPlanner {
7172
let primary_keys_dimensions = self.utils.primary_keys_dimensions(&cube_name)?;
7273
let expression = subquery_dimension.sql_call()?;
7374
let measure = BaseMeasure::try_new_from_expression(
74-
expression,
75+
MemberExpressionExpression::SqlCall(expression),
7576
cube_name.clone(),
7677
dim_name.clone(),
7778
None,

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

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use super::filter::compiler::FilterCompiler;
22
use super::filter::BaseSegment;
33
use super::query_tools::QueryTools;
4+
use crate::cube_bridge::member_expression::MemberExpressionExpressionDef;
5+
use crate::planner::sql_evaluator::MemberExpressionExpression;
46

57
use super::sql_evaluator::MemberSymbol;
68
use super::{BaseDimension, BaseMeasure, BaseMember, BaseMemberHelper, BaseTimeDimension};
@@ -111,8 +113,16 @@ impl QueryProperties {
111113
} else {
112114
"".to_string()
113115
};
114-
let expression_evaluator = evaluator_compiler
115-
.compile_sql_call(&cube_name, member_expression.expression()?)?;
116+
let expression_evaluator = match member_expression.expression()? {
117+
MemberExpressionExpressionDef::Sql(sql) => {
118+
evaluator_compiler.compile_sql_call(&cube_name, sql)?
119+
}
120+
MemberExpressionExpressionDef::Struct(_) => {
121+
return Err(CubeError::user(format!(
122+
"Expression struct not supported for dimension"
123+
)));
124+
}
125+
};
116126
BaseDimension::try_new_from_expression(
117127
expression_evaluator,
118128
cube_name,
@@ -165,13 +175,45 @@ impl QueryProperties {
165175
let name =
166176
if let Some(name) = &member_expression.static_data().expression_name {
167177
name.clone()
178+
} else if let Some(name) = &member_expression.static_data().name {
179+
format!("{}.{}", cube_name, name)
168180
} else {
169181
"".to_string()
170182
};
171-
let expression_evaluator = evaluator_compiler
172-
.compile_sql_call(&cube_name, member_expression.expression()?)?;
183+
let expression = match member_expression.expression()? {
184+
MemberExpressionExpressionDef::Sql(sql) => {
185+
MemberExpressionExpression::SqlCall(
186+
evaluator_compiler.compile_sql_call(&cube_name, sql)?,
187+
)
188+
}
189+
MemberExpressionExpressionDef::Struct(expr) => {
190+
if expr.static_data().expression_type != "PatchMeasure" {
191+
return Err(CubeError::user(format!("Only `PatchMeasure` type of memeber expression is supported")));
192+
}
193+
194+
if let Some(source_measure) = &expr.static_data().source_measure {
195+
196+
let new_measure_type = expr.static_data().replace_aggregation_type.clone();
197+
let mut filters_to_add = vec![];
198+
if let Some(add_filters) = expr.add_filters()? {
199+
for filter in add_filters.iter() {
200+
let node = evaluator_compiler.compile_sql_call(&cube_name, filter.sql()?)?;
201+
filters_to_add.push(node);
202+
}
203+
}
204+
let source_measure_compiled = evaluator_compiler.add_measure_evaluator(source_measure.clone())?;
205+
let patched_measure = source_measure_compiled.as_measure()?.new_patched(new_measure_type, filters_to_add)?;
206+
let patched_symbol = Rc::new(MemberSymbol::Measure(patched_measure));
207+
MemberExpressionExpression::PatchedSymbol(patched_symbol)
208+
209+
} else {
210+
return Err(CubeError::user(format!("Source measure is required for `PatchMeasure` type of memeber expression")));
211+
}
212+
213+
}
214+
};
173215
BaseMeasure::try_new_from_expression(
174-
expression_evaluator,
216+
expression,
175217
cube_name,
176218
name,
177219
member_expression.static_data().definition.clone(),
@@ -230,8 +272,16 @@ impl QueryProperties {
230272
} else {
231273
"".to_string()
232274
};
233-
let expression_evaluator = evaluator_compiler
234-
.compile_sql_call(&cube_name, member_expression.expression()?)?;
275+
let expression_evaluator = match member_expression.expression()? {
276+
MemberExpressionExpressionDef::Sql(sql) => {
277+
evaluator_compiler.compile_sql_call(&cube_name, sql)?
278+
}
279+
MemberExpressionExpressionDef::Struct(_) => {
280+
return Err(CubeError::user(format!(
281+
"Expression struct not supported for dimension"
282+
)));
283+
}
284+
};
235285
BaseSegment::try_new(
236286
expression_evaluator,
237287
cube_name,
@@ -811,7 +861,7 @@ impl QueryProperties {
811861
.rendered_as_multiplied_measures
812862
.insert(item.measure.full_name());
813863
}
814-
if item.multiplied && !item.measure.can_used_as_addictive_in_multplied()? {
864+
if item.multiplied && !item.measure.can_used_as_addictive_in_multplied() {
815865
result.multiplied_measures.push(item.measure.clone());
816866
} else {
817867
result.regular_measures.push(item.measure.clone());

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub use symbols::{
1717
CubeNameSymbol, CubeNameSymbolFactory, CubeTableSymbol, CubeTableSymbolFactory,
1818
DimensionCaseDefinition, DimensionCaseWhenItem, DimensionSymbol, DimensionSymbolFactory,
1919
DimensionTimeShift, DimenstionCaseLabel, MeasureSymbol, MeasureSymbolFactory,
20-
MeasureTimeShifts, MemberExpressionSymbol, MemberSymbol, SymbolFactory, TimeDimensionSymbol,
20+
MeasureTimeShifts, MemberExpressionExpression, MemberExpressionSymbol, MemberSymbol,
21+
SymbolFactory, TimeDimensionSymbol,
2122
};
2223
pub use visitor::TraversalVisitor;

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ impl SqlNode for TimeShiftSqlNode {
5555
input
5656
} else {
5757
let shift = interval.to_sql();
58-
let res = templates
59-
.base_tools()
60-
.add_timestamp_interval(input, shift)?;
58+
let res = templates.add_timestamp_interval(input, shift)?;
6159
format!("({})", res)
6260
}
6361
} else {

0 commit comments

Comments
 (0)