Skip to content

Commit b5572bd

Browse files
committed
in work
1 parent 03737c6 commit b5572bd

39 files changed

+1251
-262
lines changed

packages/cubejs-schema-compiler/test/integration/postgres/calc-groups.test.ts

Lines changed: 478 additions & 9 deletions
Large diffs are not rendered by default.

packages/cubejs-schema-compiler/test/unit/sql-parser.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,6 @@ describe('SqlParser', () => {
126126

127127
// Verify table alias extraction still works after grammar changes
128128
const extractedConditions = sqlParser.extractWhereConditions('t');
129-
expect(extractedConditions).toEqual(`t.status = 'active' AND t.created_at > '2024-01-01'`);
129+
expect(extractedConditions).toEqual('t.status = \'active\' AND t.created_at > \'2024-01-01\'');
130130
});
131131
});

rust/cubesqlplanner/cubesqlplanner/src/logical_plan/join.rs

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,16 @@ impl PrettyPrint for LogicalJoinItem {
3131

3232
#[derive(Clone, TypedBuilder)]
3333
pub struct LogicalJoin {
34-
root: Rc<Cube>,
34+
#[builder(default)]
35+
root: Option<Rc<Cube>>,
3536
#[builder(default)]
3637
joins: Vec<LogicalJoinItem>,
3738
#[builder(default)]
3839
dimension_subqueries: Vec<Rc<DimensionSubQuery>>,
3940
}
4041

4142
impl LogicalJoin {
42-
pub fn root(&self) -> &Rc<Cube> {
43+
pub fn root(&self) -> &Option<Rc<Cube>> {
4344
&self.root
4445
}
4546

@@ -68,6 +69,12 @@ impl LogicalNode for LogicalJoin {
6869
dimension_subqueries,
6970
} = LogicalJoinInputUnPacker::new(&self, &inputs)?;
7071

72+
let root = if let Some(r) = root {
73+
Some(r.clone().into_logical_node()?)
74+
} else {
75+
None
76+
};
77+
7178
let joins = self
7279
.joins()
7380
.iter()
@@ -81,12 +88,14 @@ impl LogicalNode for LogicalJoin {
8188
.collect::<Result<Vec<_>, _>>()?;
8289

8390
let result = Self::builder()
84-
.root(root.clone().into_logical_node()?)
91+
.root(root)
8592
.joins(joins)
86-
.dimension_subqueries(dimension_subqueries
87-
.iter()
88-
.map(|itm| itm.clone().into_logical_node())
89-
.collect::<Result<Vec<_>, _>>()?)
93+
.dimension_subqueries(
94+
dimension_subqueries
95+
.iter()
96+
.map(|itm| itm.clone().into_logical_node())
97+
.collect::<Result<Vec<_>, _>>()?,
98+
)
9099
.build();
91100

92101
Ok(Rc::new(result))
@@ -109,7 +118,9 @@ pub struct LogicalJoinInputPacker;
109118
impl LogicalJoinInputPacker {
110119
pub fn pack(join: &LogicalJoin) -> Vec<PlanNode> {
111120
let mut result = vec![];
112-
result.push(join.root().as_plan_node());
121+
if let Some(root) = join.root() {
122+
result.push(root.as_plan_node());
123+
}
113124
result.extend(join.joins().iter().map(|item| item.cube().as_plan_node()));
114125
result.extend(
115126
join.dimension_subqueries()
@@ -121,7 +132,7 @@ impl LogicalJoinInputPacker {
121132
}
122133

123134
pub struct LogicalJoinInputUnPacker<'a> {
124-
root: &'a PlanNode,
135+
root: Option<&'a PlanNode>,
125136
joins: &'a [PlanNode],
126137
dimension_subqueries: &'a [PlanNode],
127138
}
@@ -130,8 +141,14 @@ impl<'a> LogicalJoinInputUnPacker<'a> {
130141
pub fn new(join: &LogicalJoin, inputs: &'a Vec<PlanNode>) -> Result<Self, CubeError> {
131142
check_inputs_len(&inputs, Self::inputs_len(join), join.node_name())?;
132143

133-
let root = &inputs[0];
134-
let joins_start = 1;
144+
let mut joins_start = 0;
145+
let root = if join.root.is_some() {
146+
joins_start = 1;
147+
Some(&inputs[0])
148+
} else {
149+
None
150+
};
151+
135152
let joins_end = joins_start + join.joins().len();
136153
let joins = &inputs[joins_start..joins_end];
137154
let dimension_subqueries = &inputs[joins_end..];
@@ -150,22 +167,27 @@ impl<'a> LogicalJoinInputUnPacker<'a> {
150167

151168
impl PrettyPrint for LogicalJoin {
152169
fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) {
153-
result.println(&format!("Join: "), state);
154-
let state = state.new_level();
155-
let details_state = state.new_level();
156-
result.println(&format!("root: "), &state);
157-
self.root().pretty_print(result, &details_state);
158-
result.println(&format!("joins: "), &state);
159-
let state = state.new_level();
160-
for join in self.joins().iter() {
161-
join.pretty_print(result, &state);
162-
}
163-
if !self.dimension_subqueries().is_empty() {
164-
result.println("dimension_subqueries:", &state);
170+
if let Some(root) = self.root() {
171+
result.println(&format!("Join: "), state);
172+
173+
let state = state.new_level();
165174
let details_state = state.new_level();
166-
for subquery in self.dimension_subqueries().iter() {
167-
subquery.pretty_print(result, &details_state);
175+
result.println(&format!("root: "), &state);
176+
root.pretty_print(result, &details_state);
177+
result.println(&format!("joins: "), &state);
178+
let state = state.new_level();
179+
for join in self.joins().iter() {
180+
join.pretty_print(result, &state);
181+
}
182+
if !self.dimension_subqueries().is_empty() {
183+
result.println("dimension_subqueries:", &state);
184+
let details_state = state.new_level();
185+
for subquery in self.dimension_subqueries().iter() {
186+
subquery.pretty_print(result, &details_state);
187+
}
168188
}
189+
} else {
190+
result.println(&format!("Empty source"), state);
169191
}
170192
}
171193
}

rust/cubesqlplanner/cubesqlplanner/src/logical_plan/logical_node.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub enum PlanNode {
2929
MultiStageGetDateRange(Rc<MultiStageGetDateRange>),
3030
MultiStageLeafMeasure(Rc<MultiStageLeafMeasure>),
3131
MultiStageMeasureCalculation(Rc<MultiStageMeasureCalculation>),
32+
MultiStageDimensionCalculation(Rc<MultiStageDimensionCalculation>),
3233
MultiStageTimeSeries(Rc<MultiStageTimeSeries>),
3334
MultiStageRollingWindow(Rc<MultiStageRollingWindow>),
3435
LogicalMultiStageMember(Rc<LogicalMultiStageMember>),
@@ -51,6 +52,7 @@ macro_rules! match_plan_node {
5152
PlanNode::MultiStageGetDateRange($node) => $block,
5253
PlanNode::MultiStageLeafMeasure($node) => $block,
5354
PlanNode::MultiStageMeasureCalculation($node) => $block,
55+
PlanNode::MultiStageDimensionCalculation($node) => $block,
5456
PlanNode::MultiStageTimeSeries($node) => $block,
5557
PlanNode::MultiStageRollingWindow($node) => $block,
5658
PlanNode::LogicalMultiStageMember($node) => $block,
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use crate::logical_plan::*;
2+
use crate::planner::query_properties::OrderByItem;
3+
use crate::planner::sql_evaluator::collectors::has_multi_stage_members;
4+
use crate::planner::sql_evaluator::MemberSymbol;
5+
use cubenativeutils::CubeError;
6+
use itertools::Itertools;
7+
use std::collections::HashSet;
8+
use std::rc::Rc;
9+
use typed_builder::TypedBuilder;
10+
11+
#[derive(TypedBuilder)]
12+
pub struct MultiStageDimensionCalculation {
13+
schema: Rc<LogicalSchema>,
14+
multi_stage_dimension: Rc<MemberSymbol>,
15+
#[builder(default)]
16+
order_by: Vec<OrderByItem>,
17+
source: Rc<FullKeyAggregate>,
18+
}
19+
20+
impl MultiStageDimensionCalculation {
21+
pub fn schema(&self) -> &Rc<LogicalSchema> {
22+
&self.schema
23+
}
24+
25+
pub fn multi_stage_dimension(&self) -> &Rc<MemberSymbol> {
26+
&self.multi_stage_dimension
27+
}
28+
29+
pub fn order_by(&self) -> &Vec<OrderByItem> {
30+
&self.order_by
31+
}
32+
33+
pub fn source(&self) -> &Rc<FullKeyAggregate> {
34+
&self.source
35+
}
36+
37+
pub fn resolved_dimensions(&self) -> Result<Vec<String>, CubeError> {
38+
let mut result = vec![];
39+
for dim in self.schema.all_dimensions() {
40+
if has_multi_stage_members(dim, false)? {
41+
result.push(dim.clone().resolve_reference_chain().full_name());
42+
}
43+
}
44+
result.sort();
45+
Ok(result)
46+
}
47+
48+
pub fn join_dimensions(&self) -> Result<Vec<Rc<MemberSymbol>>, CubeError> {
49+
let mut result = vec![];
50+
for dim in self.schema.all_dimensions() {
51+
if !has_multi_stage_members(dim, false)? {
52+
result.push(dim.clone());
53+
}
54+
}
55+
Ok(result)
56+
}
57+
}
58+
59+
impl PrettyPrint for MultiStageDimensionCalculation {
60+
fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) {
61+
result.println(&format!("Dimension Calculation",), state);
62+
let state = state.new_level();
63+
let details_state = state.new_level();
64+
result.println("schema:", &state);
65+
self.schema().pretty_print(result, &details_state);
66+
if !self.order_by().is_empty() {
67+
result.println("order_by:", &state);
68+
for order_by in self.order_by().iter() {
69+
result.println(
70+
&format!(
71+
"{} {}",
72+
order_by.name(),
73+
if order_by.desc() { "desc" } else { "asc" }
74+
),
75+
&details_state,
76+
);
77+
}
78+
}
79+
result.println("source:", &state);
80+
self.source().pretty_print(result, &details_state);
81+
}
82+
}
83+
84+
impl LogicalNode for MultiStageDimensionCalculation {
85+
fn as_plan_node(self: &Rc<Self>) -> PlanNode {
86+
PlanNode::MultiStageDimensionCalculation(self.clone())
87+
}
88+
89+
fn inputs(&self) -> Vec<PlanNode> {
90+
vec![self.source().as_plan_node()]
91+
}
92+
93+
fn with_inputs(self: Rc<Self>, inputs: Vec<PlanNode>) -> Result<Rc<Self>, CubeError> {
94+
check_inputs_len(&inputs, 1, self.node_name())?;
95+
let source = &inputs[0];
96+
97+
Ok(Rc::new(
98+
Self::builder()
99+
.schema(self.schema().clone())
100+
.order_by(self.order_by().clone())
101+
.multi_stage_dimension(self.multi_stage_dimension.clone())
102+
.source(source.clone().into_logical_node()?)
103+
.build(),
104+
))
105+
}
106+
107+
fn node_name(&self) -> &'static str {
108+
"MultiStageDimensionCalculation"
109+
}
110+
111+
fn try_from_plan_node(plan_node: PlanNode) -> Result<Rc<Self>, CubeError> {
112+
if let PlanNode::MultiStageDimensionCalculation(item) = plan_node {
113+
Ok(item)
114+
} else {
115+
Err(cast_error(&plan_node, "MultiStageMeasureCalculation"))
116+
}
117+
}
118+
}

rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/member.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::rc::Rc;
55
pub enum MultiStageMemberLogicalType {
66
LeafMeasure(Rc<MultiStageLeafMeasure>),
77
MeasureCalculation(Rc<MultiStageMeasureCalculation>),
8+
DimensionCalculation(Rc<MultiStageDimensionCalculation>),
89
GetDateRange(Rc<MultiStageGetDateRange>),
910
TimeSeries(Rc<MultiStageTimeSeries>),
1011
RollingWindow(Rc<MultiStageRollingWindow>),
@@ -15,6 +16,7 @@ impl MultiStageMemberLogicalType {
1516
match self {
1617
Self::LeafMeasure(item) => item.as_plan_node(),
1718
Self::MeasureCalculation(item) => item.as_plan_node(),
19+
Self::DimensionCalculation(item) => item.as_plan_node(),
1820
Self::GetDateRange(item) => item.as_plan_node(),
1921
Self::TimeSeries(item) => item.as_plan_node(),
2022
Self::RollingWindow(item) => item.as_plan_node(),
@@ -25,6 +27,9 @@ impl MultiStageMemberLogicalType {
2527
Ok(match self {
2628
Self::LeafMeasure(_) => Self::LeafMeasure(plan_node.into_logical_node()?),
2729
Self::MeasureCalculation(_) => Self::MeasureCalculation(plan_node.into_logical_node()?),
30+
Self::DimensionCalculation(_) => {
31+
Self::DimensionCalculation(plan_node.into_logical_node()?)
32+
}
2833
Self::GetDateRange(_) => Self::GetDateRange(plan_node.into_logical_node()?),
2934
Self::TimeSeries(_) => Self::TimeSeries(plan_node.into_logical_node()?),
3035
Self::RollingWindow(_) => Self::RollingWindow(plan_node.into_logical_node()?),
@@ -37,6 +42,7 @@ impl PrettyPrint for MultiStageMemberLogicalType {
3742
match self {
3843
Self::LeafMeasure(measure) => measure.pretty_print(result, state),
3944
Self::MeasureCalculation(calculation) => calculation.pretty_print(result, state),
45+
Self::DimensionCalculation(calculation) => calculation.pretty_print(result, state),
4046
Self::GetDateRange(get_date_range) => get_date_range.pretty_print(result, state),
4147
Self::TimeSeries(time_series) => time_series.pretty_print(result, state),
4248
Self::RollingWindow(rolling_window) => rolling_window.pretty_print(result, state),

rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
mod calculation;
22
mod common;
3+
mod dimension;
34
mod get_date_range;
45
mod leaf_measure;
56
mod member;
67
mod rolling_window;
78
mod time_series;
89

910
pub use calculation::*;
11+
pub use dimension::*;
1012
pub use get_date_range::*;
1113
pub use leaf_measure::*;
1214
pub use member::*;

rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/dimension_matcher.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ impl<'a> DimensionMatcher<'a> {
270270
filter.member_evaluator().clone()
271271
};
272272
let add_to_matched_dimension = add_to_matched_dimension && filter.is_single_value_equal();
273-
self.try_match_symbol(&symbol, add_to_matched_dimension)
273+
let res = self.try_match_symbol(&symbol, add_to_matched_dimension)?;
274+
Ok(res)
274275
}
275276
}

0 commit comments

Comments
 (0)