Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use cubenativeutils::wrappers::NativeObjectHandle;
use cubenativeutils::CubeError;
use serde::Serialize;

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Ord, PartialOrd)]
pub enum JoinHintItem {
Single(String),
Vector(Vec<String>),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::logical_plan::PreAggregationJoin;
use crate::logical_plan::PreAggregationJoinItem;
use crate::logical_plan::PreAggregationTable;
use crate::logical_plan::PreAggregationUnion;
use crate::planner::join_hints::JoinHints;
use crate::planner::planners::JoinPlanner;
use crate::planner::planners::ResolvedJoinItem;
use crate::planner::query_tools::QueryTools;
Expand Down Expand Up @@ -342,10 +343,12 @@ impl PreAggregationsCompiler {
.cloned()
.chain(dimensions.iter().cloned())
.collect_vec();
let pre_aggr_join_hints = collect_cube_names_from_symbols(&all_symbols)?
.into_iter()
.map(|v| JoinHintItem::Single(v))
.collect_vec();
let pre_aggr_join_hints = JoinHints::from_items(
collect_cube_names_from_symbols(&all_symbols)?
.into_iter()
.map(|v| JoinHintItem::Single(v))
.collect_vec(),
);

let join_planner = JoinPlanner::new(self.query_tools.clone());
let pre_aggrs_for_join = rollups
Expand All @@ -364,10 +367,12 @@ impl PreAggregationsCompiler {
.cloned()
.chain(join_pre_aggr.dimensions.iter().cloned())
.collect_vec();
let join_pre_aggr_join_hints = collect_cube_names_from_symbols(&all_symbols)?
.into_iter()
.map(|v| JoinHintItem::Single(v))
.collect_vec();
let join_pre_aggr_join_hints = JoinHints::from_items(
collect_cube_names_from_symbols(&all_symbols)?
.into_iter()
.map(|v| JoinHintItem::Single(v))
.collect_vec(),
);
existing_joins.append(
&mut join_planner.resolve_join_members_by_hints(&join_pre_aggr_join_hints)?,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,7 @@ impl PreAggregation {

for dim in self.dimensions().iter() {
let alias = dim.alias();
res.insert(
dim.full_name(),
QualifiedColumnName::new(None, alias.clone()),
);
res.insert(dim.full_name(), QualifiedColumnName::new(None, alias));
}
for dim in self.time_dimensions().iter() {
let (base_symbol, granularity) = if let Ok(td) = dim.as_time_dimension() {
Expand All @@ -117,7 +114,7 @@ impl PreAggregation {
let alias = format!("{}{}", base_symbol.alias(), suffix);
res.insert(
base_symbol.full_name(),
QualifiedColumnName::new(None, alias.clone()),
QualifiedColumnName::new(None, alias),
);
}

Expand All @@ -130,10 +127,7 @@ impl PreAggregation {
for item in join.items.iter() {
for member in item.from_members.iter().chain(item.to_members.iter()) {
let alias = member.alias();
res.insert(
member.full_name(),
QualifiedColumnName::new(None, alias.clone()),
);
res.insert(member.full_name(), QualifiedColumnName::new(None, alias));
}
}
}
Expand All @@ -145,10 +139,7 @@ impl PreAggregation {
.iter()
.map(|measure| {
let alias = measure.alias();
(
measure.full_name(),
QualifiedColumnName::new(None, alias.clone()),
)
(measure.full_name(), QualifiedColumnName::new(None, alias))
})
.collect()
}
Expand Down
5 changes: 1 addition & 4 deletions rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@ impl BaseCube {
context: Rc<VisitorContext>,
templates: &PlanSqlTemplates,
) -> Result<String, CubeError> {
let cube_ref = CubeRef::Table {
symbol: self.cube_table_symbol.clone(),
path: vec![],
};
let cube_ref = CubeRef::Table(self.cube_table_symbol.clone());
let visitor = context.make_visitor(context.query_tools());
let node_processor = context.node_processor();
visitor.evaluate_cube_ref(&cube_ref, node_processor, templates)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::planner::query_tools::QueryTools;
use crate::planner::sql_evaluator::{
MemberExpressionExpression, MemberExpressionSymbol, MemberSymbol, SqlCall,
CubeTableSymbol, MemberExpressionExpression, MemberExpressionSymbol, MemberSymbol, SqlCall,
};
use crate::planner::sql_templates::PlanSqlTemplates;
use crate::planner::{evaluate_with_context, VisitorContext};
Expand All @@ -24,18 +23,18 @@ impl PartialEq for BaseSegment {
impl BaseSegment {
pub fn try_new(
expression: Rc<SqlCall>,
cube_name: String,
cube_symbol: Rc<CubeTableSymbol>,
name: String,
full_name: Option<String>,
query_tools: Rc<QueryTools>,
) -> Result<Rc<Self>, CubeError> {
let cube_name = cube_symbol.cube_name().clone();
let member_expression_symbol = MemberExpressionSymbol::try_new(
cube_name.clone(),
cube_symbol,
name.clone(),
MemberExpressionExpression::SqlCall(expression),
None,
None,
query_tools.base_tools().clone(),
vec![cube_name.clone()],
)?;
let full_name = full_name.unwrap_or(member_expression_symbol.full_name());
let member_evaluator = MemberSymbol::new_member_expression(member_expression_symbol);
Expand Down
149 changes: 149 additions & 0 deletions rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use crate::cube_bridge::join_hints::JoinHintItem;

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct JoinHints {
items: Vec<JoinHintItem>,
}

impl JoinHints {
pub fn new() -> Self {
Self { items: Vec::new() }
}

pub fn from_items(mut items: Vec<JoinHintItem>) -> Self {
items.sort();
items.dedup();
Self { items }
}

pub fn insert(&mut self, item: JoinHintItem) {
match self.items.binary_search(&item) {
Ok(_) => {}
Err(pos) => self.items.insert(pos, item),
}
}

pub fn extend(&mut self, other: &JoinHints) {
for item in other.items.iter() {
self.insert(item.clone());
}
}

pub fn is_empty(&self) -> bool {
self.items.is_empty()
}

pub fn len(&self) -> usize {
self.items.len()
}

pub fn items(&self) -> &[JoinHintItem] {
&self.items
}

pub fn iter(&self) -> std::slice::Iter<'_, JoinHintItem> {
self.items.iter()
}

pub fn into_items(self) -> Vec<JoinHintItem> {
self.items
}
}

impl IntoIterator for JoinHints {
type Item = JoinHintItem;
type IntoIter = std::vec::IntoIter<JoinHintItem>;

fn into_iter(self) -> Self::IntoIter {
self.items.into_iter()
}
}

impl<'a> IntoIterator for &'a JoinHints {
type Item = &'a JoinHintItem;
type IntoIter = std::slice::Iter<'a, JoinHintItem>;

fn into_iter(self) -> Self::IntoIter {
self.items.iter()
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

fn s(name: &str) -> JoinHintItem {
JoinHintItem::Single(name.to_string())
}

fn v(names: &[&str]) -> JoinHintItem {
JoinHintItem::Vector(names.iter().map(|n| n.to_string()).collect())
}

fn hash_of(h: &JoinHints) -> u64 {
let mut hasher = DefaultHasher::new();
h.hash(&mut hasher);
hasher.finish()
}

#[test]
fn test_from_items_normalizes_and_deduplicates() {
let hints = JoinHints::from_items(vec![
s("orders"),
v(&["users", "orders"]),
s("orders"),
s("abc"),
]);

assert_eq!(hints.len(), 3);
// sorted: Single comes before Vector for same content, and Singles are alphabetical
assert_eq!(hints.items()[0], s("abc"));
assert_eq!(hints.items()[1], s("orders"));
assert_eq!(hints.items()[2], v(&["users", "orders"]));

// Different insertion order → same result
let hints2 = JoinHints::from_items(vec![s("abc"), v(&["users", "orders"]), s("orders")]);
assert_eq!(hints, hints2);
assert_eq!(hash_of(&hints), hash_of(&hints2));
}

#[test]
fn test_insert_and_extend_preserve_invariant() {
let mut a = JoinHints::new();
assert!(a.is_empty());

a.insert(s("orders"));
a.insert(s("abc"));
a.insert(s("orders")); // duplicate
assert_eq!(a.len(), 2);
assert_eq!(a.items()[0], s("abc"));
assert_eq!(a.items()[1], s("orders"));

let b = JoinHints::from_items(vec![s("orders"), v(&["a", "b"]), s("zzz")]);
a.extend(&b);
assert_eq!(a.len(), 4);
// abc, orders, zzz (Singles sorted), then Vector
assert_eq!(a.items()[0], s("abc"));
assert_eq!(a.items()[1], s("orders"));
assert_eq!(a.items()[2], s("zzz"));
assert_eq!(a.items()[3], v(&["a", "b"]));
}

#[test]
fn test_into_items_and_into_iter() {
let hints = JoinHints::from_items(vec![s("b"), s("a"), v(&["x", "y"])]);
let cloned = hints.clone();

// into_iter
let collected: Vec<_> = cloned.into_iter().collect();
assert_eq!(collected.len(), 3);

// into_items
let items = hints.into_items();
assert_eq!(items.len(), 3);
assert_eq!(items[0], s("a"));
assert_eq!(items[1], s("b"));
}
}
2 changes: 2 additions & 0 deletions rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod base_cube;
pub mod base_join_condition;
pub mod base_query;
pub mod filter;
pub mod join_hints;
pub mod time_dimension;

pub mod params_allocator;
Expand All @@ -17,6 +18,7 @@ pub mod visitor_context;
pub use base_cube::BaseCube;
pub use base_join_condition::{BaseJoinCondition, SqlJoinCondition};
pub use base_query::BaseQuery;
pub use join_hints::JoinHints;
pub use params_allocator::ParamsAllocator;
pub use query_properties::{FullKeyAggregateMeasures, OrderByItem, QueryProperties};
pub use time_dimension::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ impl CommonUtils {
let evaluator_compiler_cell = self.query_tools.evaluator_compiler().clone();
let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut();

let evaluator = evaluator_compiler.add_cube_table_evaluator(cube_path.to_string())?;
let evaluator =
evaluator_compiler.add_cube_table_evaluator(cube_path.to_string(), vec![])?;
BaseCube::try_new(cube_path.to_string(), self.query_tools.clone(), evaluator)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{CommonUtils, QueryPlanner};
use crate::logical_plan::{pretty_print_rc, DimensionSubQuery};
use crate::plan::{FilterItem, QualifiedColumnName};
use crate::planner::join_hints::JoinHints;
use crate::planner::query_tools::QueryTools;
use crate::planner::sql_evaluator::collectors::collect_sub_query_dimensions;
use crate::planner::sql_evaluator::{
Expand Down Expand Up @@ -83,13 +84,18 @@ impl DimensionSubqueryPlanner {
)));
};

let cube_symbol = self
.query_tools
.evaluator_compiler()
.borrow_mut()
.add_cube_table_evaluator(cube_name.clone(), vec![])?;
let member_expression_symbol = MemberExpressionSymbol::try_new(
cube_name.clone(),
cube_symbol,
dim_name.clone(),
MemberExpressionExpression::SqlCall(expression),
None,
None,
self.query_tools.base_tools().clone(),
vec![cube_name.clone()],
)?;
let measure = MemberSymbol::new_member_expression(member_expression_symbol);

Expand Down Expand Up @@ -122,7 +128,7 @@ impl DimensionSubqueryPlanner {
false,
false,
false,
Rc::new(vec![]),
Rc::new(JoinHints::new()),
true,
self.query_properties.disable_external_pre_aggregations(),
)?;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::CommonUtils;
use crate::cube_bridge::join_definition::JoinDefinition;
use crate::cube_bridge::join_hints::JoinHintItem;
use crate::cube_bridge::join_item::JoinItem;
use crate::logical_plan::*;
use crate::planner::join_hints::JoinHints;
use crate::planner::query_tools::QueryTools;
use crate::planner::sql_evaluator::MemberSymbol;
use crate::planner::sql_evaluator::SqlCall;
Expand Down Expand Up @@ -42,10 +42,13 @@ impl JoinPlanner {

pub fn make_join_logical_plan_with_join_hints(
&self,
join_hints: Vec<JoinHintItem>,
join_hints: JoinHints,
dimension_subqueries: Vec<Rc<DimensionSubQuery>>,
) -> Result<Rc<LogicalJoin>, CubeError> {
let join = self.query_tools.join_graph().build_join(join_hints)?;
let join = self
.query_tools
.join_graph()
.build_join(join_hints.into_items())?;
self.make_join_logical_plan(join, dimension_subqueries)
}

Expand Down Expand Up @@ -93,12 +96,12 @@ impl JoinPlanner {

pub fn resolve_join_members_by_hints(
&self,
join_hints: &Vec<JoinHintItem>,
join_hints: &JoinHints,
) -> Result<Vec<ResolvedJoinItem>, CubeError> {
let join = self
.query_tools
.join_graph()
.build_join(join_hints.clone())?;
.build_join(join_hints.items().to_vec())?;
self.resolve_join_members(join)
}
pub fn resolve_join_members(
Expand Down
Loading
Loading