Skip to content

Commit d787e45

Browse files
committed
member expressions fixes
1 parent 44099a1 commit d787e45

File tree

14 files changed

+168
-98
lines changed

14 files changed

+168
-98
lines changed

packages/cubejs-schema-compiler/src/adapter/BaseQuery.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,7 @@ export class BaseQuery {
806806
exportAnnotatedSql: exportAnnotatedSql === true,
807807
preAggregationQuery: this.options.preAggregationQuery,
808808
totalQuery: this.options.totalQuery,
809+
joinHints: this.options.joinHints,
809810
};
810811

811812
const buildResult = nativeBuildSqlAndParams(queryParams);

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::join_graph::{JoinGraph, NativeJoinGraph};
2+
use super::join_hints::JoinHintItem;
23
use super::options_member::OptionsMember;
34
use crate::cube_bridge::base_tools::{BaseTools, NativeBaseTools};
45
use crate::cube_bridge::evaluator::{CubeEvaluator, NativeCubeEvaluator};
@@ -81,4 +82,6 @@ pub trait BaseQueryOptions {
8182
fn base_tools(&self) -> Result<Rc<dyn BaseTools>, CubeError>;
8283
#[nbridge(field)]
8384
fn join_graph(&self) -> Result<Rc<dyn JoinGraph>, CubeError>;
85+
#[nbridge(field, optional, vec)]
86+
fn join_hints(&self) -> Result<Option<Vec<JoinHintItem>>, CubeError>;
8487
}
Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
1-
use serde::{Deserialize, Serialize};
1+
use cubenativeutils::wrappers::inner_types::InnerTypes;
2+
use cubenativeutils::wrappers::serializer::NativeDeserialize;
3+
use cubenativeutils::wrappers::NativeObjectHandle;
4+
use cubenativeutils::CubeError;
5+
use serde::Serialize;
26

3-
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
7+
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
48
pub enum JoinHintItem {
59
Single(String),
610
Vector(Vec<String>),
711
}
12+
13+
impl<IT: InnerTypes> NativeDeserialize<IT> for JoinHintItem {
14+
fn from_native(native_object: NativeObjectHandle<IT>) -> Result<Self, CubeError> {
15+
match Vec::<String>::from_native(native_object.clone()) {
16+
Ok(value) => Ok(Self::Vector(value)),
17+
Err(_) => match String::from_native(native_object) {
18+
Ok(value) => Ok(Self::Single(value)),
19+
Err(_) => Err(CubeError::user(format!(
20+
"Join hint item expected to be string or vector of strings"
21+
))),
22+
},
23+
}
24+
}
25+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ impl DimensionSubqueryPlanner {
108108
false,
109109
false,
110110
false,
111+
Rc::new(vec![]),
111112
)?;
112113
let query_planner = QueryPlanner::new(sub_query_properties, self.query_tools.clone());
113114
let sub_query = query_planner.plan()?;

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::rc::Rc;
1616

1717
pub struct MultiStageMemberQueryPlanner {
1818
query_tools: Rc<QueryTools>,
19-
_query_properties: Rc<QueryProperties>,
19+
query_properties: Rc<QueryProperties>,
2020
description: Rc<MultiStageQueryDescription>,
2121
}
2222

@@ -28,7 +28,7 @@ impl MultiStageMemberQueryPlanner {
2828
) -> Self {
2929
Self {
3030
query_tools,
31-
_query_properties: query_properties,
31+
query_properties,
3232
description,
3333
}
3434
}
@@ -73,6 +73,7 @@ impl MultiStageMemberQueryPlanner {
7373
true,
7474
false,
7575
false,
76+
Rc::new(vec![]),
7677
)?;
7778

7879
let simple_query_planer =
@@ -266,6 +267,7 @@ impl MultiStageMemberQueryPlanner {
266267
self.description.member().is_ungrupped(),
267268
false,
268269
false,
270+
self.query_properties.query_join_hints().clone(),
269271
)?;
270272

271273
let query_planner =

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ impl MultipliedMeasuresQueryPlanner {
6767
.into_iter()
6868
.into_group_map_by(|m| m.cube_name().clone())
6969
{
70+
let measures = measures
71+
.into_iter()
72+
.map(|m| m.measure().clone())
73+
.collect_vec();
7074
let join_multi_fact_groups = self
7175
.query_properties
7276
.compute_join_multi_fact_groups_with_measures(&measures)?;
@@ -91,7 +95,12 @@ impl MultipliedMeasuresQueryPlanner {
9195
let all_measures = full_key_aggregate_measures
9296
.regular_measures
9397
.iter()
94-
.chain(full_key_aggregate_measures.multiplied_measures.iter())
98+
.chain(
99+
full_key_aggregate_measures
100+
.multiplied_measures
101+
.iter()
102+
.map(|m| m.measure()),
103+
)
95104
.map(|m| m.member_evaluator().clone())
96105
.collect_vec();
97106
let schema = Rc::new(LogicalSchema {

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

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::filter::compiler::FilterCompiler;
22
use super::filter::BaseSegment;
33
use super::query_tools::QueryTools;
4+
use crate::cube_bridge::join_hints::JoinHintItem;
45
use crate::cube_bridge::member_expression::MemberExpressionExpressionDef;
56
use crate::planner::sql_evaluator::MemberExpressionExpression;
67

@@ -45,9 +46,29 @@ impl OrderByItem {
4546
}
4647
}
4748

49+
#[derive(Debug, Clone)]
50+
pub struct MultipliedMeasure {
51+
measure: Rc<BaseMeasure>,
52+
cube_name: String, //May differ from cube_name of the measure for a member_expression that refers to a dimension.
53+
}
54+
55+
impl MultipliedMeasure {
56+
pub fn new(measure: Rc<BaseMeasure>, cube_name: String) -> Rc<Self> {
57+
Rc::new(Self { measure, cube_name })
58+
}
59+
60+
pub fn measure(&self) -> &Rc<BaseMeasure> {
61+
&self.measure
62+
}
63+
64+
pub fn cube_name(&self) -> &String {
65+
&self.cube_name
66+
}
67+
}
68+
4869
#[derive(Default, Clone, Debug)]
4970
pub struct FullKeyAggregateMeasures {
50-
pub multiplied_measures: Vec<Rc<BaseMeasure>>,
71+
pub multiplied_measures: Vec<Rc<MultipliedMeasure>>,
5172
pub regular_measures: Vec<Rc<BaseMeasure>>,
5273
pub multi_stage_measures: Vec<Rc<BaseMeasure>>,
5374
pub rendered_as_multiplied_measures: HashSet<String>,
@@ -81,6 +102,7 @@ pub struct QueryProperties {
81102
multi_fact_join_groups: Vec<(Rc<dyn JoinDefinition>, Vec<Rc<BaseMeasure>>)>,
82103
pre_aggregation_query: bool,
83104
total_query: bool,
105+
query_join_hints: Rc<Vec<JoinHintItem>>,
84106
}
85107

86108
impl QueryProperties {
@@ -350,7 +372,10 @@ impl QueryProperties {
350372
};
351373
let ungrouped = options.static_data().ungrouped.unwrap_or(false);
352374

375+
let query_join_hints = Rc::new(options.join_hints()?.unwrap_or_default());
376+
353377
let multi_fact_join_groups = Self::compute_join_multi_fact_groups(
378+
query_join_hints.clone(),
354379
query_tools.clone(),
355380
&measures,
356381
&dimensions,
@@ -381,6 +406,7 @@ impl QueryProperties {
381406
multi_fact_join_groups,
382407
pre_aggregation_query,
383408
total_query,
409+
query_join_hints,
384410
}))
385411
}
386412

@@ -400,6 +426,7 @@ impl QueryProperties {
400426
ungrouped: bool,
401427
pre_aggregation_query: bool,
402428
total_query: bool,
429+
query_join_hints: Rc<Vec<JoinHintItem>>,
403430
) -> Result<Rc<Self>, CubeError> {
404431
let order_by = if order_by.is_empty() {
405432
Self::default_order(&dimensions, &time_dimensions, &measures)
@@ -408,6 +435,7 @@ impl QueryProperties {
408435
};
409436

410437
let multi_fact_join_groups = Self::compute_join_multi_fact_groups(
438+
query_join_hints.clone(),
411439
query_tools.clone(),
412440
&measures,
413441
&dimensions,
@@ -435,6 +463,7 @@ impl QueryProperties {
435463
multi_fact_join_groups,
436464
pre_aggregation_query,
437465
total_query,
466+
query_join_hints,
438467
}))
439468
}
440469

@@ -443,6 +472,7 @@ impl QueryProperties {
443472
measures: &Vec<Rc<BaseMeasure>>,
444473
) -> Result<Vec<(Rc<dyn JoinDefinition>, Vec<Rc<BaseMeasure>>)>, CubeError> {
445474
Self::compute_join_multi_fact_groups(
475+
self.query_join_hints.clone(),
446476
self.query_tools.clone(),
447477
measures,
448478
&self.dimensions,
@@ -459,6 +489,7 @@ impl QueryProperties {
459489
}
460490

461491
pub fn compute_join_multi_fact_groups(
492+
query_join_hints: Rc<Vec<JoinHintItem>>,
462493
query_tools: Rc<QueryTools>,
463494
measures: &Vec<Rc<BaseMeasure>>,
464495
dimensions: &Vec<Rc<BaseDimension>>,
@@ -487,7 +518,7 @@ impl QueryProperties {
487518
.cached_data_mut()
488519
.join_hints_for_filter_item_vec(&measures_filters)?;
489520

490-
let mut dimension_and_filter_join_hints_concat = Vec::new();
521+
let mut dimension_and_filter_join_hints_concat = vec![query_join_hints];
491522

492523
dimension_and_filter_join_hints_concat.extend(dimensions_join_hints.into_iter());
493524
dimension_and_filter_join_hints_concat.extend(time_dimensions_join_hints.into_iter());
@@ -608,6 +639,10 @@ impl QueryProperties {
608639
self.row_limit
609640
}
610641

642+
pub fn query_join_hints(&self) -> &Rc<Vec<JoinHintItem>> {
643+
&self.query_join_hints
644+
}
645+
611646
pub fn offset(&self) -> Option<usize> {
612647
self.offset
613648
}
@@ -862,7 +897,9 @@ impl QueryProperties {
862897
.insert(item.measure.full_name());
863898
}
864899
if item.multiplied && !item.measure.can_used_as_addictive_in_multplied() {
865-
result.multiplied_measures.push(item.measure.clone());
900+
result
901+
.multiplied_measures
902+
.push(MultipliedMeasure::new(item.measure.clone(), item.cube_name));
866903
} else {
867904
result.regular_measures.push(item.measure.clone());
868905
}

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl TraversalVisitor for CubeNamesCollector {
2929
) -> Result<Option<Self::State>, CubeError> {
3030
match node.as_ref() {
3131
MemberSymbol::Dimension(e) => {
32-
if e.owned_by_cube() {
32+
if !e.is_view() {
3333
if !path.is_empty() {
3434
for p in path {
3535
self.names.insert(p.clone());
@@ -46,7 +46,7 @@ impl TraversalVisitor for CubeNamesCollector {
4646
return self.on_node_traverse(e.base_symbol(), path, &())
4747
}
4848
MemberSymbol::Measure(e) => {
49-
if e.owned_by_cube() {
49+
if !e.is_view() {
5050
if !path.is_empty() {
5151
for p in path {
5252
self.names.insert(p.clone());

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl TraversalVisitor for JoinHintsCollector {
2929
) -> Result<Option<Self::State>, CubeError> {
3030
match node.as_ref() {
3131
MemberSymbol::Dimension(e) => {
32-
if e.owned_by_cube() {
32+
if !e.is_view() {
3333
if !path.is_empty() {
3434
if path.len() == 1 {
3535
self.hints.push(JoinHintItem::Single(path[0].clone()))
@@ -48,7 +48,7 @@ impl TraversalVisitor for JoinHintsCollector {
4848
return self.on_node_traverse(e.base_symbol(), path, &())
4949
}
5050
MemberSymbol::Measure(e) => {
51-
if e.owned_by_cube() {
51+
if !e.is_view() {
5252
if !path.is_empty() {
5353
if path.len() == 1 {
5454
self.hints.push(JoinHintItem::Single(path[0].clone()))

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
use super::member_childs;
12
use crate::cube_bridge::join_definition::JoinDefinition;
23
use crate::planner::query_tools::QueryTools;
34
use crate::planner::sql_evaluator::{MemberSymbol, TraversalVisitor};
45
use crate::planner::BaseMeasure;
56
use cubenativeutils::CubeError;
7+
use itertools::Itertools;
68
use std::collections::HashSet;
79
use std::rc::Rc;
810

911
struct CompositeMeasuresCollector {
1012
composite_measures: HashSet<String>,
1113
}
1214

15+
#[derive(Clone)]
1316
struct CompositeMeasureCollectorState {
1417
pub parent_measure: Option<Rc<MemberSymbol>>,
1518
}
@@ -52,15 +55,18 @@ impl TraversalVisitor for CompositeMeasuresCollector {
5255
Some(new_state)
5356
}
5457
MemberSymbol::Dimension(_) => None,
58+
MemberSymbol::MemberExpression(_) => Some(state.clone()),
5559
_ => None,
5660
};
5761
Ok(res)
5862
}
5963
}
6064

65+
#[derive(Debug)]
6166
pub struct MeasureResult {
6267
pub multiplied: bool,
6368
pub measure: Rc<BaseMeasure>,
69+
pub cube_name: String,
6470
}
6571

6672
pub struct MultipliedMeasuresCollector {
@@ -113,6 +119,7 @@ impl TraversalVisitor for MultipliedMeasuresCollector {
113119
multiplied,
114120
measure: BaseMeasure::try_new(node.clone(), self.query_tools.clone())?
115121
.unwrap(),
122+
cube_name: node.cube_name(),
116123
})
117124
}
118125

@@ -138,7 +145,47 @@ pub fn collect_multiplied_measures(
138145
let mut composite_collector = CompositeMeasuresCollector::new();
139146
composite_collector.apply(node, &CompositeMeasureCollectorState::new(None))?;
140147
let composite_measures = composite_collector.extract_result();
141-
let mut visitor = MultipliedMeasuresCollector::new(query_tools, composite_measures, join);
148+
let mut visitor =
149+
MultipliedMeasuresCollector::new(query_tools.clone(), composite_measures, join.clone());
142150
visitor.apply(node, &())?;
143-
Ok(visitor.extract_result())
151+
let result = visitor.extract_result();
152+
if result.is_empty() && node.as_member_expression().is_ok() {
153+
let childs = member_childs(node, true)?;
154+
let cube_names = childs
155+
.into_iter()
156+
.map(|child| child.cube_name())
157+
.collect_vec();
158+
let result = if cube_names.is_empty() {
159+
let measure = BaseMeasure::try_new(node.clone(), query_tools.clone())?.unwrap();
160+
vec![MeasureResult {
161+
cube_name: measure.cube_name().clone(),
162+
measure,
163+
multiplied: false,
164+
}]
165+
} else if cube_names.len() == 1 {
166+
let cube_name = cube_names[0].clone();
167+
let multiplied = join
168+
.static_data()
169+
.multiplication_factor
170+
.get(&cube_name)
171+
.unwrap_or(&false)
172+
.clone();
173+
let measure = BaseMeasure::try_new(node.clone(), query_tools.clone())?.unwrap();
174+
175+
vec![MeasureResult {
176+
measure,
177+
cube_name,
178+
multiplied,
179+
}]
180+
} else {
181+
return Err(CubeError::user(format!(
182+
"Expected single cube for dimension-only measure {}, got {:?}",
183+
node.full_name(),
184+
cube_names
185+
)));
186+
};
187+
Ok(result)
188+
} else {
189+
Ok(result)
190+
}
144191
}

0 commit comments

Comments
 (0)