From 4312b871a84fb1add5ab3e539caf9e03035e7a6f Mon Sep 17 00:00:00 2001 From: waralexrom <108349432+waralexrom@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:20:12 +0100 Subject: [PATCH 1/5] chore(tesseract): Symbols path refactoring (#10476) --- .../src/cube_bridge/join_hints.rs | 2 +- .../pre_aggregations_compiler.rs | 21 +- .../src/logical_plan/pre_aggregation.rs | 17 +- .../cubesqlplanner/src/planner/base_cube.rs | 5 +- .../src/planner/filter/base_segment.rs | 11 +- .../cubesqlplanner/src/planner/join_hints.rs | 149 +++++++ .../cubesqlplanner/src/planner/mod.rs | 2 + .../src/planner/planners/common_utils.rs | 3 +- .../planners/dimension_subquery_planner.rs | 12 +- .../src/planner/planners/join_planner.rs | 13 +- .../multi_stage/member_query_planner.rs | 3 +- .../multiplied_measures_query_planner.rs | 23 +- .../src/planner/query_properties.rs | 40 +- .../cubesqlplanner/src/planner/query_tools.rs | 20 +- .../collectors/calc_group_dims_collector.rs | 5 +- .../collectors/cube_names_collector.rs | 16 +- .../collectors/has_cumulative_members.rs | 1 - .../collectors/has_multi_stage_members.rs | 1 - .../collectors/join_hints_collector.rs | 36 +- .../collectors/member_childs_collector.rs | 1 - .../multiplied_measures_collector.rs | 4 +- .../collectors/sub_query_dimensions.rs | 3 +- .../src/planner/sql_evaluator/compiler.rs | 87 ++-- .../sql_evaluator/cube_ref_evaluator.rs | 4 +- .../src/planner/sql_evaluator/sql_call.rs | 83 ++-- .../planner/sql_evaluator/sql_call_builder.rs | 181 ++------- .../sql_evaluator/symbols/common/case.rs | 36 -- .../symbols/common/compiled_member_path.rs | 70 ++++ .../sql_evaluator/symbols/common/mod.rs | 2 + .../symbols/common/symbol_path.rs | 370 ++++++++++++++---- .../sql_evaluator/symbols/cube_symbol.rs | 36 +- .../symbols/dimension_kinds/case_dimension.rs | 9 - .../symbols/dimension_kinds/geo.rs | 7 - .../symbols/dimension_kinds/mod.rs | 9 - .../symbols/dimension_kinds/regular.rs | 6 - .../symbols/dimension_kinds/switch.rs | 8 - .../sql_evaluator/symbols/dimension_symbol.rs | 61 +-- .../symbols/measure_kinds/aggregated.rs | 8 - .../symbols/measure_kinds/calculated.rs | 8 - .../symbols/measure_kinds/count.rs | 13 - .../symbols/measure_kinds/mod.rs | 9 - .../sql_evaluator/symbols/measure_symbol.rs | 82 ++-- .../symbols/member_expression_symbol.rs | 57 ++- .../sql_evaluator/symbols/member_symbol.rs | 91 +++-- .../symbols/time_dimension_symbol.rs | 51 +-- .../src/planner/sql_evaluator/visitor.rs | 16 +- .../cube_bridge/mock_join_graph.rs | 4 +- .../src/test_fixtures/cube_bridge/mod.rs | 1 + .../yaml_files/common/many_to_one_views.yaml | 65 +++ .../test_fixtures/test_utils/test_context.rs | 36 +- .../src/tests/common_sql_generation.rs | 32 +- .../src/tests/compiled_member_path.rs | 216 ++++++++++ .../src/tests/cube_evaluator/compilation.rs | 4 +- .../src/tests/cube_names_collector.rs | 68 ++++ .../src/tests/join_hints_collector.rs | 166 ++++++++ .../src/tests/member_expressions_on_views.rs | 123 ++++++ .../cubesqlplanner/src/tests/mod.rs | 4 + ...on__diamond_join_over_direct_path_sql.snap | 10 + ...collector__many_to_one_view_build_sql.snap | 25 ++ ...on_views__many_to_one_view_base_query.snap | 25 ++ ...__many_to_one_view_child_distinct_dim.snap | 25 ++ ...views__many_to_one_view_child_val_sum.snap | 25 ++ ...ns_on_views__many_to_one_view_one_sum.snap | 25 ++ ...s__many_to_one_view_root_distinct_dim.snap | 25 ++ ..._views__many_to_one_view_root_val_sum.snap | 25 ++ 65 files changed, 1835 insertions(+), 761 deletions(-) create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/many_to_one_views.yaml create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/compiled_member_path.rs create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/cube_names_collector.rs create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__common_sql_generation__diamond_join_over_direct_path_sql.snap create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_hints.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_hints.rs index b164cb23b6c99..5e8950efa9672 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_hints.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_hints.rs @@ -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), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs index c39cbe8948528..22bec416c1f99 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs @@ -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; @@ -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 @@ -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)?, ); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs index ab7063d1df2a5..b5a35d2c67fdb 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs @@ -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() { @@ -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), ); } @@ -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)); } } } @@ -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() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs index b8f7d8661efd5..8a941c25ece15 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs @@ -43,10 +43,7 @@ impl BaseCube { context: Rc, templates: &PlanSqlTemplates, ) -> Result { - 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) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs index b1770df460ede..4d5198db52b44 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs @@ -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}; @@ -24,18 +23,18 @@ impl PartialEq for BaseSegment { impl BaseSegment { pub fn try_new( expression: Rc, - cube_name: String, + cube_symbol: Rc, name: String, full_name: Option, - query_tools: Rc, ) -> Result, 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); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs new file mode 100644 index 0000000000000..109b22a671c6a --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs @@ -0,0 +1,149 @@ +use crate::cube_bridge::join_hints::JoinHintItem; + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct JoinHints { + items: Vec, +} + +impl JoinHints { + pub fn new() -> Self { + Self { items: Vec::new() } + } + + pub fn from_items(mut items: Vec) -> 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 { + self.items + } +} + +impl IntoIterator for JoinHints { + type Item = JoinHintItem; + type IntoIter = std::vec::IntoIter; + + 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")); + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs index 142eb8be7ba96..90cccfb6104ea 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs @@ -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; @@ -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::*; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs index ede38a2726abd..3cb36feb14d48 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs @@ -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) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs index b8d9802ad816e..efce0efc61108 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs @@ -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::{ @@ -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); @@ -122,7 +128,7 @@ impl DimensionSubqueryPlanner { false, false, false, - Rc::new(vec![]), + Rc::new(JoinHints::new()), true, self.query_properties.disable_external_pre_aggregations(), )?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs index 0c0f813f4d192..552491b115a9a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs @@ -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; @@ -42,10 +42,13 @@ impl JoinPlanner { pub fn make_join_logical_plan_with_join_hints( &self, - join_hints: Vec, + join_hints: JoinHints, dimension_subqueries: Vec>, ) -> Result, 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) } @@ -93,12 +96,12 @@ impl JoinPlanner { pub fn resolve_join_members_by_hints( &self, - join_hints: &Vec, + join_hints: &JoinHints, ) -> Result, 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( diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs index 1d1382903dae6..ead41dcdff63f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs @@ -3,6 +3,7 @@ use super::{ MultiStageQueryDescription, RollingWindowDescription, TimeSeriesDescription, }; use crate::logical_plan::*; +use crate::planner::join_hints::JoinHints; use crate::planner::planners::{multi_stage::RollingWindowType, QueryPlanner, SimpleQueryPlanner}; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::MemberSymbol; @@ -74,7 +75,7 @@ impl MultiStageMemberQueryPlanner { true, false, false, - Rc::new(vec![]), + Rc::new(JoinHints::new()), true, self.query_properties.disable_external_pre_aggregations(), )?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs index a00c333e2bdfe..2c18af329f0dc 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs @@ -187,8 +187,9 @@ impl MultipliedMeasuresQueryPlanner { key_cube_name: &String, ) -> Result { for measure in measures.iter() { + let owned_measure = measure.with_stripped_join_prefix(); let member_expression_over_dimensions_cubes = - if let Ok(member_expression) = measure.as_member_expression() { + if let Ok(member_expression) = owned_measure.as_member_expression() { member_expression.cube_names_if_dimension_only_expression()? } else { None @@ -196,18 +197,21 @@ impl MultipliedMeasuresQueryPlanner { let cubes = if let Some(cubes) = member_expression_over_dimensions_cubes { cubes } else { - collect_cube_names(&measure)? + collect_cube_names(&owned_measure)? }; - let join_hints = collect_join_hints(&measure)?; + let join_hints = collect_join_hints(&owned_measure)?; if cubes.iter().any(|cube| cube != key_cube_name) { - let measures_join = self.query_tools.join_graph().build_join(join_hints)?; + let measures_join = self + .query_tools + .join_graph() + .build_join(join_hints.into_items())?; if *measures_join .static_data() .multiplication_factor .get(key_cube_name) .unwrap_or(&false) { - return Err(CubeError::user(format!("{}' references cubes that lead to row multiplication. Please rewrite it using sub query.", measure.full_name()))); + return Err(CubeError::user(format!("{}' references cubes ({}) that lead to row multiplication. Please rewrite it using sub query.", measure.full_name(), cubes.join(", ")))); } return Ok(true); } @@ -230,10 +234,11 @@ impl MultipliedMeasuresQueryPlanner { )?; let subquery_dimension_queries = dimension_subquery_planner.plan_queries(&subquery_dimensions)?; - let join_hints = collect_join_hints_for_measures(&measures)?; - let source = self - .join_planner - .make_join_logical_plan_with_join_hints(join_hints, subquery_dimension_queries)?; + let measure_join_hints = collect_join_hints_for_measures(&measures)?; + let source = self.join_planner.make_join_logical_plan_with_join_hints( + measure_join_hints, + subquery_dimension_queries, + )?; let schema = LogicalSchema::default() .set_dimensions(primary_keys_dimensions.clone()) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs index 85d8280d67322..95891a4a3916d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs @@ -1,8 +1,8 @@ use super::filter::compiler::FilterCompiler; use super::filter::BaseSegment; use super::query_tools::QueryTools; -use crate::cube_bridge::join_hints::JoinHintItem; use crate::cube_bridge::member_expression::MemberExpressionExpressionDef; +use crate::planner::join_hints::JoinHints; use crate::planner::sql_evaluator::{ apply_static_filter_to_filter_item, apply_static_filter_to_symbol, MemberExpressionExpression, MemberExpressionSymbol, TimeDimensionSymbol, @@ -105,7 +105,7 @@ pub struct QueryProperties { multi_fact_join_groups: Vec<(Rc, Vec>)>, pre_aggregation_query: bool, total_query: bool, - query_join_hints: Rc>, + query_join_hints: Rc, allow_multi_stage: bool, disable_external_pre_aggregations: bool, pre_aggregation_id: Option, @@ -149,13 +149,15 @@ impl QueryProperties { ))); } }; + let cube_symbol = evaluator_compiler + .add_cube_table_evaluator(cube_name.clone(), vec![])?; let member_expression_symbol = MemberExpressionSymbol::try_new( - cube_name.clone(), + cube_symbol, name.clone(), MemberExpressionExpression::SqlCall(expression_call), member_expression.static_data().definition.clone(), None, - query_tools.base_tools().clone(), + vec![cube_name.clone()], )?; Ok(MemberSymbol::new_member_expression( member_expression_symbol, @@ -259,13 +261,14 @@ impl QueryProperties { } }; + let cube_symbol = evaluator_compiler.add_cube_table_evaluator(cube_name.clone(), vec![])?; let member_expression_symbol = MemberExpressionSymbol::try_new( - cube_name.clone(), + cube_symbol, name.clone(), expression, member_expression.static_data().definition.clone(), None, - query_tools.base_tools().clone(), + vec![cube_name.clone()], )?; Ok(MemberSymbol::new_member_expression(member_expression_symbol)) } @@ -299,12 +302,13 @@ impl QueryProperties { .segment_by_path(member_name.clone())?; let expression_evaluator = evaluator_compiler .compile_sql_call(&cube_name, definition.sql()?)?; + let cube_symbol = evaluator_compiler + .add_cube_table_evaluator(cube_name.clone(), vec![])?; BaseSegment::try_new( expression_evaluator, - cube_name, + cube_symbol, name, Some(member_name.clone()), - query_tools.clone(), ) } OptionsMember::MemberExpression(member_expression) => { @@ -331,13 +335,9 @@ impl QueryProperties { ))); } }; - BaseSegment::try_new( - expression_evaluator, - cube_name, - name, - None, - query_tools.clone(), - ) + let cube_symbol = evaluator_compiler + .add_cube_table_evaluator(cube_name.clone(), vec![])?; + BaseSegment::try_new(expression_evaluator, cube_symbol, name, None) } }?; Ok(FilterItem::Segment(segment)) @@ -405,7 +405,9 @@ impl QueryProperties { }; let ungrouped = options.static_data().ungrouped.unwrap_or(false); - let query_join_hints = Rc::new(options.join_hints()?.unwrap_or_default()); + let query_join_hints = Rc::new(JoinHints::from_items( + options.join_hints()?.unwrap_or_default(), + )); let pre_aggregation_query = options.static_data().pre_aggregation_query.unwrap_or(false); let total_query = options.static_data().total_query.unwrap_or(false); @@ -455,7 +457,7 @@ impl QueryProperties { ungrouped: bool, pre_aggregation_query: bool, total_query: bool, - query_join_hints: Rc>, + query_join_hints: Rc, allow_multi_stage: bool, disable_external_pre_aggregations: bool, ) -> Result, CubeError> { @@ -575,7 +577,7 @@ impl QueryProperties { } pub fn compute_join_multi_fact_groups( - query_join_hints: Rc>, + query_join_hints: Rc, query_tools: Rc, measures: &Vec>, dimensions: &Vec>, @@ -728,7 +730,7 @@ impl QueryProperties { self.row_limit } - pub fn query_join_hints(&self) -> &Rc> { + pub fn query_join_hints(&self) -> &Rc { &self.query_join_hints } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs index a32a4be8b465e..ce1ff3c4460cd 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs @@ -9,6 +9,7 @@ use crate::cube_bridge::join_item::JoinItemStatic; use crate::cube_bridge::security_context::SecurityContext; use crate::cube_bridge::sql_templates_render::SqlTemplatesRender; use crate::plan::FilterItem; +use crate::planner::join_hints::JoinHints; use crate::planner::sql_evaluator::collectors::collect_join_hints; use crate::planner::sql_templates::PlanSqlTemplates; use chrono_tz::Tz; @@ -19,8 +20,8 @@ use std::collections::HashMap; use std::rc::Rc; pub struct QueryToolsCachedData { - join_hints: HashMap>>, - join_hints_to_join_key: HashMap>>, Rc>, + join_hints: HashMap>, + join_hints_to_join_key: HashMap>, Rc>, join_key_to_join: HashMap, Rc>, } @@ -42,7 +43,7 @@ impl QueryToolsCachedData { pub fn join_hints_for_member( &mut self, node: &Rc, - ) -> Result>, CubeError> { + ) -> Result, CubeError> { let full_name = node.full_name(); if let Some(val) = self.join_hints.get(&full_name) { Ok(val.clone()) @@ -56,7 +57,7 @@ impl QueryToolsCachedData { pub fn join_hints_for_member_symbol_vec( &mut self, vec: &Vec>, - ) -> Result>>, CubeError> { + ) -> Result>, CubeError> { vec.iter() .map(|b| self.join_hints_for_member(b)) .collect::, _>>() @@ -65,7 +66,7 @@ impl QueryToolsCachedData { pub fn join_hints_for_filter_item_vec( &mut self, vec: &Vec, - ) -> Result>>, CubeError> { + ) -> Result>, CubeError> { let mut member_symbols = Vec::new(); for i in vec.iter() { i.find_all_member_evaluators(&mut member_symbols); @@ -78,18 +79,13 @@ impl QueryToolsCachedData { pub fn join_by_hints( &mut self, - hints: Vec>>, + hints: Vec>, join_fn: impl FnOnce(Vec) -> Result, CubeError>, ) -> Result<(Rc, Rc), CubeError> { if let Some(key) = self.join_hints_to_join_key.get(&hints) { Ok((key.clone(), self.join_key_to_join.get(key).unwrap().clone())) } else { - let join = join_fn( - hints - .iter() - .flat_map(|h| h.as_ref().iter().cloned()) - .collect(), - )?; + let join = join_fn(hints.iter().flat_map(|h| h.iter().cloned()).collect())?; let join_key = Rc::new(JoinKey { root: join.static_data().root.to_string(), joins: join diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs index 9dc6e187dfe95..b8e00ff721e3c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs @@ -27,7 +27,6 @@ impl TraversalVisitor for CalcGroupDimsCollector { fn on_node_traverse( &mut self, node: &Rc, - path: &Vec, _: &Self::State, ) -> Result, CubeError> { match node.as_ref() { @@ -37,9 +36,7 @@ impl TraversalVisitor for CalcGroupDimsCollector { return Ok(None); } } - MemberSymbol::TimeDimension(e) => { - return self.on_node_traverse(e.base_symbol(), path, &()) - } + MemberSymbol::TimeDimension(e) => return self.on_node_traverse(e.base_symbol(), &()), MemberSymbol::Measure(_) => {} MemberSymbol::MemberExpression(_) => {} }; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs index 085ed8bc872c0..840b36958312a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs @@ -24,12 +24,12 @@ impl TraversalVisitor for CubeNamesCollector { fn on_node_traverse( &mut self, node: &Rc, - path: &Vec, _: &Self::State, ) -> Result, CubeError> { match node.as_ref() { MemberSymbol::Dimension(e) => { if !e.is_view() { + let path = node.path(); if !path.is_empty() { for p in path { self.names.insert(p.clone()); @@ -42,11 +42,10 @@ impl TraversalVisitor for CubeNamesCollector { return Ok(None); } } - MemberSymbol::TimeDimension(e) => { - return self.on_node_traverse(e.base_symbol(), path, &()) - } + MemberSymbol::TimeDimension(e) => return self.on_node_traverse(e.base_symbol(), &()), MemberSymbol::Measure(e) => { if !e.is_view() { + let path = node.path(); if !path.is_empty() { for p in path { self.names.insert(p.clone()); @@ -62,13 +61,10 @@ impl TraversalVisitor for CubeNamesCollector { } fn on_cube_ref(&mut self, cube_ref: &CubeRef, _state: &Self::State) -> Result<(), CubeError> { - if let CubeRef::Name { symbol, path, .. } = cube_ref { - if !path.is_empty() { - for p in path { - self.names.insert(p.clone()); - } + if let CubeRef::Name(symbol) = cube_ref { + for p in symbol.path() { + self.names.insert(p.clone()); } - self.names.insert(symbol.cube_name().clone()); } Ok(()) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_cumulative_members.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_cumulative_members.rs index 5848fc779ce17..15059c729e572 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_cumulative_members.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_cumulative_members.rs @@ -23,7 +23,6 @@ impl TraversalVisitor for HasCumulativeMembersCollector { fn on_node_traverse( &mut self, node: &Rc, - _path: &Vec, _: &Self::State, ) -> Result, CubeError> { if let MemberSymbol::Measure(s) = node.as_ref() { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_multi_stage_members.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_multi_stage_members.rs index 4e092c62162a3..360555be0fd14 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_multi_stage_members.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_multi_stage_members.rs @@ -25,7 +25,6 @@ impl TraversalVisitor for HasMultiStageMembersCollector { fn on_node_traverse( &mut self, node: &Rc, - _path: &Vec, _: &Self::State, ) -> Result, CubeError> { match node.as_ref() { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs index a0b9f248b46ee..51c428685e161 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs @@ -1,4 +1,5 @@ use crate::cube_bridge::join_hints::JoinHintItem; +use crate::planner::join_hints::JoinHints; use crate::planner::sql_evaluator::{CubeRef, MemberSymbol, TraversalVisitor}; use cubenativeutils::CubeError; use itertools::Itertools; @@ -23,7 +24,6 @@ impl TraversalVisitor for JoinHintsCollector { fn on_node_traverse( &mut self, node: &Rc, - path: &Vec, _: &Self::State, ) -> Result, CubeError> { if node.is_multi_stage() { @@ -33,10 +33,10 @@ impl TraversalVisitor for JoinHintsCollector { self.apply(item, &())?; } } - for (dep, path) in dim.get_dependencies_with_path().into_iter() { + for dep in dim.get_dependencies().into_iter() { if let Ok(dim) = dep.as_dimension() { if dim.is_multi_stage() { - self.on_node_traverse(&dep, &path, &())?; + self.on_node_traverse(&dep, &())?; } } } @@ -47,6 +47,7 @@ impl TraversalVisitor for JoinHintsCollector { match node.as_ref() { MemberSymbol::Dimension(e) => { if !e.is_view() { + let path = node.path(); if !path.is_empty() { if path.len() == 1 { self.hints.push(JoinHintItem::Single(path[0].clone())) @@ -61,11 +62,10 @@ impl TraversalVisitor for JoinHintsCollector { return Ok(None); } } - MemberSymbol::TimeDimension(e) => { - return self.on_node_traverse(e.base_symbol(), path, &()) - } + MemberSymbol::TimeDimension(e) => return self.on_node_traverse(e.base_symbol(), &()), MemberSymbol::Measure(e) => { if !e.is_view() { + let path = node.path(); if !path.is_empty() { if path.len() == 1 { self.hints.push(JoinHintItem::Single(path[0].clone())) @@ -83,11 +83,10 @@ impl TraversalVisitor for JoinHintsCollector { } fn on_cube_ref(&mut self, cube_ref: &CubeRef, _state: &Self::State) -> Result<(), CubeError> { - if let CubeRef::Name { symbol, path, .. } = cube_ref { - if !path.is_empty() { - let mut path = path.clone(); - path.push(symbol.cube_name().clone()); - self.hints.push(JoinHintItem::Vector(path)); + if let CubeRef::Name(symbol) = cube_ref { + let path = symbol.path(); + if path.len() > 1 { + self.hints.push(JoinHintItem::Vector(path.clone())); } else { self.hints .push(JoinHintItem::Single(symbol.cube_name().clone())); @@ -97,17 +96,12 @@ impl TraversalVisitor for JoinHintsCollector { } } -pub fn collect_join_hints(node: &Rc) -> Result, CubeError> { +pub fn collect_join_hints(node: &Rc) -> Result { let mut visitor = JoinHintsCollector::new(); visitor.apply(node, &())?; let mut collected_hints = visitor.extract_result(); - let join_map = match node.as_ref() { - MemberSymbol::Dimension(d) => d.join_map(), - MemberSymbol::TimeDimension(d) => d.join_map(), - MemberSymbol::Measure(m) => m.join_map(), - _ => &None, - }; + let join_map = node.join_map(); if let Some(join_map) = join_map { for hint in collected_hints.iter_mut() { @@ -128,17 +122,17 @@ pub fn collect_join_hints(node: &Rc) -> Result, } } - Ok(collected_hints) + Ok(JoinHints::from_items(collected_hints)) } pub fn collect_join_hints_for_measures( measures: &Vec>, -) -> Result, CubeError> { +) -> Result { let mut visitor = JoinHintsCollector::new(); for meas in measures.iter() { visitor.apply(&meas, &())?; } let res = visitor.extract_result(); - Ok(res) + Ok(JoinHints::from_items(res)) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/member_childs_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/member_childs_collector.rs index a0e6d986879f5..529c3e9d9b921 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/member_childs_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/member_childs_collector.rs @@ -33,7 +33,6 @@ impl TraversalVisitor for MemberChildsCollector { fn on_node_traverse( &mut self, node: &Rc, - _path: &Vec, state: &Self::State, ) -> Result, CubeError> { if state.is_root { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs index 26836acbe5796..dcfc235564cf0 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs @@ -36,7 +36,6 @@ impl TraversalVisitor for CompositeMeasuresCollector { fn on_node_traverse( &mut self, node: &Rc, - _path: &Vec, state: &Self::State, ) -> Result, CubeError> { let res = match node.as_ref() { @@ -90,7 +89,6 @@ impl TraversalVisitor for MultipliedMeasuresCollector { fn on_node_traverse( &mut self, node: &Rc, - _path: &Vec, _: &Self::State, ) -> Result, CubeError> { let res = match node.as_ref() { @@ -100,7 +98,7 @@ impl TraversalVisitor for MultipliedMeasuresCollector { .join .static_data() .multiplication_factor - .get(e.cube_name()) + .get(&e.cube_name()) .unwrap_or(&false) .clone(); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs index 3f95e194bc3e8..436d948472cb5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs @@ -38,7 +38,6 @@ impl TraversalVisitor for SubQueryDimensionsCollector { fn on_node_traverse( &mut self, node: &Rc, - path: &Vec, _state: &Self::State, ) -> Result, CubeError> { match node.as_ref() { @@ -54,7 +53,7 @@ impl TraversalVisitor for SubQueryDimensionsCollector { } Ok(Some(())) } - MemberSymbol::TimeDimension(dim) => self.on_node_traverse(dim.base_symbol(), path, &()), + MemberSymbol::TimeDimension(dim) => self.on_node_traverse(dim.base_symbol(), &()), _ => Ok(Some(())), } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs index 6c133522863ff..5c25f368d441f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs @@ -31,8 +31,8 @@ pub struct Compiler { security_context: Rc, timezone: Tz, members: HashMap<(CacheSymbolType, String), Rc>, - cube_names: HashMap>, - cube_tables: HashMap>, + cube_names: HashMap, Rc>, + cube_tables: HashMap, Rc>, } impl Compiler { @@ -57,18 +57,15 @@ impl Compiler { &mut self, name: String, ) -> Result, CubeError> { - let path = name.split(".").map(|s| s.to_string()).collect::>(); - if self.cube_evaluator.is_measure(path.clone())? { - Ok(self.add_measure_evaluator(name)?) - } else if self.cube_evaluator.is_dimension(path.clone())? { - Ok(self.add_dimension_evaluator(name)?) - } else if self.cube_evaluator.is_segment(path.clone())? { - Ok(self.add_segment_evaluator(name)?) - } else { - Err(CubeError::internal(format!( - "Cannot resolve evaluator of member {}. Only dimensions, measures and segments can be autoresolved", + let path = SymbolPath::parse(self.cube_evaluator.clone(), &name)?; + match path.path_type() { + SymbolPathType::Dimension => self.add_dimension_evaluator_by_path(path), + SymbolPathType::Measure => self.add_measure_evaluator_by_path(path), + SymbolPathType::Segment => self.add_segment_evaluator_by_path(path), + _ => Err(CubeError::internal(format!( + "Cannot auto-resolve {}. Only dimensions, measures and segments", name - ))) + ))), } } @@ -77,10 +74,10 @@ impl Compiler { measure: String, ) -> Result, CubeError> { let path = SymbolPath::parse(self.cube_evaluator.clone(), &measure)?; - self.add_measure_evaluator_impl(path) + self.add_measure_evaluator_by_path(path) } - fn add_measure_evaluator_impl( + pub fn add_measure_evaluator_by_path( &mut self, path: SymbolPath, ) -> Result, CubeError> { @@ -101,15 +98,15 @@ impl Compiler { let path = SymbolPath::parse(self.cube_evaluator.clone(), &dimension)?; match path.path_type() { SymbolPathType::Segment => { - let symbol = self.add_segment_evaluator(path.full_name().clone())?; + let symbol = self.add_segment_evaluator_by_path(path)?; let me = symbol.as_member_expression()?; Ok(MemberSymbol::new_member_expression(me.with_parenthesized())) } - _ => self.add_dimension_evaluator_impl(path), + _ => self.add_dimension_evaluator_by_path(path), } } - fn add_dimension_evaluator_impl( + pub fn add_dimension_evaluator_by_path( &mut self, path: SymbolPath, ) -> Result, CubeError> { @@ -124,27 +121,33 @@ impl Compiler { } pub fn add_segment_evaluator(&mut self, name: String) -> Result, CubeError> { - if let Some(exists) = self.exists_member(CacheSymbolType::Segment, &name) { + let path = SymbolPath::parse(self.cube_evaluator.clone(), &name)?; + self.add_segment_evaluator_by_path(path) + } + + pub fn add_segment_evaluator_by_path( + &mut self, + path: SymbolPath, + ) -> Result, CubeError> { + let full_name = path.full_name().clone(); + if let Some(exists) = self.exists_member(CacheSymbolType::Segment, &full_name) { return Ok(exists.clone()); } - let path = self - .cube_evaluator - .parse_path("segments".to_string(), name.clone())?; - let cube_name = path[0].clone(); - let member_name = path[1].clone(); - let definition = self.cube_evaluator.segment_by_path(name.clone())?; - let sql_call = self.compile_sql_call(&cube_name, definition.sql()?)?; - let alias = PlanSqlTemplates::member_alias_name(&cube_name, &member_name, &None); + let definition = self.cube_evaluator.segment_by_path(full_name.clone())?; + let sql_call = self.compile_sql_call(path.cube_name(), definition.sql()?)?; + let alias = + PlanSqlTemplates::member_alias_name(path.cube_name(), path.symbol_name(), &None); + let cube_symbol = self.add_cube_table_evaluator(path.cube_name().clone(), vec![])?; let symbol = MemberExpressionSymbol::try_new( - cube_name, - member_name, + cube_symbol, + path.symbol_name().clone(), MemberExpressionExpression::SqlCall(sql_call), None, Some(alias), - self.base_tools.clone(), + path.path().clone(), )?; let result = MemberSymbol::new_member_expression(symbol); - let key = (CacheSymbolType::Segment, name); + let key = (CacheSymbolType::Segment, full_name); self.members.insert(key, result.clone()); Ok(result) } @@ -152,13 +155,16 @@ impl Compiler { pub fn add_cube_name_evaluator( &mut self, cube_name: String, + path: Vec, ) -> Result, CubeError> { - if let Some(exists) = self.cube_names.get(&cube_name) { + let cache_key = CubeNameSymbol::normalize_path(path.clone(), &cube_name); + if let Some(exists) = self.cube_names.get(&cache_key) { Ok(exists.clone()) } else { - let result = CubeNameSymbolFactory::try_new(&cube_name, self.cube_evaluator.clone())? - .build(self)?; - self.cube_names.insert(cube_name, result.clone()); + let result = + CubeNameSymbolFactory::try_new(&cube_name, self.cube_evaluator.clone(), path)? + .build(self)?; + self.cube_names.insert(cache_key, result.clone()); Ok(result) } } @@ -166,13 +172,16 @@ impl Compiler { pub fn add_cube_table_evaluator( &mut self, cube_name: String, + path: Vec, ) -> Result, CubeError> { - if let Some(exists) = self.cube_tables.get(&cube_name) { + let cache_key = CubeNameSymbol::normalize_path(path.clone(), &cube_name); + if let Some(exists) = self.cube_tables.get(&cache_key) { Ok(exists.clone()) } else { - let result = CubeTableSymbolFactory::try_new(&cube_name, self.cube_evaluator.clone())? - .build(self)?; - self.cube_tables.insert(cube_name, result.clone()); + let result = + CubeTableSymbolFactory::try_new(&cube_name, self.cube_evaluator.clone(), path)? + .build(self)?; + self.cube_tables.insert(cache_key, result.clone()); Ok(result) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/cube_ref_evaluator.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/cube_ref_evaluator.rs index 1bf94e8f7e824..de638d5d9954b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/cube_ref_evaluator.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/cube_ref_evaluator.rs @@ -32,12 +32,12 @@ impl CubeRefEvaluator { templates: &PlanSqlTemplates, ) -> Result { match cube_ref { - CubeRef::Name { symbol, .. } => { + CubeRef::Name(symbol) => { let name = symbol.evaluate_sql()?; let alias = self.resolve_cube_alias(&name); templates.quote_identifier(&alias) } - CubeRef::Table { symbol, .. } => { + CubeRef::Table(symbol) => { if let Some(pre_agg) = self.original_sql_pre_aggregations.get(symbol.cube_name()) { return Ok(pre_agg.clone()); } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs index c6989cefc838d..08dc7235cb380 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs @@ -13,41 +13,35 @@ use std::rc::Rc; #[derive(Clone, Debug)] pub enum CubeRef { - Name { - symbol: Rc, - path: Vec, - }, - Table { - symbol: Rc, - path: Vec, - }, + Name(Rc), + Table(Rc), } impl CubeRef { pub fn cube_name(&self) -> &String { match self { - CubeRef::Name { symbol, .. } => symbol.cube_name(), - CubeRef::Table { symbol, .. } => symbol.cube_name(), + CubeRef::Name(symbol) => symbol.cube_name(), + CubeRef::Table(symbol) => symbol.cube_name(), } } pub fn path(&self) -> &Vec { match self { - CubeRef::Name { path, .. } => path, - CubeRef::Table { path, .. } => path, + CubeRef::Name(symbol) => symbol.path(), + CubeRef::Table(symbol) => symbol.path(), } } pub fn as_name(&self) -> Option<&Rc> { match self { - CubeRef::Name { symbol, .. } => Some(symbol), + CubeRef::Name(symbol) => Some(symbol), _ => None, } } pub fn as_table(&self) -> Option<&Rc> { match self { - CubeRef::Table { symbol, .. } => Some(symbol), + CubeRef::Table(symbol) => Some(symbol), _ => None, } } @@ -105,12 +99,6 @@ impl SqlCallArg { } } -#[derive(Debug, Clone)] -pub struct SqlCallDependency { - pub path: Vec, - pub symbol: SqlDependency, -} - #[derive(Debug, Clone)] pub struct SqlCallFilterParamsItem { pub filter_symbol_name: String, @@ -125,7 +113,7 @@ pub struct SqlCallFilterGroupItem { #[derive(Clone, Debug)] pub struct SqlCall { template: SqlTemplate, - deps: Vec, + deps: Vec, filter_params: Vec, filter_groups: Vec, security_context: SecutityContextProps, @@ -134,7 +122,7 @@ pub struct SqlCall { impl SqlCall { pub(super) fn new( template: SqlTemplate, - deps: Vec, + deps: Vec, filter_params: Vec, filter_groups: Vec, security_context: SecutityContextProps, @@ -168,7 +156,7 @@ impl SqlCall { ) } else { Err(CubeError::internal( - "SqlCall::eval called for fuction that return string".to_string(), + "SqlCall::eval called for function that returns string".to_string(), )) } } @@ -213,7 +201,7 @@ impl SqlCall { if self.deps.is_empty() { true } else { - self.deps.iter().any(|dep| dep.symbol.is_cube_ref()) + self.deps.iter().any(|dep| dep.is_cube_ref()) } } @@ -221,7 +209,7 @@ impl SqlCall { self.deps .iter() .filter_map(|dep| { - if let SqlDependency::CubeRef(CubeRef::Name { symbol, .. }) = &dep.symbol { + if let SqlDependency::CubeRef(CubeRef::Name(symbol)) = dep { Some(symbol.clone()) } else { None @@ -266,7 +254,7 @@ impl SqlCall { let deps = self .deps .iter() - .map(|dep| match &dep.symbol { + .map(|dep| match dep { SqlDependency::Symbol(m) => visitor.apply(m, node_processor.clone(), templates), SqlDependency::CubeRef(cr) => { visitor.evaluate_cube_ref(cr, node_processor.clone(), templates) @@ -402,60 +390,35 @@ impl SqlCall { pub fn resolve_direct_reference(&self) -> Option> { if self.is_direct_reference() { - self.deps[0].symbol.as_symbol().cloned() + self.deps[0].as_symbol().cloned() } else { None } } pub fn dependencies_count(&self) -> usize { - self.deps.iter().filter(|d| d.symbol.is_symbol()).count() + self.deps.iter().filter(|d| d.is_symbol()).count() } pub fn get_dependencies(&self) -> Vec> { self.deps .iter() - .filter_map(|d| d.symbol.as_symbol().cloned()) - .collect() - } - - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - self.deps - .iter() - .filter_map(|d| d.symbol.as_symbol().map(|s| (s.clone(), d.path.clone()))) + .filter_map(|d| d.as_symbol().cloned()) .collect() } pub fn extract_symbol_deps(&self, result: &mut Vec>) { for dep in self.deps.iter() { - if let Some(s) = dep.symbol.as_symbol() { + if let Some(s) = dep.as_symbol() { result.push(s.clone()) } } } - pub fn extract_symbol_deps_with_path(&self, result: &mut Vec<(Rc, Vec)>) { - for dep in self.deps.iter() { - if let Some(s) = dep.symbol.as_symbol() { - result.push((s.clone(), dep.path.clone())) - } - } - } - pub fn extract_cube_refs(&self, result: &mut Vec) { for dep in self.deps.iter() { - if let SqlDependency::CubeRef(cr) = &dep.symbol { - let cube_ref = match cr { - CubeRef::Name { symbol, .. } => CubeRef::Name { - symbol: symbol.clone(), - path: dep.path.clone(), - }, - CubeRef::Table { symbol, .. } => CubeRef::Table { - symbol: symbol.clone(), - path: dep.path.clone(), - }, - }; - result.push(cube_ref); + if let SqlDependency::CubeRef(cr) = dep { + result.push(cr.clone()); } } } @@ -466,8 +429,8 @@ impl SqlCall { ) -> Result, CubeError> { let mut result = self.clone(); for dep in result.deps.iter_mut() { - if let SqlDependency::Symbol(ref s) = dep.symbol { - dep.symbol = SqlDependency::Symbol(s.apply_recursive(f)?); + if let SqlDependency::Symbol(ref s) = dep { + *dep = SqlDependency::Symbol(s.apply_recursive(f)?); } } Ok(Rc::new(result)) @@ -486,7 +449,7 @@ impl crate::utils::debug::DebugSql for SqlCall { let deps = self .deps .iter() - .map(|dep| match &dep.symbol { + .map(|dep| match dep { SqlDependency::Symbol(s) => { if expand_deps { s.debug_sql(true) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs index 99a32ff5352f9..5617bad221a07 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs @@ -1,15 +1,12 @@ -use super::symbols::MemberSymbol; use super::Compiler; use super::{ - CubeRef, SqlCall, SqlCallDependency, SqlCallFilterGroupItem, SqlCallFilterParamsItem, - SqlDependency, + CubeRef, SqlCall, SqlCallFilterGroupItem, SqlCallFilterParamsItem, SqlDependency, SymbolPath, + SymbolPathType, }; use crate::cube_bridge::base_tools::BaseTools; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::member_sql::*; use crate::cube_bridge::security_context::SecurityContext; -use crate::planner::sql_evaluator::TimeDimensionSymbol; -use crate::planner::GranularityHelper; use cubenativeutils::CubeError; use std::rc::Rc; @@ -97,145 +94,49 @@ impl<'a> SqlCallBuilder<'a> { &mut self, current_cube_name: &String, dep_path: &Vec, - ) -> Result { + ) -> Result { assert!(!dep_path.is_empty()); - self.process_dependency_item(current_cube_name, dep_path, vec![]) - .map_err(|e| CubeError::user(format!("Error in `{}`: {}", dep_path.join("."), e))) - } - - fn process_dependency_item( - &mut self, - current_cube_name: &String, - path_tail: &[String], - processed_path: Vec, - ) -> Result { - assert!(!path_tail.is_empty()); - if let Some(member) = self.try_process_member_dependency_item( - current_cube_name, - path_tail, - processed_path.clone(), - ) { - Ok(member) - } else if let Some(cube_name) = self.get_cube_name(¤t_cube_name, &path_tail[0])? { - self.process_cube_dependency_item(&cube_name, path_tail, processed_path) - } else { - Err(CubeError::user(format!( - "Undefined property {}", - path_tail[0] - ))) - } - } - - fn try_process_member_dependency_item( - &mut self, - current_cube_name: &String, - path_tail: &[String], - processed_path: Vec, - ) -> Option { - if let Ok(member_symbol) = self.build_evaluator(¤t_cube_name, &path_tail[0]) { - if let Ok(dimension) = member_symbol.as_dimension() { - if dimension.is_time() && path_tail.len() == 2 { - let granularity = &path_tail[1]; - if let Ok(Some(granularity_obj)) = GranularityHelper::make_granularity_obj( - self.cube_evaluator.clone(), - self.compiler, - ¤t_cube_name, - &path_tail[0], - Some(granularity.clone()), - ) { - let time_dim_symbol = - MemberSymbol::new_time_dimension(TimeDimensionSymbol::new( - member_symbol, - Some(granularity.clone()), - Some(granularity_obj), - None, - )); - let result = SqlCallDependency { - path: processed_path, - symbol: SqlDependency::Symbol(time_dim_symbol), - }; - return Some(result); - } else { - return None; - } - } + let symbol_path = SymbolPath::parse_parts( + self.cube_evaluator.clone(), + Some(current_cube_name), + dep_path, + ) + .map_err(|e| CubeError::user(format!("Error in `{}`: {}", dep_path.join("."), e)))?; + + let path = symbol_path.path().clone(); + + match symbol_path.path_type() { + SymbolPathType::Dimension => { + let member = self + .compiler + .add_dimension_evaluator_by_path(symbol_path.clone())?; + Ok(SqlDependency::Symbol(member)) } - if path_tail.len() > 1 { - return None; + SymbolPathType::Measure => { + let member = self + .compiler + .add_measure_evaluator_by_path(symbol_path.clone())?; + Ok(SqlDependency::Symbol(member)) + } + SymbolPathType::Segment => { + let member = self + .compiler + .add_segment_evaluator_by_path(symbol_path.clone())?; + Ok(SqlDependency::Symbol(member)) + } + SymbolPathType::CubeName => { + let symbol = self + .compiler + .add_cube_name_evaluator(symbol_path.cube_name().clone(), path)?; + Ok(SqlDependency::CubeRef(CubeRef::Name(symbol))) + } + SymbolPathType::CubeTable => { + let symbol = self + .compiler + .add_cube_table_evaluator(symbol_path.cube_name().clone(), path)?; + Ok(SqlDependency::CubeRef(CubeRef::Table(symbol))) } - let result = SqlCallDependency { - path: processed_path, - symbol: SqlDependency::Symbol(member_symbol), - }; - Some(result) - } else { - None - } - } - - fn process_cube_dependency_item( - &mut self, - cube_name: &String, - path_tail: &[String], - mut processed_path: Vec, - ) -> Result { - processed_path.push(cube_name.clone()); - if path_tail.len() == 1 { - let symbol = self.compiler.add_cube_name_evaluator(cube_name.clone())?; - let result = SqlCallDependency { - path: processed_path.clone(), - symbol: SqlDependency::CubeRef(CubeRef::Name { - symbol, - path: processed_path, - }), - }; - return Ok(result); - } - if path_tail.len() == 2 && path_tail[1] == "__sql_fn" { - let symbol = self.compiler.add_cube_table_evaluator(cube_name.clone())?; - let result = SqlCallDependency { - path: processed_path.clone(), - symbol: SqlDependency::CubeRef(CubeRef::Table { - symbol, - path: processed_path, - }), - }; - return Ok(result); - } - self.process_dependency_item(&cube_name, &path_tail[1..], processed_path) - } - - fn get_cube_name( - &mut self, - current_cube: &String, - cube_name: &String, - ) -> Result, CubeError> { - if self.is_current_cube(cube_name) { - Ok(Some(current_cube.clone())) - } else if self.cube_evaluator.cube_exists(cube_name.clone())? { - Ok(Some(cube_name.clone())) - } else { - Ok(None) - } - } - - fn is_current_cube(&self, name: &str) -> bool { - match name { - "CUBE" | "TABLE" => true, - _ => false, } } - - fn build_evaluator( - &mut self, - current_cube_name: &String, - name: &String, - ) -> Result, CubeError> { - let dep_full_name = format!("{}.{}", current_cube_name, name); - let res = self - .compiler - .add_auto_resolved_member_evaluator(dep_full_name.clone())?; - Ok(res) - } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/case.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/case.rs index 4066a50df1857..eaaf7bcdd966c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/case.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/case.rs @@ -52,18 +52,6 @@ impl CaseDefinition { sql.extract_symbol_deps(result); } } - fn extract_symbol_deps_with_path(&self, result: &mut Vec<(Rc, Vec)>) { - for itm in self.items.iter() { - itm.sql.extract_symbol_deps_with_path(result); - if let CaseLabel::Sql(sql) = &itm.label { - sql.extract_symbol_deps_with_path(result); - } - } - if let CaseLabel::Sql(sql) = &self.else_label { - sql.extract_symbol_deps_with_path(result); - } - } - fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, @@ -130,13 +118,6 @@ impl CaseSwitchItem { } } - fn extract_symbol_deps_with_path(&self, result: &mut Vec<(Rc, Vec)>) { - match self { - CaseSwitchItem::Sql(sql_call) => sql_call.extract_symbol_deps_with_path(result), - CaseSwitchItem::Member(member_symbol) => result.push((member_symbol.clone(), vec![])), - } - } - fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, @@ -247,16 +228,6 @@ impl CaseSwitchDefinition { else_sql.extract_symbol_deps(result); } } - fn extract_symbol_deps_with_path(&self, result: &mut Vec<(Rc, Vec)>) { - self.switch.extract_symbol_deps_with_path(result); - for itm in self.items.iter() { - itm.sql.extract_symbol_deps_with_path(result); - } - if let Some(else_sql) = &self.else_sql { - else_sql.extract_symbol_deps_with_path(result); - } - } - fn get_switch_values(&self) -> Option> { if let CaseSwitchItem::Member(member) = &self.switch { if let Ok(switch_dim) = member.as_dimension() { @@ -411,13 +382,6 @@ impl Case { Case::CaseSwitch(def) => def.extract_symbol_deps(result), } } - pub fn extract_symbol_deps_with_path(&self, result: &mut Vec<(Rc, Vec)>) { - match self { - Case::Case(def) => def.extract_symbol_deps_with_path(result), - Case::CaseSwitch(def) => def.extract_symbol_deps_with_path(result), - } - } - pub fn case_switch_dimension(&self) -> Option> { if let Case::CaseSwitch(case) = &self { if let CaseSwitchItem::Member(member) = &case.switch { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs new file mode 100644 index 0000000000000..e8c9c8fa7dbc3 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs @@ -0,0 +1,70 @@ +use crate::planner::sql_evaluator::{CubeNameSymbol, CubeTableSymbol}; +use std::rc::Rc; + +#[derive(Clone, Debug)] +pub struct CompiledMemberPath { + cube: Rc, + full_name: String, + name: String, + alias: String, + path: Vec, +} + +impl CompiledMemberPath { + pub fn new( + cube: Rc, + full_name: String, + name: String, + alias: String, + path: Vec, + ) -> Self { + let path = CubeNameSymbol::normalize_path(path, cube.cube_name()); + Self { + cube, + full_name, + name, + alias, + path, + } + } + + pub fn full_name(&self) -> &String { + &self.full_name + } + + pub fn cube_name(&self) -> &String { + self.cube.cube_name() + } + + pub fn cube(&self) -> &Rc { + &self.cube + } + + pub fn join_map(&self) -> &Option>> { + self.cube.join_map() + } + + pub fn name(&self) -> &String { + &self.name + } + + pub fn alias(&self) -> &String { + &self.alias + } + + pub fn path(&self) -> &Vec { + &self.path + } + + /// Returns a copy with the path reduced to just the owning cube, + /// stripping any join chain prefix (e.g. from views or cross-cube references). + pub fn strip_join_prefix(&self) -> Self { + Self { + cube: self.cube.clone(), + full_name: self.full_name.clone(), + name: self.name.clone(), + alias: self.alias.clone(), + path: vec![self.cube_name().clone()], + } + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/mod.rs index ed8bf27a461d2..e278e2e966bff 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/mod.rs @@ -1,11 +1,13 @@ mod aggregation_type; mod case; +mod compiled_member_path; mod dimension_type; mod static_filter; mod symbol_path; pub use aggregation_type::*; pub use case::*; +pub use compiled_member_path::*; pub use dimension_type::*; pub use static_filter::*; pub use symbol_path::*; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/symbol_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/symbol_path.rs index fe05d49b0e835..0a595851443cc 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/symbol_path.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/symbol_path.rs @@ -4,11 +4,13 @@ use cubenativeutils::CubeError; use crate::cube_bridge::evaluator::CubeEvaluator; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SymbolPathType { Dimension, Measure, Segment, + CubeName, + CubeTable, } #[derive(Debug, Clone)] @@ -29,7 +31,11 @@ impl SymbolPath { symbol_name: String, granularity: Option, ) -> Self { - let full_name = format!("{}.{}", cube_name, symbol_name); + let full_name = if symbol_name.is_empty() { + cube_name.clone() + } else { + format!("{}.{}", cube_name, symbol_name) + }; Self { path_type, path, @@ -41,46 +47,140 @@ impl SymbolPath { } pub fn parse(cube_evaluator: Rc, path: &str) -> Result { - let parts = path.split(".").collect::>(); - if parts.len() < 2 { - return Err(CubeError::user(format!("Invalid symbol path: {}", path))); + let parts: Vec = path.split('.').map(|s| s.to_string()).collect(); + Self::parse_parts(cube_evaluator, None, &parts) + } + + pub fn parse_parts( + cube_evaluator: Rc, + current_cube: Option<&str>, + parts: &[String], + ) -> Result { + Self::resolve_parts(cube_evaluator, current_cube, parts, vec![]) + } + + fn resolve_parts( + cube_evaluator: Rc, + current_cube: Option<&str>, + parts: &[String], + path: Vec, + ) -> Result { + if parts.is_empty() { + return Err(CubeError::user("Empty path".to_string())); } - if let Some(dim_path) = - Self::try_parse_as_dimension_with_granularity(cube_evaluator.clone(), &parts)? - { - return Ok(dim_path); + // Step 1: If current_cube set, try resolving parts[0] as member + if let Some(cube_name) = current_cube { + if let Some(result) = + Self::try_resolve_as_member(cube_evaluator.clone(), cube_name, parts, &path)? + { + return Ok(result); + } } - let path_to_check = vec![ - parts[parts.len() - 2].to_string(), - parts[parts.len() - 1].to_string(), - ]; - - let path_type = if cube_evaluator.is_dimension(path_to_check.clone())? { - SymbolPathType::Dimension - } else if cube_evaluator.is_measure(path_to_check.clone())? { - SymbolPathType::Measure - } else if cube_evaluator.is_segment(path_to_check.clone())? { - SymbolPathType::Segment - } else { - return Err(CubeError::user(format!( - "Symbol path doesn't refer to a dimension, measure or segment: {}", - path + // Step 2: Try resolving parts[0] as cube reference + let resolved = Self::resolve_cube_name(cube_evaluator.clone(), current_cube, &parts[0])?; + + if let Some(cube_name) = resolved { + let mut new_path = path; + new_path.push(cube_name.clone()); + + if parts.len() == 1 { + return Ok(Self::new( + SymbolPathType::CubeName, + new_path, + cube_name, + String::new(), + None, + )); + } + if parts.len() >= 2 && parts[1] == "__sql_fn" { + return Ok(Self::new( + SymbolPathType::CubeTable, + new_path, + cube_name, + String::new(), + None, + )); + } + return Self::resolve_parts(cube_evaluator, Some(&cube_name), &parts[1..], new_path); + } + + Err(CubeError::user(format!("Cannot resolve: {}", parts[0]))) + } + + fn try_resolve_as_member( + evaluator: Rc, + cube_name: &str, + parts: &[String], + path: &[String], + ) -> Result, CubeError> { + let check_path = vec![cube_name.to_string(), parts[0].clone()]; + + if evaluator.is_dimension(check_path.clone())? { + if parts.len() == 1 { + return Ok(Some(Self::new( + SymbolPathType::Dimension, + path.to_vec(), + cube_name.to_string(), + parts[0].clone(), + None, + ))); + } + if parts.len() == 2 { + let dim = evaluator.dimension_by_path(format!("{}.{}", cube_name, parts[0]))?; + if dim.static_data().dimension_type == "time" { + return Ok(Some(Self::new( + SymbolPathType::Dimension, + path.to_vec(), + cube_name.to_string(), + parts[0].clone(), + Some(parts[1].clone()), + ))); + } + } + // Dimension with extra parts (non-time) — not a member match + return Ok(None); + } + + // Measures/segments can't have extra parts + if parts.len() > 1 { + return Ok(None); + } + + if evaluator.is_measure(check_path.clone())? { + return Ok(Some(Self::new( + SymbolPathType::Measure, + path.to_vec(), + cube_name.to_string(), + parts[0].clone(), + None, ))); - }; + } + if evaluator.is_segment(check_path)? { + return Ok(Some(Self::new( + SymbolPathType::Segment, + path.to_vec(), + cube_name.to_string(), + parts[0].clone(), + None, + ))); + } + Ok(None) + } - let path = parts[0..parts.len() - 2] - .iter() - .map(|s| s.to_string()) - .collect(); - Ok(Self::new( - path_type, - path, - path_to_check[0].clone(), - path_to_check[1].clone(), - None, - )) + fn resolve_cube_name( + evaluator: Rc, + current_cube: Option<&str>, + name: &str, + ) -> Result, CubeError> { + if matches!(name, "CUBE" | "TABLE") { + return Ok(current_cube.map(|s| s.to_string())); + } + if evaluator.cube_exists(name.to_string())? { + return Ok(Some(name.to_string())); + } + Ok(None) } pub fn path_type(&self) -> &SymbolPathType { @@ -114,35 +214,6 @@ impl SymbolPath { pub fn granularity(&self) -> &Option { &self.granularity } - - fn try_parse_as_dimension_with_granularity( - cube_evaluator: Rc, - parts: &[&str], - ) -> Result, CubeError> { - if parts.len() > 2 { - let path_to_check = vec![ - parts[parts.len() - 3].to_string(), - parts[parts.len() - 2].to_string(), - ]; - if cube_evaluator.is_dimension(path_to_check.clone())? { - let path_type = SymbolPathType::Dimension; - let path = parts[0..parts.len() - 3] - .iter() - .map(|s| s.to_string()) - .collect(); - - let granularity = Some(parts[parts.len() - 1].to_string()); - return Ok(Some(Self::new( - path_type, - path, - path_to_check[0].clone(), - path_to_check[1].clone(), - granularity, - ))); - } - } - Ok(None) - } } #[cfg(test)] @@ -151,8 +222,7 @@ mod tests { use crate::test_fixtures::cube_bridge::{MockCubeEvaluator, MockSchema}; use indoc::indoc; - #[test] - fn test_symbol_path_parsing() { + fn create_test_evaluator() -> Rc { let schema = MockSchema::from_yaml(indoc! {" cubes: - name: users @@ -164,9 +234,15 @@ mod tests { - name: id type: number sql: id + - name: source + type: string + sql: source measures: - name: count type: count + segments: + - name: google + sql: \"{CUBE.source} = 'google'\" - name: orders sql: SELECT * FROM orders dimensions: @@ -179,55 +255,187 @@ mod tests { sql: amount "}) .unwrap(); + Rc::new(MockCubeEvaluator::new(schema)) + } - let evaluator = Rc::new(MockCubeEvaluator::new(schema)); - + #[test] + fn test_parse_simple_dimension() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "users.created_at").unwrap(); assert_eq!(result.cube_name(), "users"); assert_eq!(result.symbol_name(), "created_at"); assert_eq!(result.full_name(), "users.created_at"); - assert_eq!(result.path(), &Vec::::new()); + assert_eq!(result.path(), &vec!["users".to_string()]); assert_eq!(result.granularity(), &None); - assert!(matches!(result.path_type, SymbolPathType::Dimension)); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + } + #[test] + fn test_parse_cross_cube_dimension() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "orders.users.created_at").unwrap(); assert_eq!(result.cube_name(), "users"); assert_eq!(result.symbol_name(), "created_at"); assert_eq!(result.full_name(), "users.created_at"); - assert_eq!(result.path(), &vec!["orders".to_string()]); + assert_eq!( + result.path(), + &vec!["orders".to_string(), "users".to_string()] + ); assert_eq!(result.granularity(), &None); - assert!(matches!(result.path_type, SymbolPathType::Dimension)); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + } + #[test] + fn test_parse_time_dimension_with_granularity() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "users.created_at.day").unwrap(); assert_eq!(result.cube_name(), "users"); assert_eq!(result.symbol_name(), "created_at"); assert_eq!(result.full_name(), "users.created_at"); - assert_eq!(result.path(), &Vec::::new()); + assert_eq!(result.path(), &vec!["users".to_string()]); assert_eq!(result.granularity(), &Some("day".to_string())); - assert!(matches!(result.path_type, SymbolPathType::Dimension)); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + } + #[test] + fn test_parse_cross_cube_time_dimension_with_granularity() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "orders.users.created_at.day").unwrap(); assert_eq!(result.cube_name(), "users"); assert_eq!(result.symbol_name(), "created_at"); assert_eq!(result.full_name(), "users.created_at"); - assert_eq!(result.path(), &vec!["orders".to_string()]); + assert_eq!( + result.path(), + &vec!["orders".to_string(), "users".to_string()] + ); assert_eq!(result.granularity(), &Some("day".to_string())); - assert!(matches!(result.path_type, SymbolPathType::Dimension)); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + } + #[test] + fn test_parse_measure() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "users.count").unwrap(); assert_eq!(result.cube_name(), "users"); assert_eq!(result.symbol_name(), "count"); assert_eq!(result.full_name(), "users.count"); - assert_eq!(result.path(), &Vec::::new()); + assert_eq!(result.path(), &vec!["users".to_string()]); assert_eq!(result.granularity(), &None); - assert!(matches!(result.path_type, SymbolPathType::Measure)); + assert_eq!(result.path_type(), &SymbolPathType::Measure); + } + #[test] + fn test_parse_cross_cube_measure() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "users.orders.total").unwrap(); assert_eq!(result.cube_name(), "orders"); assert_eq!(result.symbol_name(), "total"); assert_eq!(result.full_name(), "orders.total"); - assert_eq!(result.path(), &vec!["users".to_string()]); + assert_eq!( + result.path(), + &vec!["users".to_string(), "orders".to_string()] + ); assert_eq!(result.granularity(), &None); - assert!(matches!(result.path_type, SymbolPathType::Measure)); + assert_eq!(result.path_type(), &SymbolPathType::Measure); + } + + #[test] + fn test_parse_parts_cube_alias() { + let evaluator = create_test_evaluator(); + let parts = vec!["CUBE".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::CubeName); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.path(), &vec!["users".to_string()]); + } + + #[test] + fn test_parse_parts_table_alias() { + let evaluator = create_test_evaluator(); + let parts = vec!["TABLE".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::CubeName); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.path(), &vec!["users".to_string()]); + } + + #[test] + fn test_parse_parts_cube_table() { + let evaluator = create_test_evaluator(); + let parts = vec!["CUBE".to_string(), "__sql_fn".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::CubeTable); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.path(), &vec!["users".to_string()]); + } + + #[test] + fn test_parse_parts_simple_member() { + let evaluator = create_test_evaluator(); + let parts = vec!["source".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.symbol_name(), "source"); + assert_eq!(result.path(), &Vec::::new()); + } + + #[test] + fn test_parse_parts_time_dimension_with_granularity() { + let evaluator = create_test_evaluator(); + let parts = vec!["created_at".to_string(), "day".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.symbol_name(), "created_at"); + assert_eq!(result.granularity(), &Some("day".to_string())); + assert_eq!(result.path(), &Vec::::new()); + } + + #[test] + fn test_parse_parts_cross_cube_member() { + let evaluator = create_test_evaluator(); + let parts = vec!["orders".to_string(), "total".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::Measure); + assert_eq!(result.cube_name(), "orders"); + assert_eq!(result.symbol_name(), "total"); + assert_eq!(result.path(), &vec!["orders".to_string()]); + } + + #[test] + fn test_parse_parts_measure() { + let evaluator = create_test_evaluator(); + let parts = vec!["count".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::Measure); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.symbol_name(), "count"); + assert_eq!(result.path(), &Vec::::new()); + } + + #[test] + fn test_parse_parts_segment() { + let evaluator = create_test_evaluator(); + let parts = vec!["google".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::Segment); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.symbol_name(), "google"); + assert_eq!(result.path(), &Vec::::new()); + } + + #[test] + fn test_cache_name_without_granularity() { + let evaluator = create_test_evaluator(); + let result = SymbolPath::parse(evaluator.clone(), "users.count").unwrap(); + assert_eq!(result.cache_name(), "users.count"); + } + + #[test] + fn test_cache_name_with_granularity() { + let evaluator = create_test_evaluator(); + let result = SymbolPath::parse(evaluator.clone(), "users.created_at.day").unwrap(); + assert_eq!(result.cache_name(), "users.created_at.day"); } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/cube_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/cube_symbol.rs index f2ac27522b0c1..e1d48fc34821e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/cube_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/cube_symbol.rs @@ -12,11 +12,20 @@ use std::rc::Rc; #[derive(Debug)] pub struct CubeNameSymbol { cube_name: String, + path: Vec, } impl CubeNameSymbol { - pub fn new(cube_name: String) -> Rc { - Rc::new(Self { cube_name }) + pub fn new(cube_name: String, path: Vec) -> Rc { + let path = Self::normalize_path(path, &cube_name); + Rc::new(Self { cube_name, path }) + } + + pub(crate) fn normalize_path(mut path: Vec, cube_name: &str) -> Vec { + if path.is_empty() || path.last().map(|s| s.as_str()) != Some(cube_name) { + path.push(cube_name.to_string()); + } + path } pub fn evaluate_sql(&self) -> Result { @@ -25,6 +34,9 @@ impl CubeNameSymbol { pub fn cube_name(&self) -> &String { &self.cube_name } + pub fn path(&self) -> &Vec { + &self.path + } pub fn alias(&self) -> String { PlanSqlTemplates::alias_name(&self.cube_name) } @@ -32,30 +44,34 @@ impl CubeNameSymbol { pub struct CubeNameSymbolFactory { cube_name: String, + path: Vec, } impl CubeNameSymbolFactory { pub fn try_new( full_name: &String, _cube_evaluator: Rc, + path: Vec, ) -> Result { //TODO check that cube exists Ok(Self { cube_name: full_name.clone(), + path, }) } } impl CubeNameSymbolFactory { pub fn build(self, _compiler: &mut Compiler) -> Result, CubeError> { - let Self { cube_name } = self; - Ok(CubeNameSymbol::new(cube_name)) + let Self { cube_name, path } = self; + Ok(CubeNameSymbol::new(cube_name, path)) } } #[derive(Debug)] pub struct CubeTableSymbol { cube_name: String, + path: Vec, member_sql: Option>, alias: String, is_table_sql: bool, @@ -65,13 +81,16 @@ pub struct CubeTableSymbol { impl CubeTableSymbol { pub fn new( cube_name: String, + path: Vec, member_sql: Option>, alias: String, is_table_sql: bool, join_map: Option>>, ) -> Rc { + let path = CubeNameSymbol::normalize_path(path, &cube_name); Rc::new(Self { cube_name, + path, member_sql, alias, is_table_sql, @@ -118,6 +137,10 @@ impl CubeTableSymbol { &self.cube_name } + pub fn path(&self) -> &Vec { + &self.path + } + pub fn alias(&self) -> String { self.alias.clone() } @@ -129,6 +152,7 @@ impl CubeTableSymbol { pub struct CubeTableSymbolFactory { cube_name: String, + path: Vec, sql: Option>, definition: Rc, is_table_sql: bool, @@ -138,6 +162,7 @@ impl CubeTableSymbolFactory { pub fn try_new( cube_name: &String, cube_evaluator: Rc, + path: Vec, ) -> Result { let definition = cube_evaluator.cube_from_path(cube_name.clone())?; let table_sql = definition.sql_table()?; @@ -146,6 +171,7 @@ impl CubeTableSymbolFactory { let sql = table_sql.or(sql); Ok(Self { cube_name: cube_name.clone(), + path, sql, definition, is_table_sql, @@ -155,6 +181,7 @@ impl CubeTableSymbolFactory { pub fn build(self, compiler: &mut Compiler) -> Result, CubeError> { let Self { cube_name, + path, sql, definition, is_table_sql, @@ -171,6 +198,7 @@ impl CubeTableSymbolFactory { }; Ok(CubeTableSymbol::new( cube_name, + path, sql, alias, is_table_sql, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/case_dimension.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/case_dimension.rs index d903bd2a1795d..b84df2734a140 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/case_dimension.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/case_dimension.rs @@ -69,15 +69,6 @@ impl CaseDimension { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - if let Some(member_sql) = &self.member_sql { - member_sql.extract_symbol_deps_with_path(&mut deps); - } - self.case.extract_symbol_deps_with_path(&mut deps); - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/geo.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/geo.rs index 30096c1b5ed17..c5ebd21e700a8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/geo.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/geo.rs @@ -32,13 +32,6 @@ impl GeoDimension { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - self.latitude.extract_symbol_deps_with_path(&mut deps); - self.longitude.extract_symbol_deps_with_path(&mut deps); - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/mod.rs index 0e7e8040ecccd..7ad346fcbff29 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/mod.rs @@ -58,15 +58,6 @@ impl DimensionKind { } } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - match self { - Self::Regular(r) => r.get_dependencies_with_path(), - Self::Geo(g) => g.get_dependencies_with_path(), - Self::Switch(s) => s.get_dependencies_with_path(), - Self::Case(c) => c.get_dependencies_with_path(), - } - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/regular.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/regular.rs index edcf8a549c088..c1a88920a70df 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/regular.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/regular.rs @@ -45,12 +45,6 @@ impl RegularDimension { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - self.member_sql.extract_symbol_deps_with_path(&mut deps); - deps - } - pub fn get_cube_refs(&self) -> Vec { self.member_sql.get_cube_refs() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/switch.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/switch.rs index 917e0e0c7b124..a8a8e2ed98bd2 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/switch.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/switch.rs @@ -51,14 +51,6 @@ impl SwitchDimension { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - if let Some(member_sql) = &self.member_sql { - member_sql.extract_symbol_deps_with_path(&mut deps); - } - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs index 2444bd9137cef..5954cb400f529 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs @@ -1,4 +1,5 @@ use super::common::Case; +use super::common::CompiledMemberPath; use super::dimension_kinds::{ CaseDimension, DimensionKind, GeoDimension, RegularDimension, SwitchDimension, }; @@ -8,10 +9,10 @@ use crate::cube_bridge::dimension_definition::DimensionDefinition; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::member_sql::MemberSql; use crate::planner::query_tools::QueryTools; +use crate::planner::sql_evaluator::TimeDimensionSymbol; use crate::planner::sql_evaluator::{ sql_nodes::SqlNode, Compiler, CubeRef, SqlCall, SqlEvaluatorVisitor, }; -use crate::planner::sql_evaluator::{CubeTableSymbol, TimeDimensionSymbol}; use crate::planner::sql_templates::PlanSqlTemplates; use crate::planner::GranularityHelper; use crate::planner::SqlInterval; @@ -27,10 +28,8 @@ pub struct CalendarDimensionTimeShift { #[derive(Clone)] pub struct DimensionSymbol { - cube: Rc, - name: String, + compiled_path: CompiledMemberPath, kind: DimensionKind, - alias: String, is_reference: bool, // Symbol is a direct reference to another symbol without any calculations is_view: bool, add_group_by: Option>>, @@ -44,10 +43,8 @@ pub struct DimensionSymbol { impl DimensionSymbol { pub fn new( - cube: Rc, - name: String, + compiled_path: CompiledMemberPath, kind: DimensionKind, - alias: String, is_reference: bool, is_view: bool, add_group_by: Option>>, @@ -59,10 +56,8 @@ impl DimensionSymbol { propagate_filters_to_sub_query: bool, ) -> Rc { Rc::new(Self { - cube, - name, + compiled_path, kind, - alias, is_reference, is_view, add_group_by, @@ -83,8 +78,8 @@ impl DimensionSymbol { templates: &PlanSqlTemplates, ) -> Result { self.kind.evaluate_sql( - &self.name, - &self.full_name(), + self.compiled_path.name(), + self.compiled_path.full_name(), visitor, node_processor, query_tools, @@ -139,12 +134,20 @@ impl DimensionSymbol { self.time_shift_pk_full_name.clone() } + pub fn compiled_path(&self) -> &CompiledMemberPath { + &self.compiled_path + } + + pub fn strip_join_prefix(&mut self) { + self.compiled_path = self.compiled_path.strip_join_prefix(); + } + pub fn full_name(&self) -> String { - format!("{}.{}", self.cube.cube_name(), self.name) + self.compiled_path.full_name().clone() } pub fn alias(&self) -> String { - self.alias.clone() + self.compiled_path.alias().clone() } pub fn owned_by_cube(&self) -> bool { @@ -227,24 +230,24 @@ impl DimensionSymbol { self.kind.get_dependencies() } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - self.kind.get_dependencies_with_path() - } - pub fn get_cube_refs(&self) -> Vec { self.kind.get_cube_refs() } - pub fn cube_name(&self) -> &String { - self.cube.cube_name() + pub fn cube_name(&self) -> String { + self.compiled_path.cube_name().clone() } pub fn join_map(&self) -> &Option>> { - self.cube.join_map() + self.compiled_path.join_map() } - pub fn name(&self) -> &String { - &self.name + pub fn name(&self) -> String { + self.compiled_path.name().clone() + } + + pub fn path(&self) -> &Vec { + self.compiled_path.path() } pub fn calendar_time_shift_for_interval( @@ -468,13 +471,19 @@ impl SymbolFactory for DimensionSymbolFactory { .propagate_filters_to_sub_query .unwrap_or(false); - let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone())?; + let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone(), vec![])?; - let symbol = MemberSymbol::new_dimension(DimensionSymbol::new( + let compiled_path = CompiledMemberPath::new( cube_symbol, + path.full_name().clone(), path.symbol_name().clone(), - kind, alias, + path.path().clone(), + ); + + let symbol = MemberSymbol::new_dimension(DimensionSymbol::new( + compiled_path, + kind, is_reference, is_view, add_group_by, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/aggregated.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/aggregated.rs index b04208e28a6ca..0a4593bd28567 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/aggregated.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/aggregated.rs @@ -58,14 +58,6 @@ impl AggregatedMeasure { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - if let Some(sql) = &self.member_sql { - sql.extract_symbol_deps_with_path(&mut deps); - } - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/calculated.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/calculated.rs index d5909956f85ef..95e3b3b32dab8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/calculated.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/calculated.rs @@ -86,14 +86,6 @@ impl CalculatedMeasure { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - if let Some(sql) = &self.member_sql { - sql.extract_symbol_deps_with_path(&mut deps); - } - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/count.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/count.rs index d49bfb849ed3f..d43b9e9d9ca17 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/count.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/count.rs @@ -72,19 +72,6 @@ impl CountMeasure { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - match &self.sql { - CountSql::Explicit(sql) => sql.extract_symbol_deps_with_path(&mut deps), - CountSql::Auto(pk_sqls) => { - for pk in pk_sqls { - pk.extract_symbol_deps_with_path(&mut deps); - } - } - } - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/mod.rs index 7c75d4580194f..5e2ba9ad77d1c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/mod.rs @@ -90,15 +90,6 @@ impl MeasureKind { } } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - match self { - Self::Count(c) => c.get_dependencies_with_path(), - Self::Aggregated(a) => a.get_dependencies_with_path(), - Self::Calculated(c) => c.get_dependencies_with_path(), - Self::Rank => vec![], - } - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs index 444299b749d7c..bcda67f7e4497 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs @@ -1,4 +1,4 @@ -use super::common::{AggregationType, Case}; +use super::common::{AggregationType, Case, CompiledMemberPath}; use super::measure_kinds::{CalculatedMeasure, CalculatedMeasureType, MeasureKind}; use super::SymbolPath; use super::{MemberSymbol, SymbolFactory}; @@ -7,7 +7,6 @@ use crate::cube_bridge::measure_definition::{MeasureDefinition, RollingWindow}; use crate::cube_bridge::member_sql::MemberSql; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::collectors::find_owned_by_cube_child; -use crate::planner::sql_evaluator::CubeTableSymbol; use crate::planner::sql_evaluator::{ sql_nodes::SqlNode, Compiler, CubeRef, SqlCall, SqlEvaluatorVisitor, }; @@ -72,9 +71,7 @@ pub enum MeasureTimeShifts { #[derive(Clone)] pub struct MeasureSymbol { - cube: Rc, - name: String, - alias: String, + compiled_path: CompiledMemberPath, kind: MeasureKind, rolling_window: Option, is_multi_stage: bool, @@ -93,9 +90,7 @@ pub struct MeasureSymbol { impl MeasureSymbol { pub fn new( - cube: Rc, - name: String, - alias: String, + compiled_path: CompiledMemberPath, is_reference: bool, is_view: bool, case: Option, @@ -111,9 +106,7 @@ impl MeasureSymbol { group_by: Option>>, ) -> Rc { Rc::new(Self { - cube, - name, - alias, + compiled_path, is_reference, is_view, case, @@ -148,9 +141,7 @@ impl MeasureSymbol { self.kind.clone() }; Rc::new(Self { - cube: self.cube.clone(), - name: self.name.clone(), - alias: self.alias.clone(), + compiled_path: self.compiled_path.clone(), kind, rolling_window: None, is_multi_stage: false, @@ -180,7 +171,7 @@ impl MeasureSymbol { if !self.kind.can_replace_type_with(&new_measure_type) { return Err(CubeError::user(format!( "Unsupported measure type replacement for {}: {} => {}", - self.name, + self.compiled_path.name(), self.kind.measure_type_str(), new_measure_type ))); @@ -195,16 +186,14 @@ impl MeasureSymbol { if !result_kind.supports_additional_filters() { return Err(CubeError::user(format!( "Unsupported additional filters for measure {} type {}", - self.name, + self.compiled_path.name(), result_kind.measure_type_str() ))); } measure_filters.extend(add_filters); } Ok(Rc::new(Self { - cube: self.cube.clone(), - name: self.name.clone(), - alias: self.alias.clone(), + compiled_path: self.compiled_path.clone(), kind: result_kind, rolling_window: self.rolling_window.clone(), is_multi_stage: self.is_multi_stage, @@ -228,12 +217,20 @@ impl MeasureSymbol { Rc::new(new) } + pub fn compiled_path(&self) -> &CompiledMemberPath { + &self.compiled_path + } + + pub fn strip_join_prefix(&mut self) { + self.compiled_path = self.compiled_path.strip_join_prefix(); + } + pub fn full_name(&self) -> String { - format!("{}.{}", self.cube.cube_name(), self.name) + self.compiled_path.full_name().clone() } pub fn alias(&self) -> String { - self.alias.clone() + self.compiled_path.alias().clone() } pub fn is_splitted_source(&self) -> bool { @@ -346,23 +343,6 @@ impl MeasureSymbol { refs } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = self.kind.get_dependencies_with_path(); - for filter in self.measure_filters.iter() { - filter.extract_symbol_deps_with_path(&mut deps); - } - for filter in self.measure_drill_filters.iter() { - filter.extract_symbol_deps_with_path(&mut deps); - } - for order in self.measure_order_by.iter() { - order.sql_call().extract_symbol_deps_with_path(&mut deps); - } - if let Some(case) = &self.case { - case.extract_symbol_deps_with_path(&mut deps); - } - deps - } - pub fn can_used_as_addictive_in_multplied(&self) -> bool { match &self.kind { MeasureKind::Aggregated(agg) => agg.agg_type().is_distinct(), @@ -459,16 +439,20 @@ impl MeasureSymbol { self.is_multi_stage } - pub fn cube_name(&self) -> &String { - &self.cube.cube_name() + pub fn cube_name(&self) -> String { + self.compiled_path.cube_name().clone() } pub fn join_map(&self) -> &Option>> { - self.cube.join_map() + self.compiled_path.join_map() } - pub fn name(&self) -> &String { - &self.name + pub fn name(&self) -> String { + self.compiled_path.name().clone() + } + + pub fn path(&self) -> &Vec { + self.compiled_path.path() } } @@ -593,7 +577,7 @@ impl SymbolFactory for MeasureSymbolFactory { } } else { shifts.insert( - dimension_name, + dimension_name.clone(), DimensionTimeShift { interval: interval.clone(), name: name.clone(), @@ -728,12 +712,18 @@ impl SymbolFactory for MeasureSymbolFactory { && add_group_by.is_none() && group_by.is_none()); - let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone())?; + let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone(), vec![])?; - Ok(MemberSymbol::new_measure(MeasureSymbol::new( + let compiled_path = CompiledMemberPath::new( cube_symbol, + path.full_name().clone(), path.symbol_name().clone(), alias, + path.path().clone(), + ); + + Ok(MemberSymbol::new_measure(MeasureSymbol::new( + compiled_path, is_reference, is_view, case, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs index fbe71cf49b536..69b218bad843f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs @@ -1,8 +1,10 @@ +use super::common::CompiledMemberPath; use super::MemberSymbol; -use crate::cube_bridge::base_tools::BaseTools; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::collectors::member_childs; -use crate::planner::sql_evaluator::{sql_nodes::SqlNode, CubeRef, SqlCall, SqlEvaluatorVisitor}; +use crate::planner::sql_evaluator::{ + sql_nodes::SqlNode, CubeRef, CubeTableSymbol, SqlCall, SqlEvaluatorVisitor, +}; use crate::planner::sql_templates::PlanSqlTemplates; use crate::utils::debug::DebugSql; use cubenativeutils::CubeError; @@ -17,9 +19,7 @@ pub enum MemberExpressionExpression { #[derive(Clone)] pub struct MemberExpressionSymbol { - cube_name: String, - name: String, - alias: String, + compiled_path: CompiledMemberPath, expression: MemberExpressionExpression, #[allow(dead_code)] definition: Option, @@ -29,22 +29,22 @@ pub struct MemberExpressionSymbol { impl MemberExpressionSymbol { pub fn try_new( - cube_name: String, + cube: Rc, name: String, expression: MemberExpressionExpression, definition: Option, alias: Option, - _base_tools: Rc, + path: Vec, ) -> Result, CubeError> { + let full_name = format!("expr:{}.{}", cube.cube_name(), name); let alias = alias.unwrap_or_else(|| PlanSqlTemplates::alias_name(&name)); let is_reference = match &expression { MemberExpressionExpression::SqlCall(sql_call) => sql_call.is_direct_reference(), MemberExpressionExpression::PatchedSymbol(_symbol) => false, }; + let compiled_path = CompiledMemberPath::new(cube, full_name, name, alias, path); Ok(Rc::new(Self { - cube_name, - name, - alias, + compiled_path, expression, definition, is_reference, @@ -80,12 +80,20 @@ impl MemberExpressionSymbol { Rc::new(result) } + pub fn compiled_path(&self) -> &CompiledMemberPath { + &self.compiled_path + } + + pub fn strip_join_prefix(&mut self) { + self.compiled_path = self.compiled_path.strip_join_prefix(); + } + pub fn full_name(&self) -> String { - format!("expr:{}.{}", self.cube_name, self.name) + self.compiled_path.full_name().clone() } pub fn alias(&self) -> String { - self.alias.clone() + self.compiled_path.alias().clone() } pub fn is_reference(&self) -> bool { @@ -133,19 +141,6 @@ impl MemberExpressionSymbol { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - match &self.expression { - MemberExpressionExpression::SqlCall(sql_call) => { - sql_call.extract_symbol_deps_with_path(&mut deps) - } - MemberExpressionExpression::PatchedSymbol(member_symbol) => { - deps.push((member_symbol.clone(), vec![])) - } - } - deps - } - pub fn get_cube_refs(&self) -> Vec { let mut refs = vec![]; match &self.expression { @@ -170,12 +165,16 @@ impl MemberExpressionSymbol { } } - pub fn cube_name(&self) -> &String { - &self.cube_name + pub fn cube_name(&self) -> String { + self.compiled_path.cube_name().clone() + } + + pub fn name(&self) -> String { + self.compiled_path.name().clone() } - pub fn name(&self) -> &String { - &self.name + pub fn path(&self) -> &Vec { + self.compiled_path.path() } pub fn definition(&self) -> &Option { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs index ddb7887d79e35..921da1fee1741 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs @@ -4,6 +4,7 @@ use itertools::Itertools; use crate::planner::sql_evaluator::collectors::has_multi_stage_members; use crate::planner::sql_evaluator::{Case, CubeRef, SqlCall}; +use super::common::CompiledMemberPath; use super::{DimensionSymbol, MeasureSymbol, MemberExpressionSymbol, TimeDimensionSymbol}; use std::fmt::Debug; use std::rc::Rc; @@ -34,15 +35,8 @@ impl Debug for MemberSymbol { impl PartialEq for MemberSymbol { fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::Dimension(l0), Self::Dimension(r0)) => l0.full_name() == r0.full_name(), - (Self::TimeDimension(l0), Self::TimeDimension(r0)) => l0.full_name() == r0.full_name(), - (Self::Measure(l0), Self::Measure(r0)) => l0.full_name() == r0.full_name(), - (Self::MemberExpression(l0), Self::MemberExpression(r0)) => { - l0.full_name() == r0.full_name() - } - _ => false, - } + self.full_name() == other.full_name() + && std::mem::discriminant(self) == std::mem::discriminant(other) } } @@ -63,40 +57,37 @@ impl MemberSymbol { Rc::new(Self::TimeDimension(symbol)) } - pub fn full_name(&self) -> String { + pub fn compiled_path(&self) -> &CompiledMemberPath { match self { - Self::Dimension(d) => d.full_name(), - Self::TimeDimension(d) => d.full_name(), - Self::Measure(m) => m.full_name(), - Self::MemberExpression(e) => e.full_name().clone(), + Self::Dimension(d) => d.compiled_path(), + Self::TimeDimension(d) => d.compiled_path(), + Self::Measure(m) => m.compiled_path(), + Self::MemberExpression(e) => e.compiled_path(), } } + pub fn full_name(&self) -> String { + self.compiled_path().full_name().clone() + } + pub fn alias(&self) -> String { - match self { - Self::Dimension(d) => d.alias(), - Self::TimeDimension(d) => d.alias(), - Self::Measure(m) => m.alias(), - Self::MemberExpression(e) => e.alias(), - } + self.compiled_path().alias().clone() } pub fn name(&self) -> String { - match self { - Self::Dimension(d) => d.name().clone(), - Self::TimeDimension(d) => d.name(), - Self::Measure(m) => m.name().clone(), - Self::MemberExpression(e) => e.name().clone(), - } + self.compiled_path().name().clone() } pub fn cube_name(&self) -> String { - match self { - Self::Dimension(d) => d.cube_name().clone(), - Self::TimeDimension(d) => d.cube_name(), - Self::Measure(m) => m.cube_name().clone(), - Self::MemberExpression(e) => e.cube_name().clone(), - } + self.compiled_path().cube_name().clone() + } + + pub fn path(&self) -> &Vec { + self.compiled_path().path() + } + + pub fn join_map(&self) -> &Option>> { + self.compiled_path().join_map() } pub fn is_multi_stage(&self) -> bool { @@ -156,15 +147,6 @@ impl MemberSymbol { } } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - match self { - Self::Dimension(d) => d.get_dependencies_with_path(), - Self::TimeDimension(d) => d.get_dependencies_with_path(), - Self::Measure(m) => m.get_dependencies_with_path(), - Self::MemberExpression(e) => e.get_dependencies_with_path(), - } - } - pub fn get_cube_refs(&self) -> Vec { match self { Self::Dimension(d) => d.get_cube_refs(), @@ -215,6 +197,33 @@ impl MemberSymbol { false } + /// Returns a copy of this symbol with the path reduced to just the owning cube, + /// stripping any join chain prefix (e.g. from views or cross-cube references). + pub fn with_stripped_join_prefix(&self) -> Rc { + match self { + Self::Dimension(d) => { + let mut new = (**d).clone(); + new.strip_join_prefix(); + Rc::new(Self::Dimension(Rc::new(new))) + } + Self::TimeDimension(d) => { + let mut new = (**d).clone(); + new.strip_join_prefix(); + Rc::new(Self::TimeDimension(Rc::new(new))) + } + Self::Measure(m) => { + let mut new = (**m).clone(); + new.strip_join_prefix(); + Rc::new(Self::Measure(Rc::new(new))) + } + Self::MemberExpression(e) => { + let mut new = (**e).clone(); + new.strip_join_prefix(); + Rc::new(Self::MemberExpression(Rc::new(new))) + } + } + } + pub fn owned_by_cube(&self) -> bool { match self { Self::Dimension(d) => d.owned_by_cube(), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs index b449fe1e473e2..35003830ae56d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs @@ -1,3 +1,4 @@ +use super::common::CompiledMemberPath; use super::MemberSymbol; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::CubeRef; @@ -11,8 +12,7 @@ use std::rc::Rc; #[derive(Clone)] pub struct TimeDimensionSymbol { base_symbol: Rc, - full_name: String, - alias: String, + compiled_path: CompiledMemberPath, granularity: Option, granularity_obj: Option, date_range: Option<(String, String)>, @@ -33,12 +33,18 @@ impl TimeDimensionSymbol { }; let full_name = format!("{}_{}", base_symbol.full_name(), name_suffix); let alias = format!("{}_{}", base_symbol.alias(), name_suffix); + let compiled_path = CompiledMemberPath::new( + base_symbol.compiled_path().cube().clone(), + full_name, + base_symbol.name().clone(), + alias, + base_symbol.path().clone(), + ); Rc::new(Self { base_symbol, - alias, + compiled_path, granularity, granularity_obj, - full_name, date_range, alias_suffix: name_suffix, }) @@ -94,8 +100,16 @@ impl TimeDimensionSymbol { Ok(result) } + pub fn compiled_path(&self) -> &CompiledMemberPath { + &self.compiled_path + } + + pub fn strip_join_prefix(&mut self) { + self.compiled_path = self.compiled_path.strip_join_prefix(); + } + pub fn full_name(&self) -> String { - self.full_name.clone() + self.compiled_path.full_name().clone() } pub fn alias_suffix(&self) -> String { @@ -103,7 +117,7 @@ impl TimeDimensionSymbol { } pub fn alias(&self) -> String { - self.alias.clone() + self.compiled_path.alias().clone() } pub fn owned_by_cube(&self) -> bool { @@ -160,18 +174,6 @@ impl TimeDimensionSymbol { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - if let Some(granularity_obj) = &self.granularity_obj { - if let Some(calendar_sql) = granularity_obj.calendar_sql() { - calendar_sql.extract_symbol_deps_with_path(&mut deps); - } - } - - deps.append(&mut self.base_symbol.get_dependencies_with_path()); - deps - } - pub fn get_cube_refs(&self) -> Vec { let mut refs = vec![]; if let Some(granularity_obj) = &self.granularity_obj { @@ -184,14 +186,15 @@ impl TimeDimensionSymbol { } pub fn cube_name(&self) -> String { - self.base_symbol.cube_name() + self.compiled_path.cube_name().clone() + } + + pub fn path(&self) -> &Vec { + self.compiled_path.path() } pub fn join_map(&self) -> &Option>> { - match self.base_symbol.as_ref() { - MemberSymbol::Dimension(d) => d.join_map(), - _ => &None, - } + self.compiled_path.join_map() } pub fn is_multi_stage(&self) -> bool { @@ -223,7 +226,7 @@ impl TimeDimensionSymbol { } pub fn name(&self) -> String { - self.base_symbol.name() + self.compiled_path.name().clone() } pub fn date_range_granularity( diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs index df1c324c37c4f..26cba72a006b5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs @@ -7,7 +7,6 @@ pub trait TraversalVisitor { fn on_node_traverse( &mut self, node: &Rc, - path: &Vec, state: &Self::State, ) -> Result, CubeError>; @@ -16,18 +15,9 @@ pub trait TraversalVisitor { } fn apply(&mut self, node: &Rc, state: &Self::State) -> Result<(), CubeError> { - self.apply_with_path(node, &vec![], state) - } - - fn apply_with_path( - &mut self, - node: &Rc, - path: &Vec, - state: &Self::State, - ) -> Result<(), CubeError> { - if let Some(state) = self.on_node_traverse(node, path, state)? { - for (dep, dep_path) in node.get_dependencies_with_path() { - self.apply_with_path(&dep, &dep_path, &state)? + if let Some(state) = self.on_node_traverse(node, state)? { + for dep in node.get_dependencies() { + self.apply(&dep, &state)? } for cube_ref in node.get_cube_refs() { self.on_cube_ref(&cube_ref, &state)?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_join_graph.rs b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_join_graph.rs index 46101d6ca314c..1872eebc2e281 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_join_graph.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mock_join_graph.rs @@ -353,8 +353,8 @@ impl MockJoinGraph { pub(crate) fn check_if_cube_multiplied(&self, cube: &str, join: &JoinEdge) -> bool { let relationship = &join.join.static_data().relationship; - (join.from == cube && relationship == "hasMany") - || (join.to == cube && relationship == "belongsTo") + (join.from == cube && (relationship == "hasMany" || relationship == "one_to_many")) + || (join.to == cube && (relationship == "belongsTo" || relationship == "many_to_one")) } pub(crate) fn find_multiplication_factor_for(&self, cube: &str, joins: &[JoinEdge]) -> bool { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs index 6c0bc53e8cbb3..c468058ab1294 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs @@ -56,6 +56,7 @@ pub use mock_join_graph::MockJoinGraph; pub use mock_join_item::MockJoinItem; pub use mock_join_item_definition::MockJoinItemDefinition; pub use mock_measure_definition::MockMeasureDefinition; +pub use mock_member_expression_definition::MockMemberExpressionDefinition; pub use mock_member_order_by::MockMemberOrderBy; pub use mock_member_sql::MockMemberSql; pub use mock_pre_aggregation_description::MockPreAggregationDescription; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/many_to_one_views.yaml b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/many_to_one_views.yaml new file mode 100644 index 0000000000000..0d9aaf1faa1f5 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/many_to_one_views.yaml @@ -0,0 +1,65 @@ +cubes: + - name: many_to_one_root + sql: "SELECT * FROM many_to_one_root" + joins: + - name: many_to_one_child + relationship: many_to_one + sql: "{many_to_one_root}.child_id = {many_to_one_child.id}" + dimensions: + - name: id + type: number + sql: id + primary_key: true + - name: child_id + type: number + sql: child_id + - name: root_dim + type: string + sql: dim + - name: root_test_dim + type: string + sql: test_dim + measures: + - name: root_val_sum + type: sum + sql: val + - name: root_val_avg + type: avg + sql: val + + - name: many_to_one_child + sql: "SELECT * FROM many_to_one_child" + dimensions: + - name: id + type: number + sql: id + primary_key: true + - name: child_dim + type: string + sql: dim + - name: child_test_dim + type: string + sql: test_dim + measures: + - name: child_val_sum + type: sum + sql: val + - name: child_val_avg + type: avg + sql: val + +views: + - name: many_to_one_view + cubes: + - join_path: many_to_one_root + includes: + - root_dim + - root_test_dim + - root_val_sum + - root_val_avg + - join_path: many_to_one_root.many_to_one_child + includes: + - child_dim + - child_test_dim + - child_val_sum + - child_val_avg diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs index 66e4d36d209e8..77523563a54b0 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs @@ -53,6 +53,13 @@ impl TestContext { &self.query_tools } + #[allow(dead_code)] + pub fn security_context( + &self, + ) -> &Rc { + &self.security_context + } + #[allow(dead_code)] pub fn create_symbol(&self, member_path: &str) -> Result, CubeError> { self.query_tools @@ -87,18 +94,11 @@ impl TestContext { .query_tools .cube_evaluator() .segment_by_path(path.to_string())?; - let expression = self - .query_tools - .evaluator_compiler() - .borrow_mut() - .compile_sql_call(&cube_name, definition.sql()?)?; - BaseSegment::try_new( - expression, - cube_name, - name, - Some(path.to_string()), - self.query_tools.clone(), - ) + let mut compiler = self.query_tools.evaluator_compiler().borrow_mut(); + let expression = compiler.compile_sql_call(&cube_name, definition.sql()?)?; + let cube_symbol = compiler.add_cube_table_evaluator(cube_name.clone(), vec![])?; + drop(compiler); + BaseSegment::try_new(expression, cube_symbol, name, Some(path.to_string())) } #[allow(dead_code)] @@ -263,6 +263,18 @@ impl TestContext { let (sql, _) = self.build_sql_with_used_pre_aggregations(query)?; Ok(sql) } + + #[allow(dead_code)] + pub fn build_sql_from_options( + &self, + options: Rc, + ) -> Result { + let request = QueryProperties::try_new(self.query_tools.clone(), options)?; + let planner = TopLevelPlanner::new(request, self.query_tools.clone(), false); + let (sql, _) = planner.plan()?; + Ok(sql) + } + pub fn build_sql_with_used_pre_aggregations( &self, query: &str, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs index b9f8fc7dcfa88..77efa96d8421c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs @@ -33,7 +33,7 @@ fn test_simple_paths_in_request_sql() { let query_yaml = indoc! {" measures: - - cube_c.cube_a.count + - cube_a.count dimensions: - cube_a.cube_c.code "}; @@ -66,7 +66,6 @@ fn test_simple_paths_in_time_dimension_request_sql() { let sql = test_context .build_sql(query_yaml) .expect("Should generate SQL"); - println!("{}", sql); assert!( sql.contains(r#"ON "cube_a".c_id = "cube_c".id"#), @@ -105,6 +104,35 @@ fn test_diamond_join_over_view_sql() { insta::assert_snapshot!(sql); } +#[test] +fn test_diamond_join_over_direct_path_sql() { + let schema = MockSchema::from_yaml_file("common/diamond_joins.yaml"); + let test_context = TestContext::new(schema).unwrap(); + + let query_yaml = indoc! {" + measures: + - cube_a.count + dimensions: + - cube_a.cube_b.cube_c.code + "}; + + let sql = test_context + .build_sql(query_yaml) + .expect("Should generate SQL for simple join"); + + assert!( + sql.contains(r#"ON "cube_a".b_id = "cube_b".id"#), + "SQL should contain join condition between cube_a and cube_b" + ); + + assert!( + sql.contains(r#"ON "cube_b".c_id = "cube_c".id"#), + "SQL should contain join condition between cube_b and cube_c" + ); + + insta::assert_snapshot!(sql); +} + #[test] fn test_simple_segment_sql() { let schema = MockSchema::from_yaml_file("common/simple.yaml"); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/compiled_member_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/compiled_member_path.rs new file mode 100644 index 0000000000000..da35648d6d4b6 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/compiled_member_path.rs @@ -0,0 +1,216 @@ +use crate::planner::sql_evaluator::MemberSymbol; +use crate::test_fixtures::cube_bridge::MockSchema; +use crate::test_fixtures::test_utils::TestContext; +use std::rc::Rc; + +fn simple_ctx() -> TestContext { + TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap() +} + +fn visitors_ctx() -> TestContext { + TestContext::new(MockSchema::from_yaml_file("common/visitors.yaml")).unwrap() +} + +fn many_to_one_ctx() -> TestContext { + TestContext::new(MockSchema::from_yaml_file("common/many_to_one_views.yaml")).unwrap() +} + +fn dep_paths(symbol: &Rc) -> Vec<(String, Vec)> { + let mut result: Vec<_> = symbol + .get_dependencies() + .into_iter() + .map(|d| (d.full_name(), d.path().clone())) + .collect(); + result.sort_by(|a, b| a.0.cmp(&b.0)); + result +} + +// --- Simple dimension/measure path --- + +#[test] +fn test_simple_dimension_path() { + let ctx = simple_ctx(); + let dim = ctx.create_dimension("orders.status").unwrap(); + assert_eq!(dim.path(), &vec!["orders".to_string()]); + assert_eq!(dim.cube_name(), "orders"); +} + +#[test] +fn test_simple_measure_path() { + let ctx = simple_ctx(); + let m = ctx.create_measure("orders.count").unwrap(); + assert_eq!(m.path(), &vec!["orders".to_string()]); + assert_eq!(m.cube_name(), "orders"); +} + +// --- View paths --- + +#[test] +fn test_view_dimension_has_underlying_path() { + let ctx = simple_ctx(); + + // orders_with_customer.name → join_path: orders.customers + let dim = ctx.create_dimension("orders_with_customer.name").unwrap(); + assert!(dim.as_dimension().unwrap().is_view()); + assert_eq!(dim.path(), &vec!["orders_with_customer".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "customers.name"); + assert_eq!( + deps[0].1, + vec!["orders".to_string(), "customers".to_string()] + ); +} + +#[test] +fn test_many_to_one_view_child_has_underlying_path() { + let ctx = many_to_one_ctx(); + + // many_to_one_view.child_dim → join_path: many_to_one_root.many_to_one_child + let dim = ctx.create_dimension("many_to_one_view.child_dim").unwrap(); + assert!(dim.as_dimension().unwrap().is_view()); + assert_eq!(dim.path(), &vec!["many_to_one_view".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "many_to_one_child.child_dim"); + assert_eq!( + deps[0].1, + vec![ + "many_to_one_root".to_string(), + "many_to_one_child".to_string() + ] + ); +} + +// --- Dependencies from SQL templates: same cube --- + +#[test] +fn test_dep_path_same_cube_member_ref() { + // visitors.visitor_id_proxy: sql = "{visitors.visitor_id}" + let ctx = visitors_ctx(); + let dim = ctx.create_dimension("visitors.visitor_id_proxy").unwrap(); + assert_eq!(dim.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "visitors.visitor_id"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); +} + +#[test] +fn test_dep_path_short_member_ref() { + let ctx = visitors_ctx(); + let dim = ctx.create_dimension("visitors.visitor_id_twice").unwrap(); + assert_eq!(dim.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "visitors.visitor_id"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); +} + +// --- Dependencies from SQL templates: cross-cube --- + +#[test] +fn test_dep_path_cross_cube_member_ref() { + // visitors.minVisitorCheckinDate: sql = "{visitor_checkins.minDate}" + let ctx = visitors_ctx(); + let dim = ctx + .create_dimension("visitors.minVisitorCheckinDate") + .unwrap(); + assert_eq!(dim.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "visitor_checkins.minDate"); + assert_eq!(deps[0].1, vec!["visitor_checkins".to_string()]); +} + +// --- Dependencies from SQL: multiple refs --- + +#[test] +fn test_dep_path_multiple_refs_in_sql() { + let ctx = visitors_ctx(); + let dim = ctx.create_dimension("visitors.source_concat_id").unwrap(); + assert_eq!(dim.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 2); + assert_eq!(deps[0].0, "visitors.source"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); + assert_eq!(deps[1].0, "visitors.visitor_id"); + assert_eq!(deps[1].1, vec!["visitors".to_string()]); +} + +// --- Measure dependencies --- + +#[test] +fn test_dep_path_measure_self_ref() { + // visitors.total_revenue_proxy: sql = "{total_revenue}" + let ctx = visitors_ctx(); + let m = ctx.create_measure("visitors.total_revenue_proxy").unwrap(); + assert_eq!(m.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&m); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "visitors.total_revenue"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); +} + +#[test] +fn test_dep_path_measure_cross_cube_ref() { + // customers.payments_per_order: sql = "{payments} / {orders.count}" + let ctx = simple_ctx(); + let m = ctx.create_measure("customers.payments_per_order").unwrap(); + assert_eq!(m.path(), &vec!["customers".to_string()]); + + let deps = dep_paths(&m); + assert_eq!(deps.len(), 2); + assert_eq!(deps[0].0, "customers.payments"); + assert_eq!(deps[0].1, vec!["customers".to_string()]); + assert_eq!(deps[1].0, "orders.count"); + assert_eq!(deps[1].1, vec!["orders".to_string()]); +} + +#[test] +fn test_dep_path_measure_mixed_refs() { + let ctx = visitors_ctx(); + let m = ctx + .create_measure("visitors.total_revenue_per_count") + .unwrap(); + assert_eq!(m.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&m); + assert_eq!(deps.len(), 2); + assert_eq!(deps[0].0, "visitors.count"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); + assert_eq!(deps[1].0, "visitors.total_revenue"); + assert_eq!(deps[1].1, vec!["visitors".to_string()]); +} + +// --- Segment path --- + +#[test] +fn test_segment_compiled_path() { + let ctx = visitors_ctx(); + let seg = ctx.create_symbol("visitors.google").unwrap(); + assert_eq!(seg.path(), &vec!["visitors".to_string()]); + assert_eq!(seg.cube_name(), "visitors"); + + let deps = dep_paths(&seg); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "visitors.source"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); +} + +// --- Time dimension path --- + +#[test] +fn test_time_dimension_path() { + let ctx = simple_ctx(); + let td = ctx.create_dimension("orders.created_at.day").unwrap(); + assert_eq!(td.path(), &vec!["orders".to_string()]); + assert_eq!(td.cube_name(), "orders"); +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_evaluator/compilation.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_evaluator/compilation.rs index b0fcc9c61eb1c..0ec8136aacb25 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_evaluator/compilation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_evaluator/compilation.rs @@ -277,7 +277,7 @@ fn test_add_cube_table_evaluator() { let symbol = test_compiler .compiler - .add_cube_table_evaluator("visitors".to_string()) + .add_cube_table_evaluator("visitors".to_string(), vec![]) .unwrap(); assert_eq!(symbol.cube_name(), "visitors"); @@ -291,7 +291,7 @@ fn test_add_cube_name_evaluator() { let symbol = test_compiler .compiler - .add_cube_name_evaluator("visitors".to_string()) + .add_cube_name_evaluator("visitors".to_string(), vec![]) .unwrap(); assert_eq!(symbol.cube_name(), "visitors"); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_names_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_names_collector.rs new file mode 100644 index 0000000000000..e6d1e192ed74a --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_names_collector.rs @@ -0,0 +1,68 @@ +use crate::planner::sql_evaluator::collectors::collect_cube_names; +use crate::test_fixtures::cube_bridge::MockSchema; +use crate::test_fixtures::test_utils::TestContext; + +fn many_to_one_ctx() -> TestContext { + TestContext::new(MockSchema::from_yaml_file("common/many_to_one_views.yaml")).unwrap() +} + +#[test] +fn test_cube_names_single_cube_dimension() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + let dim = ctx.create_dimension("orders.status").unwrap(); + let names = collect_cube_names(&dim).unwrap(); + assert_eq!(names, vec!["orders"]); +} + +#[test] +fn test_cube_names_single_cube_measure() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + let measure = ctx.create_measure("orders.count").unwrap(); + let names = collect_cube_names(&measure).unwrap(); + assert_eq!(names, vec!["orders"]); +} + +#[test] +fn test_cube_names_cross_cube_measure() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + let measure = ctx.create_measure("customers.payments_per_order").unwrap(); + let mut names = collect_cube_names(&measure).unwrap(); + names.sort(); + assert_eq!(names, vec!["customers", "orders"]); +} + +#[test] +fn test_cube_names_many_to_one_view_root_dim() { + let ctx = many_to_one_ctx(); + let dim = ctx.create_dimension("many_to_one_view.root_dim").unwrap(); + let names = collect_cube_names(&dim).unwrap(); + assert_eq!(names, vec!["many_to_one_root"]); +} + +#[test] +fn test_cube_names_many_to_one_view_child_dim() { + let ctx = many_to_one_ctx(); + let dim = ctx.create_dimension("many_to_one_view.child_dim").unwrap(); + let mut names = collect_cube_names(&dim).unwrap(); + names.sort(); + assert_eq!(names, vec!["many_to_one_child", "many_to_one_root"]); +} + +#[test] +fn test_cube_names_many_to_one_view_root_measure() { + let ctx = many_to_one_ctx(); + let measure = ctx.create_measure("many_to_one_view.root_val_avg").unwrap(); + let names = collect_cube_names(&measure).unwrap(); + assert_eq!(names, vec!["many_to_one_root"]); +} + +#[test] +fn test_cube_names_many_to_one_view_child_measure() { + let ctx = many_to_one_ctx(); + let measure = ctx + .create_measure("many_to_one_view.child_val_avg") + .unwrap(); + let mut names = collect_cube_names(&measure).unwrap(); + names.sort(); + assert_eq!(names, vec!["many_to_one_child", "many_to_one_root"]); +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs new file mode 100644 index 0000000000000..ca654dce6dd72 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs @@ -0,0 +1,166 @@ +use crate::cube_bridge::join_hints::JoinHintItem; +use crate::planner::sql_evaluator::collectors::{ + collect_join_hints, collect_join_hints_for_measures, +}; +use crate::test_fixtures::cube_bridge::MockSchema; +use crate::test_fixtures::test_utils::TestContext; +use indoc::indoc; + +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()) +} + +#[test] +fn test_collect_join_hints_single_cube() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + + let dim = ctx.create_dimension("orders.status").unwrap(); + let hints = collect_join_hints(&dim).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("orders")]); + + let measure = ctx.create_measure("orders.count").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("orders")]); + + let measure = ctx.create_measure("customers.payments").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("customers")]); +} + +#[test] +fn test_collect_join_hints_cross_cube_measure() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + + let measure = ctx.create_measure("customers.payments_per_order").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 2); + assert!(hints.items().contains(&s("customers"))); + assert!(hints.items().contains(&s("orders"))); +} + +#[test] +fn test_collect_join_hints_view_symbols() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/diamond_joins.yaml")).unwrap(); + + let dim = ctx.create_dimension("a_with_b_and_c.code").unwrap(); + let hints = collect_join_hints(&dim).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[v(&["cube_a", "cube_b", "cube_c"])]); + + let dim = ctx.create_dimension("a_with_b_and_c.name").unwrap(); + let hints = collect_join_hints(&dim).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("cube_a")]); + + // View measure from root join_path + let measure = ctx.create_measure("a_with_b_and_c.total_value").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("cube_a")]); +} + +#[test] +fn test_collect_join_hints_for_measures_multiple() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + + let m1 = ctx.create_measure("orders.count").unwrap(); + let m2 = ctx.create_measure("customers.count").unwrap(); + let hints = collect_join_hints_for_measures(&vec![m1, m2]).unwrap(); + + assert_eq!(hints.len(), 2); + assert!(hints.items().contains(&s("orders"))); + assert!(hints.items().contains(&s("customers"))); + + let m1 = ctx.create_measure("orders.count").unwrap(); + let m2 = ctx.create_measure("orders.max_amount").unwrap(); + let hints = collect_join_hints_for_measures(&vec![m1, m2]).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("orders")]); +} + +// --- many_to_one view tests --- + +fn many_to_one_ctx() -> TestContext { + TestContext::new(MockSchema::from_yaml_file("common/many_to_one_views.yaml")).unwrap() +} + +#[test] +fn test_join_hints_many_to_one_view_root_dim() { + let ctx = many_to_one_ctx(); + let dim = ctx.create_dimension("many_to_one_view.root_dim").unwrap(); + let hints = collect_join_hints(&dim).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("many_to_one_root")]); +} + +#[test] +fn test_join_hints_many_to_one_view_child_dim() { + let ctx = many_to_one_ctx(); + let dim = ctx.create_dimension("many_to_one_view.child_dim").unwrap(); + let hints = collect_join_hints(&dim).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!( + hints.items(), + &[v(&["many_to_one_root", "many_to_one_child"])] + ); +} + +#[test] +fn test_join_hints_many_to_one_view_root_measure() { + let ctx = many_to_one_ctx(); + let measure = ctx.create_measure("many_to_one_view.root_val_avg").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("many_to_one_root")]); +} + +#[test] +fn test_join_hints_many_to_one_view_child_measure() { + let ctx = many_to_one_ctx(); + let measure = ctx + .create_measure("many_to_one_view.child_val_avg") + .unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!( + hints.items(), + &[v(&["many_to_one_root", "many_to_one_child"])] + ); +} + +#[test] +fn test_join_hints_many_to_one_view_combined_measures() { + let ctx = many_to_one_ctx(); + let m1 = ctx.create_measure("many_to_one_view.root_val_avg").unwrap(); + let m2 = ctx + .create_measure("many_to_one_view.child_val_avg") + .unwrap(); + let hints = collect_join_hints_for_measures(&vec![m1, m2]).unwrap(); + assert_eq!(hints.len(), 2); + assert!(hints.items().contains(&s("many_to_one_root"))); + assert!(hints + .items() + .contains(&v(&["many_to_one_root", "many_to_one_child"]))); +} + +#[test] +fn test_many_to_one_view_build_sql() { + let ctx = many_to_one_ctx(); + let query = indoc! {" + measures: + - many_to_one_view.root_val_avg + - many_to_one_view.child_val_avg + dimensions: + - many_to_one_view.root_dim + - many_to_one_view.child_dim + "}; + let sql = ctx.build_sql(query).unwrap(); + insta::assert_snapshot!(sql); +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs new file mode 100644 index 0000000000000..e28e1131dc891 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs @@ -0,0 +1,123 @@ +use crate::cube_bridge::member_expression::MemberExpressionExpressionDef; +use crate::cube_bridge::member_sql::MemberSql; +use crate::cube_bridge::options_member::OptionsMember; +use crate::test_fixtures::cube_bridge::{ + members_from_strings, MockBaseQueryOptions, MockMemberExpressionDefinition, MockMemberSql, + MockSchema, +}; +use crate::test_fixtures::test_utils::TestContext; +use indoc::indoc; +use std::rc::Rc; + +fn create_test_context() -> TestContext { + let schema = MockSchema::from_yaml_file("common/many_to_one_views.yaml"); + TestContext::new(schema).unwrap() +} + +fn make_member_expression(expression_name: &str, cube_name: &str, sql: &str) -> OptionsMember { + let member_sql: Rc = Rc::new(MockMemberSql::new(sql).unwrap()); + let expr = MockMemberExpressionDefinition::builder() + .expression_name(Some(expression_name.to_string())) + .name(Some(expression_name.to_string())) + .cube_name(Some(cube_name.to_string())) + .expression(MemberExpressionExpressionDef::Sql(member_sql)) + .build(); + OptionsMember::MemberExpression(Rc::new(expr)) +} + +fn build_query_with_member_expression(ctx: &TestContext, extra_measure: OptionsMember) -> String { + let mut measures = members_from_strings(vec![ + "many_to_one_view.root_val_avg", + "many_to_one_view.child_val_avg", + ]); + measures.push(extra_measure); + + let options = Rc::new( + MockBaseQueryOptions::builder() + .cube_evaluator(ctx.query_tools().cube_evaluator().clone()) + .base_tools(ctx.query_tools().base_tools().clone()) + .join_graph(ctx.query_tools().join_graph().clone()) + .security_context(ctx.security_context().clone()) + .measures(Some(measures)) + .dimensions(Some(members_from_strings(vec![ + "many_to_one_view.root_dim", + "many_to_one_view.child_dim", + ]))) + .build(), + ); + + ctx.build_sql_from_options(options).unwrap() +} + +#[test] +fn test_many_to_one_view_base_query() { + let ctx = create_test_context(); + + let query_yaml = indoc! {" + measures: + - many_to_one_view.root_val_avg + - many_to_one_view.child_val_avg + dimensions: + - many_to_one_view.root_dim + - many_to_one_view.child_dim + "}; + + let sql = ctx.build_sql(query_yaml).unwrap(); + insta::assert_snapshot!(sql); +} + +#[test] +fn test_many_to_one_view_one_sum() { + let ctx = create_test_context(); + let expr = make_member_expression("one_sum", "many_to_one_view", "SUM(1)"); + let sql = build_query_with_member_expression(&ctx, expr); + insta::assert_snapshot!(sql); +} + +#[test] +fn test_many_to_one_view_root_val_sum() { + let ctx = create_test_context(); + let expr = make_member_expression( + "root_val_sum_expr", + "many_to_one_view", + "{many_to_one_view.root_val_sum}", + ); + let sql = build_query_with_member_expression(&ctx, expr); + insta::assert_snapshot!(sql); +} + +#[test] +fn test_many_to_one_view_root_distinct_dim() { + let ctx = create_test_context(); + let expr = make_member_expression( + "root_distinct_dim", + "many_to_one_view", + "COUNT(DISTINCT {many_to_one_view.root_test_dim})", + ); + let sql = build_query_with_member_expression(&ctx, expr); + insta::assert_snapshot!(sql); +} + +#[test] +fn test_many_to_one_view_child_val_sum() { + let ctx = create_test_context(); + let expr = make_member_expression( + "child_val_sum_expr", + "many_to_one_view", + "{many_to_one_view.child_val_sum}", + ); + let sql = build_query_with_member_expression(&ctx, expr); + insta::assert_snapshot!(sql); +} + +#[test] +fn test_many_to_one_view_child_distinct_dim() { + let ctx = create_test_context(); + let expr = make_member_expression( + "child_distinct_dim", + "many_to_one_view", + "COUNT(DISTINCT {many_to_one_view.child_test_dim})", + ); + let sql = build_query_with_member_expression(&ctx, expr); + insta::assert_snapshot!(sql); +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs index 376256b06b7d3..fcb42c70992b7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs @@ -1,6 +1,10 @@ mod common_sql_generation; +mod compiled_member_path; mod cube_evaluator; +mod cube_names_collector; mod dimension_symbol; +mod join_hints_collector; mod measure_symbol; +mod member_expressions_on_views; mod pre_aggregation_sql_generation; mod utils; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__common_sql_generation__diamond_join_over_direct_path_sql.snap b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__common_sql_generation__diamond_join_over_direct_path_sql.snap new file mode 100644 index 0000000000000..c9699e3a57514 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__common_sql_generation__diamond_join_over_direct_path_sql.snap @@ -0,0 +1,10 @@ +--- +source: cubesqlplanner/src/tests/common_sql_generation.rs +expression: sql +--- +SELECT "cube_c".code "cube_c__code", count(COUNT(*)) "cube_a__count" +FROM table_a AS "cube_a" +LEFT JOIN table_b AS "cube_b" ON "cube_a".b_id = "cube_b".id +LEFT JOIN table_c AS "cube_c" ON "cube_b".c_id = "cube_c".id +GROUP BY 1 +ORDER BY 2 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap new file mode 100644 index 0000000000000..86239d66cc267 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap @@ -0,0 +1,25 @@ +--- +source: cubesqlplanner/src/tests/join_hints_collector.rs +expression: sql +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap new file mode 100644 index 0000000000000..5a3fb46b49556 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap @@ -0,0 +1,25 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +expression: sql +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap new file mode 100644 index 0000000000000..052fb0141cd91 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap @@ -0,0 +1,25 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +expression: sql +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_1"."child_distinct_dim" "child_distinct_dim" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg", COUNT(DISTINCT "many_to_one_child_key_many_to_one_child".test_dim) "child_distinct_dim" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap new file mode 100644 index 0000000000000..3233c3ade620f --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap @@ -0,0 +1,25 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +expression: sql +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_1"."many_to_one_child__child_val_sum" "child_val_sum_expr" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg", sum("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_sum" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap new file mode 100644 index 0000000000000..f8276ecd8f7c3 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap @@ -0,0 +1,25 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +expression: sql +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_0"."one_sum" "one_sum" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg", SUM(1) "one_sum" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap new file mode 100644 index 0000000000000..f0d250eaf4881 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap @@ -0,0 +1,25 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +expression: sql +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_0"."root_distinct_dim" "root_distinct_dim" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg", COUNT(DISTINCT "many_to_one_root".test_dim) "root_distinct_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap new file mode 100644 index 0000000000000..44771f9c5fc77 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap @@ -0,0 +1,25 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +expression: sql +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_0"."many_to_one_root__root_val_sum" "root_val_sum_expr" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg", sum("many_to_one_root".val) "many_to_one_root__root_val_sum" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC From 541aa3465f25cb55328249b95fa9c258c95f5e07 Mon Sep 17 00:00:00 2001 From: Alex Qyoun-ae <4062971+MazterQyou@users.noreply.github.com> Date: Tue, 10 Mar 2026 21:49:09 +0400 Subject: [PATCH 2/5] feat(cubesql): Introduce `cube_cache` session variable (#10483) --- .../core-data-apis/sql-api/index.mdx | 17 ++++- .../core-data-apis/sql-api/reference.mdx | 16 ++++- .../cubejs-backend-native/src/transport.rs | 2 +- .../cubesql/src/compile/engine/df/scan.rs | 15 ++++- rust/cubesql/cubesql/src/compile/mod.rs | 67 ++++++++++++++++++- .../cubesql/src/compile/rewrite/converter.rs | 2 +- rust/cubesql/cubesql/src/compile/router.rs | 40 ++++++++++- .../postgres/session_vars.rs | 3 +- 8 files changed, 152 insertions(+), 10 deletions(-) diff --git a/docs/content/product/apis-integrations/core-data-apis/sql-api/index.mdx b/docs/content/product/apis-integrations/core-data-apis/sql-api/index.mdx index f63748916f699..8ffee5726dd5d 100644 --- a/docs/content/product/apis-integrations/core-data-apis/sql-api/index.mdx +++ b/docs/content/product/apis-integrations/core-data-apis/sql-api/index.mdx @@ -211,6 +211,18 @@ to establish too many connections at once can lead to an out-of-memory crash. You can use the CUBEJS_MAX_SESSIONS environment variable to adjust the session limit. +## Cache control + +You can use the `cube_cache` session variable with the [`SET` command][ref-set-command] +to control [in-memory cache][ref-caching] behavior. +It works the same way as [cache control in the REST API][ref-rest-cache-control]. + +Example: + +```sql +SET cube_cache = 'must-revalidate'; +``` + ## Troubleshooting ### `Can't find rewrite` @@ -254,4 +266,7 @@ Use the following environment variables to allocate more resources for query pla [cube-bi-use-case]: https://cube.dev/use-cases/connected-bi [ref-queries-row-limit]: /product/apis-integrations/queries#row-limit [ref-rest-api]: /product/apis-integrations/rest-api -[ref-rest-api-cubesql]: /product/apis-integrations/rest-api/reference#base_pathv1cubesql \ No newline at end of file +[ref-rest-api-cubesql]: /product/apis-integrations/rest-api/reference#base_pathv1cubesql +[ref-caching]: /product/caching +[ref-rest-cache-control]: /product/apis-integrations/rest-api#cache-control +[ref-set-command]: /product/apis-integrations/sql-api/reference#set \ No newline at end of file diff --git a/docs/content/product/apis-integrations/core-data-apis/sql-api/reference.mdx b/docs/content/product/apis-integrations/core-data-apis/sql-api/reference.mdx index 734a00f8576d1..413de58db29bc 100644 --- a/docs/content/product/apis-integrations/core-data-apis/sql-api/reference.mdx +++ b/docs/content/product/apis-integrations/core-data-apis/sql-api/reference.mdx @@ -147,6 +147,19 @@ SHOW ALL; (9 rows) ``` +### `SET` + +Synopsis: + +```sql +SET name TO value +SET name = value +``` + +The `SET` command changes a session variable to a new value. + +Use `SET` with the `cube_cache` session variable for [cache control][ref-sql-cache-control]. + ## SQL functions and operators SQL API currently implements a subset of functions and operators [supported by @@ -428,4 +441,5 @@ See the [XIRR recipe](/product/data-modeling/recipes/xirr) for more details. [link-postgres-funcs]: https://www.postgresql.org/docs/current/functions.html [link-github-sql-api]: https://github.com/cube-js/cube/issues?q=is%3Aopen+is%3Aissue+label%3Aapi%3Asql [link-github-new-sql-api-issue]: https://github.com/cube-js/cube/issues/new?assignees=&labels=&projects=&template=sql_api_query_issue.md&title= -[link-xirr]: https://support.microsoft.com/en-us/office/xirr-function-de1242ec-6477-445b-b11b-a303ad9adc9d \ No newline at end of file +[link-xirr]: https://support.microsoft.com/en-us/office/xirr-function-de1242ec-6477-445b-b11b-a303ad9adc9d +[ref-sql-cache-control]: /product/apis-integrations/sql-api#cache-control \ No newline at end of file diff --git a/packages/cubejs-backend-native/src/transport.rs b/packages/cubejs-backend-native/src/transport.rs index 9db2d4407be89..a8a3fc620271b 100644 --- a/packages/cubejs-backend-native/src/transport.rs +++ b/packages/cubejs-backend-native/src/transport.rs @@ -379,7 +379,7 @@ impl TransportService for NodeBridgeTransport { member_to_alias: None, expression_params: None, streaming: false, - cache_mode: cache_mode.clone(), + cache_mode, })?; let result = call_raw_js_with_channel_as_callback( diff --git a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs index 50aeb49935dfd..df104f63a1b3d 100644 --- a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs +++ b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs @@ -49,7 +49,7 @@ use std::str::FromStr; use std::{ any::Any, borrow::Cow, - fmt, + fmt::{self, Display}, sync::Arc, task::{Context, Poll}, }; @@ -80,7 +80,7 @@ impl MemberField { } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] pub enum CacheMode { #[serde(rename = "stale-if-slow")] StaleIfSlow, @@ -106,6 +106,17 @@ impl FromStr for CacheMode { } } +impl Display for CacheMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CacheMode::StaleIfSlow => write!(f, "stale-if-slow"), + CacheMode::StaleWhileRevalidate => write!(f, "stale-while-revalidate"), + CacheMode::MustRevalidate => write!(f, "must-revalidate"), + CacheMode::NoCache => write!(f, "no-cache"), + } + } +} + #[derive(Debug, Clone)] pub struct CubeScanOptions { pub change_user: Option, diff --git a/rust/cubesql/cubesql/src/compile/mod.rs b/rust/cubesql/cubesql/src/compile/mod.rs index 2d3de410fcc6c..3cfae8db20c16 100644 --- a/rust/cubesql/cubesql/src/compile/mod.rs +++ b/rust/cubesql/cubesql/src/compile/mod.rs @@ -37,7 +37,7 @@ mod tests { }; use crate::{ compile::{ - engine::df::scan::MemberField, + engine::df::scan::{CacheMode, MemberField}, rewrite::rewriter::Rewriter, test::{get_sixteen_char_member_cube, get_string_cube_meta}, }, @@ -18590,4 +18590,69 @@ LIMIT {{ limit }}{% endif %}"#.to_string(), Ok(()) } + + #[tokio::test] + async fn test_set_cache_mode() -> Result<(), CubeError> { + if !Rewriter::sql_push_down_enabled() { + return Ok(()); + } + init_testing_logger(); + + let context = TestContext::new(DatabaseProtocol::PostgreSQL).await; + + context + .execute_query("SET cube_cache = 'must-revalidate'") + .await?; + + // Test that cache mode has been successfully applied + let query = r#" + SELECT order_date + FROM KibanaSampleDataEcommerce + GROUP BY 1 + "#; + let expected_cube_scan = V1LoadRequestQuery { + measures: Some(vec![]), + dimensions: Some(vec!["KibanaSampleDataEcommerce.order_date".to_string()]), + segments: Some(vec![]), + order: Some(vec![]), + ..Default::default() + }; + let cube_scan = context + .convert_sql_to_cube_query(&query) + .await + .unwrap() + .as_logical_plan() + .find_cube_scan(); + assert_eq!(cube_scan.request, expected_cube_scan); + assert_eq!( + cube_scan.options.cache_mode, + Some(CacheMode::MustRevalidate), + ); + + // Test that cache mode correctly resets to default + context.execute_query("SET cube_cache = DEFAULT").await?; + + let query = r#" + SELECT order_date + FROM KibanaSampleDataEcommerce + GROUP BY 1 + "#; + let expected_cube_scan = V1LoadRequestQuery { + measures: Some(vec![]), + dimensions: Some(vec!["KibanaSampleDataEcommerce.order_date".to_string()]), + segments: Some(vec![]), + order: Some(vec![]), + ..Default::default() + }; + let cube_scan = context + .convert_sql_to_cube_query(&query) + .await + .unwrap() + .as_logical_plan() + .find_cube_scan(); + assert_eq!(cube_scan.request, expected_cube_scan); + assert_eq!(cube_scan.options.cache_mode, None,); + + Ok(()) + } } diff --git a/rust/cubesql/cubesql/src/compile/rewrite/converter.rs b/rust/cubesql/cubesql/src/compile/rewrite/converter.rs index e73fffdf386a4..0c71bd7da20bb 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/converter.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/converter.rs @@ -2099,7 +2099,7 @@ impl LanguageToLogicalPlanConverter { CubeScanOptions { change_user, max_records, - cache_mode: cache_mode.clone(), + cache_mode: *cache_mode, throw_continue_wait, }, alias_to_cube.into_iter().map(|(_, c)| c).unique().collect(), diff --git a/rust/cubesql/cubesql/src/compile/router.rs b/rust/cubesql/cubesql/src/compile/router.rs index ee52eb4c0633e..386548c55e56e 100644 --- a/rust/cubesql/cubesql/src/compile/router.rs +++ b/rust/cubesql/cubesql/src/compile/router.rs @@ -3,10 +3,11 @@ use crate::compile::{ StatusFlags, }; use sqlparser::ast; -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, str::FromStr, sync::Arc}; use crate::{ compile::{ + engine::df::scan::CacheMode, error::{CompilationError, CompilationResult}, parser::parse_sql_to_statement, DatabaseVariable, DatabaseVariablesToUpdate, @@ -377,7 +378,7 @@ impl QueryRouter { session_columns_to_update.into_iter().partition(|v| { matches!( v.name.to_lowercase().as_str(), - "user" | "current_user" | "timezone" + "user" | "current_user" | "timezone" | "cube_cache" ) }); @@ -407,6 +408,18 @@ impl QueryRouter { }; self.change_timezone(timezone).await?; } + "cube_cache" => { + let cache_mode = match v.value { + ScalarValue::Utf8(Some(mode)) => mode, + _ => { + return Err(CompilationError::user(format!( + "Invalid cube_cache value: {:?}", + v.value + ))) + } + }; + self.change_cache_mode(cache_mode).await?; + } _ => { return Err(CompilationError::user(format!( "Invalid special variable: {:?}", @@ -527,6 +540,29 @@ impl QueryRouter { Ok(()) } + async fn change_cache_mode(&self, cache_mode_str: String) -> Result<(), CompilationError> { + let mut cache_mode = self.state.cache_mode.write().map_err(|err| { + CompilationError::internal(format!("Unable to acquire cache mode lock: {}", err)) + })?; + let cache_mode_value = if cache_mode_str.eq_ignore_ascii_case("default") { + *cache_mode = None; + None + } else { + let cache_mode_parsed = CacheMode::from_str(&cache_mode_str).map_err(|_| { + CompilationError::user(format!("Invalid value for cache mode: {}", cache_mode_str)) + })?; + *cache_mode = Some(cache_mode_parsed); + Some(cache_mode_parsed.to_string()) + }; + let variable = DatabaseVariable::user_defined( + "cube_cache".to_string(), + ScalarValue::Utf8(cache_mode_value), + None, + ); + self.state.set_variables(vec![variable]); + Ok(()) + } + async fn create_table_to_plan( &self, name: &ast::ObjectName, diff --git a/rust/cubesql/cubesql/src/sql/database_variables/postgres/session_vars.rs b/rust/cubesql/cubesql/src/sql/database_variables/postgres/session_vars.rs index c13ed346b5252..16c7c89707bc2 100644 --- a/rust/cubesql/cubesql/src/sql/database_variables/postgres/session_vars.rs +++ b/rust/cubesql/cubesql/src/sql/database_variables/postgres/session_vars.rs @@ -66,12 +66,13 @@ pub fn defaults() -> DatabaseVariables { ScalarValue::Utf8(Some("140002".to_string())), None, ), - // Custom cubesql variables + // Custom cube[sql] variables DatabaseVariable::user_defined( CUBESQL_PENALIZE_POST_PROCESSING_VAR.to_string(), ScalarValue::Boolean(Some(false)), None, ), + DatabaseVariable::user_defined("cube_cache".to_string(), ScalarValue::Utf8(None), None), ]; let variables = IntoIterator::into_iter(variables) From 53a80fd159fb1655b5ac5b467414b4a5352fae90 Mon Sep 17 00:00:00 2001 From: Alex Qyoun-ae <4062971+MazterQyou@users.noreply.github.com> Date: Tue, 10 Mar 2026 21:51:33 +0400 Subject: [PATCH 3/5] feat(client-core): Use `throwContinueWait` parameter in `cubeSql` method (#10461) --- packages/cubejs-client-core/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index bcf8985e3f9eb..de7fa0e2bc7f6 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -379,7 +379,7 @@ class CubeApi { body.error = text; } - if (body.error === 'Continue wait') { + if (body.error?.includes('Continue wait')) { await checkMutex(); if (options?.progressCallback) { options.progressCallback(new ProgressResult(body as ProgressResponse)); @@ -742,6 +742,7 @@ class CubeApi { signal: options?.signal, fetchTimeout: options?.timeout, baseRequestId: options?.baseRequestId, + throwContinueWait: true, }; if (options?.cache) { From d6ad885f384d675f25ca8405dad4ee5a559e98c3 Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Tue, 10 Mar 2026 19:48:28 +0100 Subject: [PATCH 4/5] fix(cubesql): Support per-column format codes (Tableau Desktop compatibility) (#10484) JDBC clients like Tableau request mixed binary/text format codes per column in the Bind message (e.g., binary for int8/float8, text for text/timestamp). CubeSQL previously took only the first format code and applied it uniformly to all columns, causing ArrayIndexOutOfBoundsException when clients tried to decode binary data that was actually text-encoded. Closes #10418 --- .../cubesql/src/sql/postgres/extended.rs | 104 ++++++++++++----- rust/cubesql/cubesql/src/sql/postgres/shim.rs | 105 +++++++++++++----- .../cubesql/src/sql/postgres/writer.rs | 84 ++++++++++++-- rust/cubesql/pg-srv/src/protocol.rs | 8 ++ 4 files changed, 237 insertions(+), 64 deletions(-) diff --git a/rust/cubesql/cubesql/src/sql/postgres/extended.rs b/rust/cubesql/cubesql/src/sql/postgres/extended.rs index 867f9a817031f..0d143ad8fef77 100644 --- a/rust/cubesql/cubesql/src/sql/postgres/extended.rs +++ b/rust/cubesql/cubesql/src/sql/postgres/extended.rs @@ -22,6 +22,49 @@ use datafusion::{ use futures::{FutureExt, Stream, StreamExt}; use pg_srv::protocol::{CommandComplete, PortalCompletion, PortalSuspended}; +#[derive(Debug, Clone)] +pub enum ResultFormat { + AllText, + AllBinary, + PerColumn(Vec), +} + +impl ResultFormat { + /// Resolve format for column at `idx`. + pub fn format_for(&self, idx: usize) -> protocol::Format { + match self { + ResultFormat::AllText => protocol::Format::Text, + ResultFormat::AllBinary => protocol::Format::Binary, + ResultFormat::PerColumn(formats) => match formats.get(idx) { + Some(&fmt) => fmt, + // Per PG protocol, the number of format codes must equal the number of + // result columns. The fallback here should never be reached in practice; + // it is a defensive default for malformed or empty format vectors. + None => { + log::error!( + "PerColumn format index {} out of bounds (len {}), falling back to Text", + idx, + formats.len() + ); + + protocol::Format::Text + } + }, + } + } +} + +impl From> for ResultFormat { + fn from(formats: Vec) -> Self { + match formats.len() { + 0 => ResultFormat::AllText, + 1 if formats[0] == protocol::Format::Text => ResultFormat::AllText, + 1 if formats[0] == protocol::Format::Binary => ResultFormat::AllBinary, + _ => ResultFormat::PerColumn(formats), + } + } +} + use crate::transport::SpanId; use async_stream::stream; @@ -210,8 +253,7 @@ pub enum PortalBatch { #[derive(Debug)] pub struct Portal { - // Format which is used to return data - format: protocol::Format, + format: ResultFormat, from: PortalFrom, // State which holds corresponding data for each step. Option is used for dereferencing state: Option, @@ -244,7 +286,7 @@ fn split_record_batch(batch: RecordBatch, mid: usize) -> (RecordBatch, Option>, ) -> Self { @@ -256,11 +298,7 @@ impl Portal { } } - pub fn new_empty( - format: protocol::Format, - from: PortalFrom, - span_id: Option>, - ) -> Self { + pub fn new_empty(format: ResultFormat, from: PortalFrom, span_id: Option>) -> Self { Self { format, from, @@ -271,7 +309,7 @@ impl Portal { pub fn get_description(&self) -> Result, ConnectionError> { match &self.state { - Some(PortalState::Prepared(state)) => state.plan.to_row_description(self.format), + Some(PortalState::Prepared(state)) => state.plan.to_row_description(&self.format), Some(PortalState::InExecutionFrame(state)) => Ok(state.description.clone()), Some(PortalState::InExecutionStream(state)) => Ok(state.description.clone()), Some(PortalState::Finished(state)) => Ok(state.description.clone()), @@ -297,10 +335,6 @@ impl Portal { } } - pub fn get_format(&self) -> protocol::Format { - self.format - } - fn hand_execution_frame_state<'a>( &'a mut self, frame_state: InExecutionFrameState, @@ -318,7 +352,8 @@ impl Portal { ) .into()); } else { - let writer = self.dataframe_to_writer(frame_state.batch)?; + let format = self.get_result_format(frame_state.description.as_ref()); + let writer = self.dataframe_to_writer(frame_state.batch, &format)?; let num_rows = writer.num_rows() as u32; if let Some(description) = &frame_state.description { @@ -354,8 +389,12 @@ impl Portal { } } - fn dataframe_to_writer(&self, frame: DataFrame) -> Result { - let mut writer = BatchWriter::new(self.get_format()); + fn dataframe_to_writer( + &self, + frame: DataFrame, + format: &ResultFormat, + ) -> Result { + let mut writer = BatchWriter::new(format.clone()); for row in frame.to_rows().into_iter() { for value in row.to_values() { @@ -387,6 +426,7 @@ impl Portal { batch: RecordBatch, max_rows: usize, left: &mut usize, + format: &ResultFormat, ) -> Result<(Option, BatchWriter), ConnectionError> { let mut unused: Option = None; @@ -407,7 +447,7 @@ impl Portal { let frame = batches_to_dataframe(batch_for_write.schema().as_ref(), vec![batch_for_write])?; - Ok((unused, self.dataframe_to_writer(frame)?)) + Ok((unused, self.dataframe_to_writer(frame, format)?)) } fn hand_execution_stream_state<'a>( @@ -419,12 +459,14 @@ impl Portal { let mut left: usize = max_rows; let mut num_of_rows = 0; + let resolved_format = self.get_result_format(stream_state.description.as_ref()); + if let Some(description) = &stream_state.description { yield Ok(PortalBatch::Description(description.clone())); } if let Some(unused_batch) = stream_state.unused.take() { - let (usused_batch, batch_writer) = self.iterate_stream_batch(unused_batch, max_rows, &mut left)?; + let (usused_batch, batch_writer) = self.iterate_stream_batch(unused_batch, max_rows, &mut left, &resolved_format)?; stream_state.unused = usused_batch; num_of_rows = batch_writer.num_rows() as u32; @@ -448,7 +490,7 @@ impl Portal { } Some(res) => match res { Ok(batch) => { - let (unused_batch, writer) = self.iterate_stream_batch(batch, max_rows, &mut left)?; + let (unused_batch, writer) = self.iterate_stream_batch(batch, max_rows, &mut left, &resolved_format)?; num_of_rows += writer.num_rows() as u32; @@ -490,7 +532,7 @@ impl Portal { ); } PortalState::Prepared(state) => { - let description = state.plan.to_row_description(self.format)?; + let description = state.plan.to_row_description(&self.format)?; match state.plan { QueryPlan::MetaOk(_, completion) => { self.state = Some(PortalState::Finished(FinishedState { description })); @@ -581,6 +623,14 @@ impl Portal { } } + /// Resolve the effective result format: use per-column formats from the row + /// description if available, otherwise fall back to the portal's format. + fn get_result_format(&self, description: Option<&protocol::RowDescription>) -> ResultFormat { + description + .map(|d| ResultFormat::PerColumn(d.get_formats())) + .unwrap_or_else(|| self.format.clone()) + } + pub fn span_id(&self) -> Option> { self.span_id.clone() } @@ -589,6 +639,7 @@ impl Portal { #[cfg(test)] mod tests { use super::*; + use crate::sql::{error::ConnectionError, extended::PortalFrom}; use crate::{ compile::engine::information_schema::postgres::InfoSchemaTestingDatasetProvider, sql::{ @@ -597,9 +648,6 @@ mod tests { ColumnFlags, ColumnType, }, }; - use pg_srv::protocol::Format; - - use crate::sql::{error::ConnectionError, extended::PortalFrom}; use datafusion::{ arrow::{ array::{ArrayRef, StringArray}, @@ -698,7 +746,7 @@ mod tests { #[tokio::test] async fn test_portal_legacy_dataframe_limited_more() -> Result<(), ConnectionError> { let mut p = Portal { - format: Format::Binary, + format: ResultFormat::AllBinary, from: PortalFrom::Extended, state: Some(PortalState::InExecutionFrame(InExecutionFrameState::new( generate_testing_data_frame(3), @@ -731,7 +779,7 @@ mod tests { #[tokio::test] async fn test_portal_legacy_dataframe_limited_less() -> Result<(), ConnectionError> { let mut p = Portal { - format: Format::Binary, + format: ResultFormat::AllBinary, from: PortalFrom::Extended, state: Some(PortalState::InExecutionFrame(InExecutionFrameState::new( generate_testing_data_frame(3), @@ -759,7 +807,7 @@ mod tests { #[tokio::test] async fn test_portal_legacy_dataframe_unlimited() -> Result<(), ConnectionError> { let mut p = Portal { - format: Format::Binary, + format: ResultFormat::AllBinary, from: PortalFrom::Extended, state: Some(PortalState::InExecutionFrame(InExecutionFrameState::new( generate_testing_data_frame(3), @@ -794,7 +842,7 @@ mod tests { let stream = ctx.read_table(table)?.execute_stream().await?; let mut portal = Portal { - format: Format::Binary, + format: ResultFormat::AllBinary, from: PortalFrom::Extended, state: Some(PortalState::InExecutionStream(InExecutionStreamState::new( stream, @@ -817,7 +865,7 @@ mod tests { let stream = ctx.read_table(table)?.execute_stream().await?; let mut portal = Portal { - format: Format::Binary, + format: ResultFormat::AllBinary, from: PortalFrom::Extended, state: Some(PortalState::InExecutionStream(InExecutionStreamState::new( stream, diff --git a/rust/cubesql/cubesql/src/sql/postgres/shim.rs b/rust/cubesql/cubesql/src/sql/postgres/shim.rs index ffd2c5e6e6260..e68793dc229d2 100644 --- a/rust/cubesql/cubesql/src/sql/postgres/shim.rs +++ b/rust/cubesql/cubesql/src/sql/postgres/shim.rs @@ -17,7 +17,7 @@ use crate::{ sql::{ compiler_cache::CompilerCacheEntry, df_type_to_pg_tid, - extended::{Cursor, Portal, PortalBatch, PortalFrom}, + extended::{Cursor, Portal, PortalBatch, PortalFrom, ResultFormat}, statement::{PostgresStatementParamsFinder, StatementPlaceholderReplacer}, AuthContextRef, Session, SessionState, }, @@ -64,7 +64,7 @@ pub enum StartupState { pub trait QueryPlanExt { fn to_row_description( &self, - required_format: protocol::Format, + format: &ResultFormat, ) -> Result, ConnectionError>; } @@ -73,18 +73,18 @@ impl QueryPlanExt for QueryPlan { /// None is used for special queries, which doesnt have any data, for example: DISCARD ALL fn to_row_description( &self, - required_format: protocol::Format, + format: &ResultFormat, ) -> Result, ConnectionError> { match &self { QueryPlan::MetaOk(_, _) | QueryPlan::CreateTempTable(_, _, _, _) => Ok(None), QueryPlan::MetaTabular(_, frame) => { let mut result = vec![]; - for field in frame.get_columns() { + for (idx, field) in frame.get_columns().iter().enumerate() { result.push(protocol::RowDescriptionField::new( field.get_name(), PgType::get_by_tid(PgTypeId::TEXT), - required_format, + format.format_for(idx), )); } @@ -93,11 +93,11 @@ impl QueryPlanExt for QueryPlan { QueryPlan::DataFusionSelect(logical_plan, _) => { let mut result = vec![]; - for field in logical_plan.schema().fields() { + for (idx, field) in logical_plan.schema().fields().iter().enumerate() { result.push(protocol::RowDescriptionField::new( field.name().clone(), df_type_to_pg_tid(field.data_type())?.to_type(), - required_format, + format.format_for(idx), )); } @@ -937,7 +937,7 @@ impl AsyncPostgresShim { ) })?; - let format = body.result_formats.first().copied().unwrap_or(Format::Text); + let format = ResultFormat::from(body.result_formats.clone()); let portal = match source_statement { PreparedStatement::Empty { .. } => { drop(statements_guard); @@ -1084,15 +1084,15 @@ impl AsyncPostgresShim { match plan { Ok(plan) => { - let description = - plan.to_row_description(Format::Text)? - .and_then(|description| { - if description.len() > 0 { - Some(description) - } else { - None - } - }); + let description = plan + .to_row_description(&ResultFormat::AllText)? + .and_then(|description| { + if description.len() > 0 { + Some(description) + } else { + None + } + }); ( PreparedStatement::Query { @@ -1219,7 +1219,12 @@ impl AsyncPostgresShim { let plan = QueryPlan::MetaOk(StatusFlags::empty(), CommandCompletion::Begin); self.write_portal( - &mut Portal::new(plan, Format::Text, PortalFrom::Simple, span_id.clone()), + &mut Portal::new( + plan, + ResultFormat::AllText, + PortalFrom::Simple, + span_id.clone(), + ), 0, cancel, ) @@ -1238,7 +1243,12 @@ impl AsyncPostgresShim { let plan = QueryPlan::MetaOk(StatusFlags::empty(), CommandCompletion::Rollback); self.write_portal( - &mut Portal::new(plan, Format::Text, PortalFrom::Simple, span_id.clone()), + &mut Portal::new( + plan, + ResultFormat::AllText, + PortalFrom::Simple, + span_id.clone(), + ), 0, CancellationToken::new(), ) @@ -1257,7 +1267,12 @@ impl AsyncPostgresShim { let plan = QueryPlan::MetaOk(StatusFlags::empty(), CommandCompletion::Commit); self.write_portal( - &mut Portal::new(plan, Format::Text, PortalFrom::Simple, span_id.clone()), + &mut Portal::new( + plan, + ResultFormat::AllText, + PortalFrom::Simple, + span_id.clone(), + ), 0, CancellationToken::new(), ) @@ -1354,8 +1369,12 @@ impl AsyncPostgresShim { ) .await?; - let mut portal = - Portal::new(plan, cursor.format, PortalFrom::Fetch, span_id.clone()); + let mut portal = Portal::new( + plan, + ResultFormat::from(vec![cursor.format]), + PortalFrom::Fetch, + span_id.clone(), + ); self.write_portal(&mut portal, limit, cancel).await?; self.portals.insert(name.value, portal); @@ -1448,7 +1467,12 @@ impl AsyncPostgresShim { QueryPlan::MetaOk(StatusFlags::empty(), CommandCompletion::DeclareCursor); self.write_portal( - &mut Portal::new(plan, Format::Text, PortalFrom::Simple, span_id.clone()), + &mut Portal::new( + plan, + ResultFormat::AllText, + PortalFrom::Simple, + span_id.clone(), + ), 0, cancel, ) @@ -1465,7 +1489,12 @@ impl AsyncPostgresShim { ); self.write_portal( - &mut Portal::new(plan, Format::Text, PortalFrom::Simple, span_id.clone()), + &mut Portal::new( + plan, + ResultFormat::AllText, + PortalFrom::Simple, + span_id.clone(), + ), 0, cancel, ) @@ -1499,7 +1528,12 @@ impl AsyncPostgresShim { }?; self.write_portal( - &mut Portal::new(plan, Format::Text, PortalFrom::Simple, span_id.clone()), + &mut Portal::new( + plan, + ResultFormat::AllText, + PortalFrom::Simple, + span_id.clone(), + ), 0, cancel, ) @@ -1540,7 +1574,12 @@ impl AsyncPostgresShim { }?; self.write_portal( - &mut Portal::new(plan, Format::Text, PortalFrom::Simple, span_id.clone()), + &mut Portal::new( + plan, + ResultFormat::AllText, + PortalFrom::Simple, + span_id.clone(), + ), 0, cancel, ) @@ -1579,7 +1618,12 @@ impl AsyncPostgresShim { let plan = QueryPlan::MetaOk(StatusFlags::empty(), CommandCompletion::Prepare); self.write_portal( - &mut Portal::new(plan, Format::Text, PortalFrom::Simple, span_id.clone()), + &mut Portal::new( + plan, + ResultFormat::AllText, + PortalFrom::Simple, + span_id.clone(), + ), 0, cancel, ) @@ -1596,7 +1640,12 @@ impl AsyncPostgresShim { .await?; self.write_portal( - &mut Portal::new(plan, Format::Text, PortalFrom::Simple, span_id.clone()), + &mut Portal::new( + plan, + ResultFormat::AllText, + PortalFrom::Simple, + span_id.clone(), + ), 0, cancel, ) diff --git a/rust/cubesql/cubesql/src/sql/postgres/writer.rs b/rust/cubesql/cubesql/src/sql/postgres/writer.rs index dfb6592bafbe7..758ffddeffa11 100644 --- a/rust/cubesql/cubesql/src/sql/postgres/writer.rs +++ b/rust/cubesql/cubesql/src/sql/postgres/writer.rs @@ -1,6 +1,7 @@ use crate::sql::{ dataframe::{Decimal128Value, ListValue}, df_type_to_pg_tid, + postgres::extended::ResultFormat, }; use bytes::{BufMut, BytesMut}; use datafusion::arrow::{ @@ -166,17 +167,17 @@ impl ToProtocolValue for ListValue { #[derive(Debug)] pub struct BatchWriter { - format: Format, + format: ResultFormat, // Data of whole rows data: BytesMut, - // Current row + // Current column index within the current row current: u32, rows: u32, row: BytesMut, } impl BatchWriter { - pub fn new(format: Format) -> Self { + pub fn new(format: ResultFormat) -> Self { Self { format, data: BytesMut::new(), @@ -186,10 +187,15 @@ impl BatchWriter { } } + fn current_format(&self) -> Format { + self.format.format_for(self.current as usize) + } + pub fn write_value(&mut self, value: T) -> Result<(), ProtocolError> { + let format = self.current_format(); self.current += 1; - match self.format { + match format { Format::Text => value.to_text(&mut self.row)?, Format::Binary => value.to_binary(&mut self.row)?, }; @@ -238,6 +244,7 @@ mod tests { use crate::sql::{ dataframe::{Decimal128Value, ListValue}, error::ConnectionError, + postgres::extended::ResultFormat, writer::{BatchWriter, ToProtocolValue}, }; use bytes::BytesMut; @@ -297,7 +304,7 @@ mod tests { async fn test_backend_writer_text_simple() -> Result<(), ConnectionError> { let mut cursor = Cursor::new(vec![]); - let mut writer = BatchWriter::new(Format::Text); + let mut writer = BatchWriter::new(ResultFormat::AllText); writer.write_value("test1".to_string())?; writer.write_value(true)?; writer.end_row()?; @@ -325,7 +332,7 @@ mod tests { async fn test_backend_writer_binary_simple() -> Result<(), ConnectionError> { let mut cursor = Cursor::new(vec![]); - let mut writer = BatchWriter::new(Format::Binary); + let mut writer = BatchWriter::new(ResultFormat::AllBinary); writer.write_value("test1".to_string())?; writer.write_value(true)?; writer.end_row()?; @@ -355,7 +362,7 @@ mod tests { // fetch 2 in test; let mut cursor = Cursor::new(vec![]); - let mut writer = BatchWriter::new(Format::Binary); + let mut writer = BatchWriter::new(ResultFormat::AllBinary); writer.write_value(Decimal128Value::new(1, 5))?; writer.end_row()?; @@ -380,7 +387,7 @@ mod tests { #[tokio::test] async fn test_backend_writer_binary_int8_array() -> Result<(), ConnectionError> { let mut cursor = Cursor::new(vec![]); - let mut writer = BatchWriter::new(Format::Binary); + let mut writer = BatchWriter::new(ResultFormat::AllBinary); // Row 1 let mut col = Int64Builder::new(3); @@ -415,4 +422,65 @@ mod tests { Ok(()) } + + #[tokio::test] + async fn test_backend_writer_per_column_format() -> Result<(), ConnectionError> { + // Simulates Tableau/JDBC: text for col0 (string), binary for col1 (int64), binary for col2 (f64) + let mut cursor = Cursor::new(vec![]); + + let mut writer = BatchWriter::new(ResultFormat::PerColumn(vec![ + Format::Text, + Format::Binary, + Format::Binary, + ])); + writer.write_value("hello".to_string())?; + writer.write_value(42_i64)?; + writer.write_value(2.5_f64)?; + writer.end_row()?; + + buffer::write_direct(&mut BytesMut::new(), &mut cursor, writer).await?; + + assert_eq!( + cursor.get_ref()[0..], + vec![ + // row: "hello" as text, 42_i64 as binary, 2.5_f64 as binary + 68, 0, 0, 0, 39, 0, 3, 0, 0, 0, 5, 104, 101, 108, 108, 111, 0, 0, 0, 8, 0, 0, 0, 0, + 0, 0, 0, 42, 0, 0, 0, 8, 64, 4, 0, 0, 0, 0, 0, 0 + ] + ); + + Ok(()) + } + + #[tokio::test] + async fn test_backend_writer_per_column_format_multiple_rows() -> Result<(), ConnectionError> { + // Ensure per-column format resets correctly across rows + let mut cursor = Cursor::new(vec![]); + + let mut writer = + BatchWriter::new(ResultFormat::PerColumn(vec![Format::Text, Format::Binary])); + + writer.write_value("row1".to_string())?; + writer.write_value(100_i64)?; + writer.end_row()?; + + writer.write_value("row2".to_string())?; + writer.write_value(200_i64)?; + writer.end_row()?; + + buffer::write_direct(&mut BytesMut::new(), &mut cursor, writer).await?; + + assert_eq!( + cursor.get_ref()[0..], + vec![ + // row 1: "row1" as text, 100_i64 as binary + 68, 0, 0, 0, 26, 0, 2, 0, 0, 0, 4, 114, 111, 119, 49, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, + 0, 100, // row 2: "row2" as text, 200_i64 as binary + 68, 0, 0, 0, 26, 0, 2, 0, 0, 0, 4, 114, 111, 119, 50, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, + 0, 200 + ] + ); + + Ok(()) + } } diff --git a/rust/cubesql/pg-srv/src/protocol.rs b/rust/cubesql/pg-srv/src/protocol.rs index f1ba92a24383d..eda47110bc5c8 100644 --- a/rust/cubesql/pg-srv/src/protocol.rs +++ b/rust/cubesql/pg-srv/src/protocol.rs @@ -555,9 +555,17 @@ impl RowDescription { pub fn new(fields: Vec) -> Self { Self { fields } } + pub fn len(&self) -> usize { self.fields.len() } + + /// Returns the resolved per-column format codes. + /// These account for `is_binary_supported()` — if a type doesn't support + /// binary encoding, the format is downgraded to Text even if Binary was requested. + pub fn get_formats(&self) -> Vec { + self.fields.iter().map(|f| f.format).collect() + } } impl Serialize for RowDescription { From a5c516b0720f2dd783293eeaa328f1da349d2dab Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Tue, 10 Mar 2026 19:51:01 +0100 Subject: [PATCH 5/5] v1.6.21 --- CHANGELOG.md | 13 ++++ lerna.json | 2 +- packages/cubejs-api-gateway/CHANGELOG.md | 4 ++ packages/cubejs-api-gateway/package.json | 10 ++-- packages/cubejs-athena-driver/CHANGELOG.md | 4 ++ packages/cubejs-athena-driver/package.json | 10 ++-- packages/cubejs-backend-cloud/CHANGELOG.md | 4 ++ packages/cubejs-backend-cloud/package.json | 6 +- packages/cubejs-backend-maven/CHANGELOG.md | 4 ++ packages/cubejs-backend-maven/package.json | 6 +- packages/cubejs-backend-native/CHANGELOG.md | 6 ++ packages/cubejs-backend-native/package.json | 8 +-- packages/cubejs-backend-shared/CHANGELOG.md | 4 ++ packages/cubejs-backend-shared/package.json | 4 +- packages/cubejs-base-driver/CHANGELOG.md | 4 ++ packages/cubejs-base-driver/package.json | 6 +- packages/cubejs-bigquery-driver/CHANGELOG.md | 4 ++ packages/cubejs-bigquery-driver/package.json | 8 +-- packages/cubejs-cli/CHANGELOG.md | 4 ++ packages/cubejs-cli/package.json | 12 ++-- .../cubejs-clickhouse-driver/CHANGELOG.md | 4 ++ .../cubejs-clickhouse-driver/package.json | 10 ++-- packages/cubejs-client-core/CHANGELOG.md | 6 ++ packages/cubejs-client-core/package.json | 4 +- packages/cubejs-client-dx/CHANGELOG.md | 4 ++ packages/cubejs-client-dx/package.json | 2 +- packages/cubejs-client-ngx/CHANGELOG.md | 4 ++ packages/cubejs-client-ngx/package.json | 2 +- packages/cubejs-client-react/CHANGELOG.md | 4 ++ packages/cubejs-client-react/package.json | 4 +- packages/cubejs-client-vue3/CHANGELOG.md | 4 ++ packages/cubejs-client-vue3/package.json | 4 +- .../cubejs-client-ws-transport/CHANGELOG.md | 4 ++ .../cubejs-client-ws-transport/package.json | 6 +- packages/cubejs-crate-driver/CHANGELOG.md | 4 ++ packages/cubejs-crate-driver/package.json | 10 ++-- packages/cubejs-cubestore-driver/CHANGELOG.md | 4 ++ packages/cubejs-cubestore-driver/package.json | 12 ++-- .../CHANGELOG.md | 4 ++ .../package.json | 12 ++-- .../cubejs-dbt-schema-extension/CHANGELOG.md | 4 ++ .../cubejs-dbt-schema-extension/package.json | 8 +-- packages/cubejs-docker/CHANGELOG.md | 4 ++ packages/cubejs-docker/package.json | 60 +++++++++---------- packages/cubejs-dremio-driver/CHANGELOG.md | 4 ++ packages/cubejs-dremio-driver/package.json | 12 ++-- packages/cubejs-druid-driver/CHANGELOG.md | 4 ++ packages/cubejs-druid-driver/package.json | 10 ++-- packages/cubejs-duckdb-driver/CHANGELOG.md | 4 ++ packages/cubejs-duckdb-driver/package.json | 12 ++-- .../cubejs-elasticsearch-driver/CHANGELOG.md | 4 ++ .../cubejs-elasticsearch-driver/package.json | 8 +-- packages/cubejs-firebolt-driver/CHANGELOG.md | 4 ++ packages/cubejs-firebolt-driver/package.json | 12 ++-- packages/cubejs-hive-driver/CHANGELOG.md | 4 ++ packages/cubejs-hive-driver/package.json | 8 +-- packages/cubejs-jdbc-driver/CHANGELOG.md | 4 ++ packages/cubejs-jdbc-driver/package.json | 8 +-- packages/cubejs-ksql-driver/CHANGELOG.md | 4 ++ packages/cubejs-ksql-driver/package.json | 10 ++-- packages/cubejs-linter/CHANGELOG.md | 4 ++ packages/cubejs-linter/package.json | 2 +- .../cubejs-materialize-driver/CHANGELOG.md | 4 ++ .../cubejs-materialize-driver/package.json | 12 ++-- packages/cubejs-mongobi-driver/CHANGELOG.md | 4 ++ packages/cubejs-mongobi-driver/package.json | 8 +-- packages/cubejs-mssql-driver/CHANGELOG.md | 4 ++ packages/cubejs-mssql-driver/package.json | 6 +- .../CHANGELOG.md | 4 ++ .../package.json | 8 +-- packages/cubejs-mysql-driver/CHANGELOG.md | 4 ++ packages/cubejs-mysql-driver/package.json | 10 ++-- packages/cubejs-oracle-driver/CHANGELOG.md | 4 ++ packages/cubejs-oracle-driver/package.json | 4 +- packages/cubejs-pinot-driver/CHANGELOG.md | 4 ++ packages/cubejs-pinot-driver/package.json | 10 ++-- packages/cubejs-playground/CHANGELOG.md | 4 ++ packages/cubejs-playground/package.json | 6 +- packages/cubejs-postgres-driver/CHANGELOG.md | 4 ++ packages/cubejs-postgres-driver/package.json | 10 ++-- packages/cubejs-prestodb-driver/CHANGELOG.md | 4 ++ packages/cubejs-prestodb-driver/package.json | 8 +-- .../cubejs-query-orchestrator/CHANGELOG.md | 4 ++ .../cubejs-query-orchestrator/package.json | 10 ++-- packages/cubejs-questdb-driver/CHANGELOG.md | 4 ++ packages/cubejs-questdb-driver/package.json | 12 ++-- packages/cubejs-redshift-driver/CHANGELOG.md | 4 ++ packages/cubejs-redshift-driver/package.json | 10 ++-- packages/cubejs-schema-compiler/CHANGELOG.md | 4 ++ packages/cubejs-schema-compiler/package.json | 12 ++-- packages/cubejs-server-core/CHANGELOG.md | 4 ++ packages/cubejs-server-core/package.json | 24 ++++---- packages/cubejs-server/CHANGELOG.md | 4 ++ packages/cubejs-server/package.json | 14 ++--- packages/cubejs-snowflake-driver/CHANGELOG.md | 4 ++ packages/cubejs-snowflake-driver/package.json | 8 +-- packages/cubejs-sqlite-driver/CHANGELOG.md | 4 ++ packages/cubejs-sqlite-driver/package.json | 8 +-- packages/cubejs-templates/CHANGELOG.md | 4 ++ packages/cubejs-templates/package.json | 6 +- packages/cubejs-testing-drivers/CHANGELOG.md | 4 ++ packages/cubejs-testing-drivers/package.json | 36 +++++------ packages/cubejs-testing-shared/CHANGELOG.md | 4 ++ packages/cubejs-testing-shared/package.json | 10 ++-- packages/cubejs-testing/CHANGELOG.md | 4 ++ packages/cubejs-testing/package.json | 22 +++---- packages/cubejs-trino-driver/CHANGELOG.md | 4 ++ packages/cubejs-trino-driver/package.json | 12 ++-- packages/cubejs-vertica-driver/CHANGELOG.md | 4 ++ packages/cubejs-vertica-driver/package.json | 14 ++--- rust/cubesql/CHANGELOG.md | 10 ++++ rust/cubesql/package.json | 2 +- rust/cubestore/CHANGELOG.md | 7 +++ rust/cubestore/package.json | 6 +- 114 files changed, 538 insertions(+), 288 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 898403cbf8acc..4ce76469e238b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +### Bug Fixes + +- **cubesql:** Support per-column format codes (Tableau Desktop compatibility) ([#10484](https://github.com/cube-js/cube/issues/10484)) ([d6ad885](https://github.com/cube-js/cube/commit/d6ad885f384d675f25ca8405dad4ee5a559e98c3)), closes [#10418](https://github.com/cube-js/cube/issues/10418) + +### Features + +- **client-core:** Use `throwContinueWait` parameter in `cubeSql` method ([#10461](https://github.com/cube-js/cube/issues/10461)) ([53a80fd](https://github.com/cube-js/cube/commit/53a80fd159fb1655b5ac5b467414b4a5352fae90)) +- **cubesql:** Introduce `cube_cache` session variable ([#10483](https://github.com/cube-js/cube/issues/10483)) ([541aa34](https://github.com/cube-js/cube/commit/541aa3465f25cb55328249b95fa9c258c95f5e07)) +- **cubestore:** Logarithmic probabilistic increment of LFU counter ([#10478](https://github.com/cube-js/cube/issues/10478)) ([4b8ac6f](https://github.com/cube-js/cube/commit/4b8ac6ff81b0ee114cd0bc4a5e8235fe294c53a2)) +- **cubestore:** Queue - add exclusivity support ([#10479](https://github.com/cube-js/cube/issues/10479)) ([b5a0ec3](https://github.com/cube-js/cube/commit/b5a0ec32c061834af0c30bd7def9097ae27af32f)) + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 31834cf1f52be..c84a75dbc5fb4 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.6.20", + "version": "1.6.21", "npmClient": "yarn", "command": { "bootstrap": { diff --git a/packages/cubejs-api-gateway/CHANGELOG.md b/packages/cubejs-api-gateway/CHANGELOG.md index ebdc3a7194c8c..eae022b00ef5d 100644 --- a/packages/cubejs-api-gateway/CHANGELOG.md +++ b/packages/cubejs-api-gateway/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/api-gateway + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) ### Features diff --git a/packages/cubejs-api-gateway/package.json b/packages/cubejs-api-gateway/package.json index 8b7ab54cf2ca9..43938620cf0ce 100644 --- a/packages/cubejs-api-gateway/package.json +++ b/packages/cubejs-api-gateway/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/api-gateway", "description": "Cube.js API Gateway", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,9 +27,9 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/native": "1.6.20", - "@cubejs-backend/query-orchestrator": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/native": "1.6.21", + "@cubejs-backend/query-orchestrator": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "@ungap/structured-clone": "^0.3.4", "assert-never": "^1.4.0", "body-parser": "^1.19.0", @@ -53,7 +53,7 @@ "zod": "^4.1.13" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/express": "^4.17.21", "@types/jest": "^29", "@types/jsonwebtoken": "^9.0.2", diff --git a/packages/cubejs-athena-driver/CHANGELOG.md b/packages/cubejs-athena-driver/CHANGELOG.md index 4a841d20c6f03..a031e3c98312f 100644 --- a/packages/cubejs-athena-driver/CHANGELOG.md +++ b/packages/cubejs-athena-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/athena-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/athena-driver diff --git a/packages/cubejs-athena-driver/package.json b/packages/cubejs-athena-driver/package.json index e7370e6408d1e..6e00100bb2a53 100644 --- a/packages/cubejs-athena-driver/package.json +++ b/packages/cubejs-athena-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/athena-driver", "description": "Cube.js Athena database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -30,13 +30,13 @@ "dependencies": { "@aws-sdk/client-athena": "^3.22.0", "@aws-sdk/credential-providers": "^3.22.0", - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "sqlstring": "^2.3.1" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", "@types/ramda": "^0.27.40", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-backend-cloud/CHANGELOG.md b/packages/cubejs-backend-cloud/CHANGELOG.md index 6518598d21c8a..baf226ae97da6 100644 --- a/packages/cubejs-backend-cloud/CHANGELOG.md +++ b/packages/cubejs-backend-cloud/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/cloud + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/cloud diff --git a/packages/cubejs-backend-cloud/package.json b/packages/cubejs-backend-cloud/package.json index 8852d14c8479b..2f4b44d440c2b 100644 --- a/packages/cubejs-backend-cloud/package.json +++ b/packages/cubejs-backend-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/cloud", - "version": "1.6.20", + "version": "1.6.21", "description": "Cube Cloud package", "main": "dist/src/index.js", "typings": "dist/src/index.d.ts", @@ -30,7 +30,7 @@ "devDependencies": { "@babel/core": "^7.24.5", "@babel/preset-env": "^7.24.5", - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/fs-extra": "^9.0.8", "@types/jest": "^29", "jest": "^29", @@ -38,7 +38,7 @@ }, "dependencies": { "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/shared": "1.6.21", "chokidar": "^3.5.1", "env-var": "^6.3.0", "form-data": "^4.0.0", diff --git a/packages/cubejs-backend-maven/CHANGELOG.md b/packages/cubejs-backend-maven/CHANGELOG.md index 7737ad00bd6e1..ea4f304792fc8 100644 --- a/packages/cubejs-backend-maven/CHANGELOG.md +++ b/packages/cubejs-backend-maven/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/maven + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/maven diff --git a/packages/cubejs-backend-maven/package.json b/packages/cubejs-backend-maven/package.json index cf106049adc90..1556fab8b3c52 100644 --- a/packages/cubejs-backend-maven/package.json +++ b/packages/cubejs-backend-maven/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/maven", "description": "Cube.js Maven Wrapper for java dependencies downloading", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "license": "Apache-2.0", "repository": { "type": "git", @@ -31,12 +31,12 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/shared": "1.6.21", "source-map-support": "^0.5.19", "xmlbuilder2": "^2.4.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "@types/node": "^20", "jest": "^29", diff --git a/packages/cubejs-backend-native/CHANGELOG.md b/packages/cubejs-backend-native/CHANGELOG.md index 4e7bb9e204507..81609f2e9feb2 100644 --- a/packages/cubejs-backend-native/CHANGELOG.md +++ b/packages/cubejs-backend-native/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +### Features + +- **cubesql:** Introduce `cube_cache` session variable ([#10483](https://github.com/cube-js/cube/issues/10483)) ([541aa34](https://github.com/cube-js/cube/commit/541aa3465f25cb55328249b95fa9c258c95f5e07)) + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) ### Features diff --git a/packages/cubejs-backend-native/package.json b/packages/cubejs-backend-native/package.json index a5d85c3e9d02a..5d9084382d1b3 100644 --- a/packages/cubejs-backend-native/package.json +++ b/packages/cubejs-backend-native/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/native", - "version": "1.6.20", + "version": "1.6.21", "author": "Cube Dev, Inc.", "description": "Native module for Cube.js (binding to Rust codebase)", "main": "dist/js/index.js", @@ -36,7 +36,7 @@ "dist/js" ], "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "@types/node": "^20", "cargo-cp-artifact": "^0.1.9", @@ -47,8 +47,8 @@ "uuid": "^8.3.2" }, "dependencies": { - "@cubejs-backend/cubesql": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/cubesql": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "@cubejs-infra/post-installer": "^0.0.7" }, "resources": { diff --git a/packages/cubejs-backend-shared/CHANGELOG.md b/packages/cubejs-backend-shared/CHANGELOG.md index 45a31722b09b4..4045fc75b44c7 100644 --- a/packages/cubejs-backend-shared/CHANGELOG.md +++ b/packages/cubejs-backend-shared/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/shared + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/shared diff --git a/packages/cubejs-backend-shared/package.json b/packages/cubejs-backend-shared/package.json index ae1f172d5515e..2b74f75c686b6 100644 --- a/packages/cubejs-backend-shared/package.json +++ b/packages/cubejs-backend-shared/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/shared", - "version": "1.6.20", + "version": "1.6.21", "description": "Shared code for Cube.js backend packages", "main": "dist/src/index.js", "typings": "dist/src/index.d.ts", @@ -27,7 +27,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/bytes": "^3.1.5", "@types/cli-progress": "^3.9.1", "@types/decompress": "^4.2.7", diff --git a/packages/cubejs-base-driver/CHANGELOG.md b/packages/cubejs-base-driver/CHANGELOG.md index 834155e44e094..3f3e99bb90160 100644 --- a/packages/cubejs-base-driver/CHANGELOG.md +++ b/packages/cubejs-base-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/base-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/base-driver diff --git a/packages/cubejs-base-driver/package.json b/packages/cubejs-base-driver/package.json index 22536e71320aa..b6235d21b4861 100644 --- a/packages/cubejs-base-driver/package.json +++ b/packages/cubejs-base-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/base-driver", "description": "Cube.js Base Driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -33,11 +33,11 @@ "@aws-sdk/s3-request-presigner": "^3.49.0", "@azure/identity": "^4.4.1", "@azure/storage-blob": "^12.9.0", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/shared": "1.6.21", "@google-cloud/storage": "^7.13.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "@types/node": "^20", "jest": "^29", diff --git a/packages/cubejs-bigquery-driver/CHANGELOG.md b/packages/cubejs-bigquery-driver/CHANGELOG.md index b5a77c6341734..87eaf1b268ff1 100644 --- a/packages/cubejs-bigquery-driver/CHANGELOG.md +++ b/packages/cubejs-bigquery-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/bigquery-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/bigquery-driver diff --git a/packages/cubejs-bigquery-driver/package.json b/packages/cubejs-bigquery-driver/package.json index d3b298d1136be..6b66a53e6dc2c 100644 --- a/packages/cubejs-bigquery-driver/package.json +++ b/packages/cubejs-bigquery-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/bigquery-driver", "description": "Cube.js BigQuery database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,15 +29,15 @@ "main": "index.js", "types": "dist/src/index.d.ts", "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/shared": "1.6.21", "@google-cloud/bigquery": "^7.7.0", "@google-cloud/storage": "^7.13.0", "ramda": "^0.27.2" }, "devDependencies": { - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/testing-shared": "1.6.21", "@types/big.js": "^6.2.2", "@types/dedent": "^0.7.0", "@types/jest": "^29", diff --git a/packages/cubejs-cli/CHANGELOG.md b/packages/cubejs-cli/CHANGELOG.md index a80b0af342365..e8e0c2546148d 100644 --- a/packages/cubejs-cli/CHANGELOG.md +++ b/packages/cubejs-cli/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package cubejs-cli + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package cubejs-cli diff --git a/packages/cubejs-cli/package.json b/packages/cubejs-cli/package.json index 592bdb2494982..605b7e62454cd 100644 --- a/packages/cubejs-cli/package.json +++ b/packages/cubejs-cli/package.json @@ -2,7 +2,7 @@ "name": "cubejs-cli", "description": "Cube.js Command Line Interface", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -30,10 +30,10 @@ "LICENSE" ], "dependencies": { - "@cubejs-backend/cloud": "1.6.20", + "@cubejs-backend/cloud": "1.6.21", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "chalk": "^2.4.2", "cli-progress": "^3.10", "commander": "^2.19.0", @@ -50,8 +50,8 @@ "colors": "1.4.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/server": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/server": "1.6.21", "@oclif/command": "^1.8.0", "@types/cli-progress": "^3.8.0", "@types/cross-spawn": "^6.0.2", diff --git a/packages/cubejs-clickhouse-driver/CHANGELOG.md b/packages/cubejs-clickhouse-driver/CHANGELOG.md index 4b96b0b627c8d..73373a2bd0552 100644 --- a/packages/cubejs-clickhouse-driver/CHANGELOG.md +++ b/packages/cubejs-clickhouse-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/clickhouse-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/clickhouse-driver diff --git a/packages/cubejs-clickhouse-driver/package.json b/packages/cubejs-clickhouse-driver/package.json index 30ff57c72ae19..5e74c314845b9 100644 --- a/packages/cubejs-clickhouse-driver/package.json +++ b/packages/cubejs-clickhouse-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/clickhouse-driver", "description": "Cube.js ClickHouse database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,16 +28,16 @@ }, "dependencies": { "@clickhouse/client": "^1.12.0", - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "moment": "^2.24.0", "sqlstring": "^2.3.1", "uuid": "^8.3.2" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", "@types/jest": "^29", "jest": "^29", "typescript": "~5.2.2" diff --git a/packages/cubejs-client-core/CHANGELOG.md b/packages/cubejs-client-core/CHANGELOG.md index 2fe22a13cd257..84fb1ac0c006c 100644 --- a/packages/cubejs-client-core/CHANGELOG.md +++ b/packages/cubejs-client-core/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +### Features + +- **client-core:** Use `throwContinueWait` parameter in `cubeSql` method ([#10461](https://github.com/cube-js/cube/issues/10461)) ([53a80fd](https://github.com/cube-js/cube/commit/53a80fd159fb1655b5ac5b467414b4a5352fae90)) + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-client/core diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index f4511acf3fddd..d3036cd671525 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/core", - "version": "1.6.20", + "version": "1.6.21", "engines": {}, "repository": { "type": "git", @@ -39,7 +39,7 @@ ], "license": "MIT", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "@types/moment-range": "^4.0.0", "@types/ramda": "^0.27.34", diff --git a/packages/cubejs-client-dx/CHANGELOG.md b/packages/cubejs-client-dx/CHANGELOG.md index ca5539e702bc7..a21c976eea37a 100644 --- a/packages/cubejs-client-dx/CHANGELOG.md +++ b/packages/cubejs-client-dx/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-client/dx + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-client/dx diff --git a/packages/cubejs-client-dx/package.json b/packages/cubejs-client-dx/package.json index 23b2308c70af4..f3789440cb290 100644 --- a/packages/cubejs-client-dx/package.json +++ b/packages/cubejs-client-dx/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/dx", - "version": "1.6.20", + "version": "1.6.21", "engines": {}, "repository": { "type": "git", diff --git a/packages/cubejs-client-ngx/CHANGELOG.md b/packages/cubejs-client-ngx/CHANGELOG.md index 8ac404c111c45..d6fbb13bd9c42 100644 --- a/packages/cubejs-client-ngx/CHANGELOG.md +++ b/packages/cubejs-client-ngx/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-client/ngx + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-client/ngx diff --git a/packages/cubejs-client-ngx/package.json b/packages/cubejs-client-ngx/package.json index dc96f0bca529f..d748fdc98ce39 100644 --- a/packages/cubejs-client-ngx/package.json +++ b/packages/cubejs-client-ngx/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/ngx", - "version": "1.6.20", + "version": "1.6.21", "author": "Cube Dev, Inc.", "engines": {}, "repository": { diff --git a/packages/cubejs-client-react/CHANGELOG.md b/packages/cubejs-client-react/CHANGELOG.md index 77d82805d8794..ef2fff5263825 100644 --- a/packages/cubejs-client-react/CHANGELOG.md +++ b/packages/cubejs-client-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-client/react + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-client/react diff --git a/packages/cubejs-client-react/package.json b/packages/cubejs-client-react/package.json index 70b65ce3e86a0..95959c20309db 100644 --- a/packages/cubejs-client-react/package.json +++ b/packages/cubejs-client-react/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/react", - "version": "1.6.20", + "version": "1.6.21", "author": "Cube Dev, Inc.", "license": "MIT", "engines": {}, @@ -24,7 +24,7 @@ ], "dependencies": { "@babel/runtime": "^7.1.2", - "@cubejs-client/core": "1.6.20", + "@cubejs-client/core": "1.6.21", "core-js": "^3.6.5", "ramda": "^0.27.2" }, diff --git a/packages/cubejs-client-vue3/CHANGELOG.md b/packages/cubejs-client-vue3/CHANGELOG.md index 13a1ac0f525f9..2979bc4bedfa1 100644 --- a/packages/cubejs-client-vue3/CHANGELOG.md +++ b/packages/cubejs-client-vue3/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-client/vue3 + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-client/vue3 diff --git a/packages/cubejs-client-vue3/package.json b/packages/cubejs-client-vue3/package.json index 54d4102aab6fe..5901d4c01a9fc 100644 --- a/packages/cubejs-client-vue3/package.json +++ b/packages/cubejs-client-vue3/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/vue3", - "version": "1.6.20", + "version": "1.6.21", "engines": {}, "repository": { "type": "git", @@ -28,7 +28,7 @@ "src" ], "dependencies": { - "@cubejs-client/core": "1.6.20", + "@cubejs-client/core": "1.6.21", "ramda": "^0.27.0" }, "devDependencies": { diff --git a/packages/cubejs-client-ws-transport/CHANGELOG.md b/packages/cubejs-client-ws-transport/CHANGELOG.md index 750dd199d9be7..bed6d9afe8305 100644 --- a/packages/cubejs-client-ws-transport/CHANGELOG.md +++ b/packages/cubejs-client-ws-transport/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-client/ws-transport + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-client/ws-transport diff --git a/packages/cubejs-client-ws-transport/package.json b/packages/cubejs-client-ws-transport/package.json index 8b32b897ab3b2..29730552928cb 100644 --- a/packages/cubejs-client-ws-transport/package.json +++ b/packages/cubejs-client-ws-transport/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/ws-transport", - "version": "1.6.20", + "version": "1.6.21", "engines": {}, "repository": { "type": "git", @@ -20,7 +20,7 @@ }, "dependencies": { "@babel/runtime": "^7.1.2", - "@cubejs-client/core": "1.6.20", + "@cubejs-client/core": "1.6.21", "core-js": "^3.6.5", "isomorphic-ws": "^4.0.1", "ws": "^7.3.1" @@ -33,7 +33,7 @@ "@babel/core": "^7.3.3", "@babel/preset-env": "^7.3.1", "@babel/preset-typescript": "^7.12.1", - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/ws": "^7.2.9", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-crate-driver/CHANGELOG.md b/packages/cubejs-crate-driver/CHANGELOG.md index 03203e45d2e0a..b0b7a2b6e8195 100644 --- a/packages/cubejs-crate-driver/CHANGELOG.md +++ b/packages/cubejs-crate-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/crate-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/crate-driver diff --git a/packages/cubejs-crate-driver/package.json b/packages/cubejs-crate-driver/package.json index 22bb80ad1e5d2..4cf03fd84f661 100644 --- a/packages/cubejs-crate-driver/package.json +++ b/packages/cubejs-crate-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/crate-driver", "description": "Cube.js Crate database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,13 +28,13 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/postgres-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20" + "@cubejs-backend/postgres-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-cubestore-driver/CHANGELOG.md b/packages/cubejs-cubestore-driver/CHANGELOG.md index fbdd8d0687d17..9e2a091b32836 100644 --- a/packages/cubejs-cubestore-driver/CHANGELOG.md +++ b/packages/cubejs-cubestore-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/cubestore-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) ### Features diff --git a/packages/cubejs-cubestore-driver/package.json b/packages/cubejs-cubestore-driver/package.json index dd58fdf8ce5fe..34326ef782dc5 100644 --- a/packages/cubejs-cubestore-driver/package.json +++ b/packages/cubejs-cubestore-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/cubestore-driver", "description": "Cube Store driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -26,10 +26,10 @@ "lint:fix": "eslint --fix src/*.ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/cubestore": "1.6.20", - "@cubejs-backend/native": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/cubestore": "1.6.21", + "@cubejs-backend/native": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "csv-write-stream": "^2.0.0", "flatbuffers": "23.3.3", "fs-extra": "^9.1.0", @@ -40,7 +40,7 @@ "ws": "^7.4.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/csv-write-stream": "^2.0.0", "@types/jest": "^29", "@types/node": "^20", diff --git a/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md b/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md index edfc6617abfdb..897b9f3434481 100644 --- a/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md +++ b/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/databricks-jdbc-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/databricks-jdbc-driver diff --git a/packages/cubejs-databricks-jdbc-driver/package.json b/packages/cubejs-databricks-jdbc-driver/package.json index 0684132bda9d9..9ed810a1d5ac7 100644 --- a/packages/cubejs-databricks-jdbc-driver/package.json +++ b/packages/cubejs-databricks-jdbc-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/databricks-jdbc-driver", "description": "Cube.js Databricks database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "license": "Apache-2.0", "repository": { "type": "git", @@ -30,17 +30,17 @@ "bin" ], "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/jdbc-driver": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/jdbc-driver": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "node-fetch": "^2.6.1", "ramda": "^0.27.2", "source-map-support": "^0.5.19", "uuid": "^8.3.2" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "@types/node": "^20", "@types/ramda": "^0.27.34", diff --git a/packages/cubejs-dbt-schema-extension/CHANGELOG.md b/packages/cubejs-dbt-schema-extension/CHANGELOG.md index 980343e547a58..23fb0f2bb45ec 100644 --- a/packages/cubejs-dbt-schema-extension/CHANGELOG.md +++ b/packages/cubejs-dbt-schema-extension/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/dbt-schema-extension + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/dbt-schema-extension diff --git a/packages/cubejs-dbt-schema-extension/package.json b/packages/cubejs-dbt-schema-extension/package.json index 0103814999b63..6a5cdd822568a 100644 --- a/packages/cubejs-dbt-schema-extension/package.json +++ b/packages/cubejs-dbt-schema-extension/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/dbt-schema-extension", "description": "Cube.js dbt Schema Extension", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -25,14 +25,14 @@ "lint:fix": "eslint --fix src/* --ext .ts,.js" }, "dependencies": { - "@cubejs-backend/schema-compiler": "1.6.20", + "@cubejs-backend/schema-compiler": "1.6.21", "fs-extra": "^9.1.0", "inflection": "^1.12.0", "node-fetch": "^2.6.1" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing": "1.6.21", "@types/jest": "^29", "jest": "^29", "stream-to-array": "^2.3.0", diff --git a/packages/cubejs-docker/CHANGELOG.md b/packages/cubejs-docker/CHANGELOG.md index d4dc0367c775e..30244e7857a7c 100644 --- a/packages/cubejs-docker/CHANGELOG.md +++ b/packages/cubejs-docker/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/docker + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/docker diff --git a/packages/cubejs-docker/package.json b/packages/cubejs-docker/package.json index cce75447652f9..3f4cd46d039b0 100644 --- a/packages/cubejs-docker/package.json +++ b/packages/cubejs-docker/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/docker", - "version": "1.6.20", + "version": "1.6.21", "description": "Cube.js In Docker (virtual package)", "author": "Cube Dev, Inc.", "license": "Apache-2.0", @@ -9,35 +9,35 @@ "node": ">=18.0.0" }, "dependencies": { - "@cubejs-backend/athena-driver": "1.6.20", - "@cubejs-backend/bigquery-driver": "1.6.20", - "@cubejs-backend/clickhouse-driver": "1.6.20", - "@cubejs-backend/crate-driver": "1.6.20", - "@cubejs-backend/databricks-jdbc-driver": "1.6.20", - "@cubejs-backend/dbt-schema-extension": "1.6.20", - "@cubejs-backend/dremio-driver": "1.6.20", - "@cubejs-backend/druid-driver": "1.6.20", - "@cubejs-backend/duckdb-driver": "1.6.20", - "@cubejs-backend/elasticsearch-driver": "1.6.20", - "@cubejs-backend/firebolt-driver": "1.6.20", - "@cubejs-backend/hive-driver": "1.6.20", - "@cubejs-backend/ksql-driver": "1.6.20", - "@cubejs-backend/materialize-driver": "1.6.20", - "@cubejs-backend/mongobi-driver": "1.6.20", - "@cubejs-backend/mssql-driver": "1.6.20", - "@cubejs-backend/mysql-driver": "1.6.20", - "@cubejs-backend/oracle-driver": "1.6.20", - "@cubejs-backend/pinot-driver": "1.6.20", - "@cubejs-backend/postgres-driver": "1.6.20", - "@cubejs-backend/prestodb-driver": "1.6.20", - "@cubejs-backend/questdb-driver": "1.6.20", - "@cubejs-backend/redshift-driver": "1.6.20", - "@cubejs-backend/server": "1.6.20", - "@cubejs-backend/snowflake-driver": "1.6.20", - "@cubejs-backend/sqlite-driver": "1.6.20", - "@cubejs-backend/trino-driver": "1.6.20", - "@cubejs-backend/vertica-driver": "1.6.20", - "cubejs-cli": "1.6.20", + "@cubejs-backend/athena-driver": "1.6.21", + "@cubejs-backend/bigquery-driver": "1.6.21", + "@cubejs-backend/clickhouse-driver": "1.6.21", + "@cubejs-backend/crate-driver": "1.6.21", + "@cubejs-backend/databricks-jdbc-driver": "1.6.21", + "@cubejs-backend/dbt-schema-extension": "1.6.21", + "@cubejs-backend/dremio-driver": "1.6.21", + "@cubejs-backend/druid-driver": "1.6.21", + "@cubejs-backend/duckdb-driver": "1.6.21", + "@cubejs-backend/elasticsearch-driver": "1.6.21", + "@cubejs-backend/firebolt-driver": "1.6.21", + "@cubejs-backend/hive-driver": "1.6.21", + "@cubejs-backend/ksql-driver": "1.6.21", + "@cubejs-backend/materialize-driver": "1.6.21", + "@cubejs-backend/mongobi-driver": "1.6.21", + "@cubejs-backend/mssql-driver": "1.6.21", + "@cubejs-backend/mysql-driver": "1.6.21", + "@cubejs-backend/oracle-driver": "1.6.21", + "@cubejs-backend/pinot-driver": "1.6.21", + "@cubejs-backend/postgres-driver": "1.6.21", + "@cubejs-backend/prestodb-driver": "1.6.21", + "@cubejs-backend/questdb-driver": "1.6.21", + "@cubejs-backend/redshift-driver": "1.6.21", + "@cubejs-backend/server": "1.6.21", + "@cubejs-backend/snowflake-driver": "1.6.21", + "@cubejs-backend/sqlite-driver": "1.6.21", + "@cubejs-backend/trino-driver": "1.6.21", + "@cubejs-backend/vertica-driver": "1.6.21", + "cubejs-cli": "1.6.21", "typescript": "~5.2.2" }, "resolutions": { diff --git a/packages/cubejs-dremio-driver/CHANGELOG.md b/packages/cubejs-dremio-driver/CHANGELOG.md index 5afe8b8a9dfe4..1afd8dfc69684 100644 --- a/packages/cubejs-dremio-driver/CHANGELOG.md +++ b/packages/cubejs-dremio-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/dremio-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/dremio-driver diff --git a/packages/cubejs-dremio-driver/package.json b/packages/cubejs-dremio-driver/package.json index 8e33726d03134..0da312a27b7e0 100644 --- a/packages/cubejs-dremio-driver/package.json +++ b/packages/cubejs-dremio-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/dremio-driver", "description": "Cube.js Dremio driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -22,15 +22,15 @@ "lint:fix": "eslint driver/*.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "axios": "^1.8.3", "sqlstring": "^2.3.1" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", "jest": "^29" }, "license": "Apache-2.0", diff --git a/packages/cubejs-druid-driver/CHANGELOG.md b/packages/cubejs-druid-driver/CHANGELOG.md index 0a51cba026e3f..96fbb7e89322a 100644 --- a/packages/cubejs-druid-driver/CHANGELOG.md +++ b/packages/cubejs-druid-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/druid-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/druid-driver diff --git a/packages/cubejs-druid-driver/package.json b/packages/cubejs-druid-driver/package.json index 6ec9d53bd0f62..1cb9e2f064bfa 100644 --- a/packages/cubejs-druid-driver/package.json +++ b/packages/cubejs-druid-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/druid-driver", "description": "Cube.js Druid database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "license": "Apache-2.0", "repository": { "type": "git", @@ -28,13 +28,13 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "axios": "^1.8.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "@types/node": "^20", "jest": "^29", diff --git a/packages/cubejs-duckdb-driver/CHANGELOG.md b/packages/cubejs-duckdb-driver/CHANGELOG.md index 0eeabd9853e3e..63f8a0fb00b20 100644 --- a/packages/cubejs-duckdb-driver/CHANGELOG.md +++ b/packages/cubejs-duckdb-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/duckdb-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/duckdb-driver diff --git a/packages/cubejs-duckdb-driver/package.json b/packages/cubejs-duckdb-driver/package.json index 685948a7bcf15..52c7ed739d088 100644 --- a/packages/cubejs-duckdb-driver/package.json +++ b/packages/cubejs-duckdb-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/duckdb-driver", "description": "Cube DuckDB database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,15 +27,15 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "duckdb": "^1.4.1" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", "@types/jest": "^29", "@types/node": "^20", "jest": "^29", diff --git a/packages/cubejs-elasticsearch-driver/CHANGELOG.md b/packages/cubejs-elasticsearch-driver/CHANGELOG.md index a9e125df89e92..8a6521dfd5cba 100644 --- a/packages/cubejs-elasticsearch-driver/CHANGELOG.md +++ b/packages/cubejs-elasticsearch-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/elasticsearch-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/elasticsearch-driver diff --git a/packages/cubejs-elasticsearch-driver/package.json b/packages/cubejs-elasticsearch-driver/package.json index ae0fd60b8a6e0..00c3b1ef93dab 100644 --- a/packages/cubejs-elasticsearch-driver/package.json +++ b/packages/cubejs-elasticsearch-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/elasticsearch-driver", "description": "Cube.js elasticsearch database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -23,14 +23,14 @@ "driver" ], "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "@elastic/elasticsearch": "7.12.0", "sqlstring": "^2.3.1" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "jest": "^29", "testcontainers": "^10.28.0" diff --git a/packages/cubejs-firebolt-driver/CHANGELOG.md b/packages/cubejs-firebolt-driver/CHANGELOG.md index 9a9527e805334..be471ff09fe6c 100644 --- a/packages/cubejs-firebolt-driver/CHANGELOG.md +++ b/packages/cubejs-firebolt-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/firebolt-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/firebolt-driver diff --git a/packages/cubejs-firebolt-driver/package.json b/packages/cubejs-firebolt-driver/package.json index 95cc37fb3eb91..6935d4f6e7c6b 100644 --- a/packages/cubejs-firebolt-driver/package.json +++ b/packages/cubejs-firebolt-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/firebolt-driver", "description": "Cube.js Firebolt database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,15 +28,15 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "firebolt-sdk": "1.10.0" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", "typescript": "~5.2.2" }, "publishConfig": { diff --git a/packages/cubejs-hive-driver/CHANGELOG.md b/packages/cubejs-hive-driver/CHANGELOG.md index 9b4a8873aa55d..9e5345fba8c5f 100644 --- a/packages/cubejs-hive-driver/CHANGELOG.md +++ b/packages/cubejs-hive-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/hive-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/hive-driver diff --git a/packages/cubejs-hive-driver/package.json b/packages/cubejs-hive-driver/package.json index 204bae3184879..34e17bcb0ba82 100644 --- a/packages/cubejs-hive-driver/package.json +++ b/packages/cubejs-hive-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/hive-driver", "description": "Cube.js Hive database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -17,8 +17,8 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "jshs2": "^0.4.4", "sasl-plain": "^0.1.0", "saslmechanisms": "^0.1.1", @@ -27,7 +27,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20" + "@cubejs-backend/linter": "1.6.21" }, "publishConfig": { "access": "public" diff --git a/packages/cubejs-jdbc-driver/CHANGELOG.md b/packages/cubejs-jdbc-driver/CHANGELOG.md index c6c646d9ca565..fa28f8a6228b6 100644 --- a/packages/cubejs-jdbc-driver/CHANGELOG.md +++ b/packages/cubejs-jdbc-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/jdbc-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/jdbc-driver diff --git a/packages/cubejs-jdbc-driver/package.json b/packages/cubejs-jdbc-driver/package.json index 5328d9b4bbf12..aded359ed7a9c 100644 --- a/packages/cubejs-jdbc-driver/package.json +++ b/packages/cubejs-jdbc-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/jdbc-driver", "description": "Cube.js JDBC database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -25,9 +25,9 @@ "index.js" ], "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", "@cubejs-backend/node-java-maven": "^0.1.3", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/shared": "1.6.21", "sqlstring": "^2.3.0" }, "optionalDependencies": { @@ -42,7 +42,7 @@ "testEnvironment": "node" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/node": "^20", "@types/sqlstring": "^2.3.0", "typescript": "~5.2.2" diff --git a/packages/cubejs-ksql-driver/CHANGELOG.md b/packages/cubejs-ksql-driver/CHANGELOG.md index 7431ca8763d88..35d999d13a960 100644 --- a/packages/cubejs-ksql-driver/CHANGELOG.md +++ b/packages/cubejs-ksql-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/ksql-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/ksql-driver diff --git a/packages/cubejs-ksql-driver/package.json b/packages/cubejs-ksql-driver/package.json index b186baeb9396c..a52742aeeb51d 100644 --- a/packages/cubejs-ksql-driver/package.json +++ b/packages/cubejs-ksql-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/ksql-driver", "description": "Cube.js ksql database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -25,9 +25,9 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "async-mutex": "0.3.2", "axios": "^1.8.3", "kafkajs": "^2.2.3", @@ -41,7 +41,7 @@ "extends": "../cubejs-linter" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "typescript": "~5.2.2" } } diff --git a/packages/cubejs-linter/CHANGELOG.md b/packages/cubejs-linter/CHANGELOG.md index f85baef1e1efd..170ce1a37a2be 100644 --- a/packages/cubejs-linter/CHANGELOG.md +++ b/packages/cubejs-linter/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/linter + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/linter diff --git a/packages/cubejs-linter/package.json b/packages/cubejs-linter/package.json index 3774467af598a..bdd415c97fecc 100644 --- a/packages/cubejs-linter/package.json +++ b/packages/cubejs-linter/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/linter", "description": "Cube.js ESLint (virtual package) for linting code", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", diff --git a/packages/cubejs-materialize-driver/CHANGELOG.md b/packages/cubejs-materialize-driver/CHANGELOG.md index f25b188c68407..6cf74eaab599e 100644 --- a/packages/cubejs-materialize-driver/CHANGELOG.md +++ b/packages/cubejs-materialize-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/materialize-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/materialize-driver diff --git a/packages/cubejs-materialize-driver/package.json b/packages/cubejs-materialize-driver/package.json index b3803a4482c49..114dcd7d2108d 100644 --- a/packages/cubejs-materialize-driver/package.json +++ b/packages/cubejs-materialize-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/materialize-driver", "description": "Cube.js Materialize database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,15 +27,15 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/postgres-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/postgres-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "semver": "^7.6.3" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing": "1.6.21", "typescript": "~5.2.2" }, "publishConfig": { diff --git a/packages/cubejs-mongobi-driver/CHANGELOG.md b/packages/cubejs-mongobi-driver/CHANGELOG.md index 50c4c30989d41..514cf1ec111bf 100644 --- a/packages/cubejs-mongobi-driver/CHANGELOG.md +++ b/packages/cubejs-mongobi-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/mongobi-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/mongobi-driver diff --git a/packages/cubejs-mongobi-driver/package.json b/packages/cubejs-mongobi-driver/package.json index 6ebfc4d327e15..20795369c2a7e 100644 --- a/packages/cubejs-mongobi-driver/package.json +++ b/packages/cubejs-mongobi-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mongobi-driver", "description": "Cube.js MongoBI driver", "author": "krunalsabnis@gmail.com", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,8 +27,8 @@ "integration:mongobi": "jest dist/test" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "@types/node": "^20", "moment": "^2.29.1", "mysql2": "^3.11.5" @@ -38,7 +38,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-mssql-driver/CHANGELOG.md b/packages/cubejs-mssql-driver/CHANGELOG.md index 1e95a48989f6d..4e26a7109b6d7 100644 --- a/packages/cubejs-mssql-driver/CHANGELOG.md +++ b/packages/cubejs-mssql-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/mssql-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/mssql-driver diff --git a/packages/cubejs-mssql-driver/package.json b/packages/cubejs-mssql-driver/package.json index 905a1d5da240c..93cf74d73a812 100644 --- a/packages/cubejs-mssql-driver/package.json +++ b/packages/cubejs-mssql-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mssql-driver", "description": "Cube.js MS SQL database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -25,8 +25,8 @@ "lint:fix": "eslint --fix src/* --ext .ts,.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "mssql": "^11.0.1" }, "devDependencies": { diff --git a/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md b/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md index 4624c897bf679..661d17817c46c 100644 --- a/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md +++ b/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/mysql-aurora-serverless-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/mysql-aurora-serverless-driver diff --git a/packages/cubejs-mysql-aurora-serverless-driver/package.json b/packages/cubejs-mysql-aurora-serverless-driver/package.json index c0eafb1fc2e37..80e9fd9ea63c0 100644 --- a/packages/cubejs-mysql-aurora-serverless-driver/package.json +++ b/packages/cubejs-mysql-aurora-serverless-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mysql-aurora-serverless-driver", "description": "Cube.js Aurora Serverless Mysql database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -21,14 +21,14 @@ "lint": "eslint driver/*.js test/*.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "@types/mysql": "^2.15.15", "aws-sdk": "^2.787.0", "data-api-client": "^1.1.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/data-api-client": "^1.2.1", "@types/jest": "^29", "jest": "^29", diff --git a/packages/cubejs-mysql-driver/CHANGELOG.md b/packages/cubejs-mysql-driver/CHANGELOG.md index 98e0617803f42..b2fb6b46dd628 100644 --- a/packages/cubejs-mysql-driver/CHANGELOG.md +++ b/packages/cubejs-mysql-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/mysql-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/mysql-driver diff --git a/packages/cubejs-mysql-driver/package.json b/packages/cubejs-mysql-driver/package.json index 2ed9e678d18e8..237c3b94fab4c 100644 --- a/packages/cubejs-mysql-driver/package.json +++ b/packages/cubejs-mysql-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mysql-driver", "description": "Cube.js Mysql database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,13 +27,13 @@ "lint:fix": "eslint --fix src/* test/* --ext .ts,.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "mysql": "^2.18.1" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", "@types/jest": "^29", "@types/mysql": "^2.15.21", "jest": "^29", diff --git a/packages/cubejs-oracle-driver/CHANGELOG.md b/packages/cubejs-oracle-driver/CHANGELOG.md index c0e4b712e2a1c..62615f90287eb 100644 --- a/packages/cubejs-oracle-driver/CHANGELOG.md +++ b/packages/cubejs-oracle-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/oracle-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/oracle-driver diff --git a/packages/cubejs-oracle-driver/package.json b/packages/cubejs-oracle-driver/package.json index 4ae20dc519eb8..99f609865dd4b 100644 --- a/packages/cubejs-oracle-driver/package.json +++ b/packages/cubejs-oracle-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/oracle-driver", "description": "Cube.js oracle database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -13,7 +13,7 @@ }, "main": "driver/OracleDriver.js", "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", "ramda": "^0.27.0" }, "optionalDependencies": { diff --git a/packages/cubejs-pinot-driver/CHANGELOG.md b/packages/cubejs-pinot-driver/CHANGELOG.md index eacef4d56b6bc..3eccc3fbb4441 100644 --- a/packages/cubejs-pinot-driver/CHANGELOG.md +++ b/packages/cubejs-pinot-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/pinot-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/pinot-driver diff --git a/packages/cubejs-pinot-driver/package.json b/packages/cubejs-pinot-driver/package.json index 25a054f5fb18b..001dfb1c786f4 100644 --- a/packages/cubejs-pinot-driver/package.json +++ b/packages/cubejs-pinot-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/pinot-driver", "description": "Cube.js Pinot database driver", "author": "Julian Ronsse, InTheMemory, Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,9 +27,9 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "node-fetch": "^2.6.1", "ramda": "^0.27.2", "sqlstring": "^2.3.3" @@ -39,7 +39,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "jest": "^29", "should": "^13.2.3", diff --git a/packages/cubejs-playground/CHANGELOG.md b/packages/cubejs-playground/CHANGELOG.md index cbbb39bdfea05..913de1f1b30fe 100644 --- a/packages/cubejs-playground/CHANGELOG.md +++ b/packages/cubejs-playground/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-client/playground + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-client/playground diff --git a/packages/cubejs-playground/package.json b/packages/cubejs-playground/package.json index 78f3dbc8b24e6..e1ab206d0f9fa 100644 --- a/packages/cubejs-playground/package.json +++ b/packages/cubejs-playground/package.json @@ -1,7 +1,7 @@ { "name": "@cubejs-client/playground", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "engines": {}, "repository": { "type": "git", @@ -69,8 +69,8 @@ "@ant-design/compatible": "^1.0.1", "@ant-design/icons": "^5.3.5", "@cube-dev/ui-kit": "0.52.3", - "@cubejs-client/core": "1.6.20", - "@cubejs-client/react": "1.6.20", + "@cubejs-client/core": "1.6.21", + "@cubejs-client/react": "1.6.21", "@types/flexsearch": "^0.7.3", "@types/node": "^20", "@types/react": "^18.3.4", diff --git a/packages/cubejs-postgres-driver/CHANGELOG.md b/packages/cubejs-postgres-driver/CHANGELOG.md index 72f1171326bce..b45a2dd844a6f 100644 --- a/packages/cubejs-postgres-driver/CHANGELOG.md +++ b/packages/cubejs-postgres-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/postgres-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/postgres-driver diff --git a/packages/cubejs-postgres-driver/package.json b/packages/cubejs-postgres-driver/package.json index 595d9a017bd59..4408ba86f903b 100644 --- a/packages/cubejs-postgres-driver/package.json +++ b/packages/cubejs-postgres-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/postgres-driver", "description": "Cube.js Postgres database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,8 +27,8 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "@types/pg": "^8.16.0", "@types/pg-query-stream": "^1.0.3", "moment": "^2.24.0", @@ -37,8 +37,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-prestodb-driver/CHANGELOG.md b/packages/cubejs-prestodb-driver/CHANGELOG.md index ba1667d993d29..931045ad8b8ba 100644 --- a/packages/cubejs-prestodb-driver/CHANGELOG.md +++ b/packages/cubejs-prestodb-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/prestodb-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/prestodb-driver diff --git a/packages/cubejs-prestodb-driver/package.json b/packages/cubejs-prestodb-driver/package.json index f90ebbf3ade8a..27eb29788a2c7 100644 --- a/packages/cubejs-prestodb-driver/package.json +++ b/packages/cubejs-prestodb-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/prestodb-driver", "description": "Cube.js Presto database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,8 +27,8 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "presto-client": "^1.1.0", "ramda": "^0.27.0", "sqlstring": "^2.3.1" @@ -38,7 +38,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "jest": "^29", "should": "^13.2.3", diff --git a/packages/cubejs-query-orchestrator/CHANGELOG.md b/packages/cubejs-query-orchestrator/CHANGELOG.md index ca061f4584648..147e810a76204 100644 --- a/packages/cubejs-query-orchestrator/CHANGELOG.md +++ b/packages/cubejs-query-orchestrator/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/query-orchestrator + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/query-orchestrator diff --git a/packages/cubejs-query-orchestrator/package.json b/packages/cubejs-query-orchestrator/package.json index 2a8ff2d7cbb02..37f5a74e8677b 100644 --- a/packages/cubejs-query-orchestrator/package.json +++ b/packages/cubejs-query-orchestrator/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/query-orchestrator", "description": "Cube.js Query Orchestrator and Cache", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,15 +29,15 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/cubestore-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/cubestore-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "csv-write-stream": "^2.0.0", "lru-cache": "^11.1.0", "ramda": "^0.27.2" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "@types/node": "^20", "@types/ramda": "^0.27.32", diff --git a/packages/cubejs-questdb-driver/CHANGELOG.md b/packages/cubejs-questdb-driver/CHANGELOG.md index 30a40dd5f1170..500a3525b6ecd 100644 --- a/packages/cubejs-questdb-driver/CHANGELOG.md +++ b/packages/cubejs-questdb-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/questdb-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/questdb-driver diff --git a/packages/cubejs-questdb-driver/package.json b/packages/cubejs-questdb-driver/package.json index e13e5ba24d87a..eef24eedd572b 100644 --- a/packages/cubejs-questdb-driver/package.json +++ b/packages/cubejs-questdb-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/questdb-driver", "description": "Cube.js QuestDB database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,9 +27,9 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "@types/pg": "^8.6.0", "moment": "^2.24.0", "pg": "^8.7.0", @@ -37,8 +37,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-redshift-driver/CHANGELOG.md b/packages/cubejs-redshift-driver/CHANGELOG.md index c2fbfc1633c2b..36801a6646232 100644 --- a/packages/cubejs-redshift-driver/CHANGELOG.md +++ b/packages/cubejs-redshift-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/redshift-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/redshift-driver diff --git a/packages/cubejs-redshift-driver/package.json b/packages/cubejs-redshift-driver/package.json index e1be1973e15b1..359e7e7a571a9 100644 --- a/packages/cubejs-redshift-driver/package.json +++ b/packages/cubejs-redshift-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/redshift-driver", "description": "Cube.js Redshift database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,13 +27,13 @@ "dependencies": { "@aws-sdk/client-redshift": "^3.22.0", "@aws-sdk/credential-providers": "^3.22.0", - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/postgres-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20" + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/postgres-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "typescript": "~5.2.2" }, "publishConfig": { diff --git a/packages/cubejs-schema-compiler/CHANGELOG.md b/packages/cubejs-schema-compiler/CHANGELOG.md index 00e4ac9748c0b..7de83372c44a3 100644 --- a/packages/cubejs-schema-compiler/CHANGELOG.md +++ b/packages/cubejs-schema-compiler/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/schema-compiler + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) ### Bug Fixes diff --git a/packages/cubejs-schema-compiler/package.json b/packages/cubejs-schema-compiler/package.json index 63184f245e542..52e7eb5d89e15 100644 --- a/packages/cubejs-schema-compiler/package.json +++ b/packages/cubejs-schema-compiler/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/schema-compiler", "description": "Cube schema compiler", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -40,8 +40,8 @@ "@babel/standalone": "^7.24", "@babel/traverse": "^7.24", "@babel/types": "^7.24", - "@cubejs-backend/native": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/native": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "antlr4": "^4.13.2", "camelcase": "^6.2.0", "cron-parser": "^4.9.0", @@ -60,9 +60,9 @@ }, "devDependencies": { "@clickhouse/client": "^1.12.0", - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/mssql-driver": "1.6.20", - "@cubejs-backend/query-orchestrator": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/mssql-driver": "1.6.21", + "@cubejs-backend/query-orchestrator": "1.6.21", "@types/babel__code-frame": "^7.0.6", "@types/babel__generator": "^7.6.8", "@types/babel__traverse": "^7.20.5", diff --git a/packages/cubejs-server-core/CHANGELOG.md b/packages/cubejs-server-core/CHANGELOG.md index e2e2f2772eee5..a34ddae558ca6 100644 --- a/packages/cubejs-server-core/CHANGELOG.md +++ b/packages/cubejs-server-core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/server-core + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/server-core diff --git a/packages/cubejs-server-core/package.json b/packages/cubejs-server-core/package.json index a702ccd9ea70c..27c5c82512419 100644 --- a/packages/cubejs-server-core/package.json +++ b/packages/cubejs-server-core/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/server-core", "description": "Cube.js base component to wire all backend components together", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,16 +29,16 @@ "unit": "jest --runInBand --forceExit --coverage dist/test" }, "dependencies": { - "@cubejs-backend/api-gateway": "1.6.20", - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/cloud": "1.6.20", - "@cubejs-backend/cubestore-driver": "1.6.20", + "@cubejs-backend/api-gateway": "1.6.21", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/cloud": "1.6.21", + "@cubejs-backend/cubestore-driver": "1.6.21", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/native": "1.6.20", - "@cubejs-backend/query-orchestrator": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", - "@cubejs-backend/templates": "1.6.20", + "@cubejs-backend/native": "1.6.21", + "@cubejs-backend/query-orchestrator": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", + "@cubejs-backend/templates": "1.6.21", "codesandbox-import-utils": "^2.1.12", "cross-spawn": "^7.0.1", "fs-extra": "^8.1.0", @@ -62,8 +62,8 @@ "ws": "^7.5.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-client/playground": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-client/playground": "1.6.21", "@types/cross-spawn": "^6.0.2", "@types/express": "^4.17.21", "@types/fs-extra": "^9.0.8", diff --git a/packages/cubejs-server/CHANGELOG.md b/packages/cubejs-server/CHANGELOG.md index de8a145a4a52a..99bec946a4e42 100644 --- a/packages/cubejs-server/CHANGELOG.md +++ b/packages/cubejs-server/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/server + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/server diff --git a/packages/cubejs-server/package.json b/packages/cubejs-server/package.json index c7bef2e389de9..5b0075e6276a3 100644 --- a/packages/cubejs-server/package.json +++ b/packages/cubejs-server/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/server", "description": "Cube.js all-in-one server", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "types": "index.d.ts", "repository": { "type": "git", @@ -40,11 +40,11 @@ "jest:shapshot": "jest --updateSnapshot test" }, "dependencies": { - "@cubejs-backend/cubestore-driver": "1.6.20", + "@cubejs-backend/cubestore-driver": "1.6.21", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/native": "1.6.20", - "@cubejs-backend/server-core": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/native": "1.6.21", + "@cubejs-backend/server-core": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "@oclif/color": "^1.0.0", "@oclif/command": "^1.8.13", "@oclif/config": "^1.18.2", @@ -61,8 +61,8 @@ "ws": "^7.1.2" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/query-orchestrator": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/query-orchestrator": "1.6.21", "@oclif/dev-cli": "^1.23.1", "@types/body-parser": "^1.19.0", "@types/cors": "^2.8.8", diff --git a/packages/cubejs-snowflake-driver/CHANGELOG.md b/packages/cubejs-snowflake-driver/CHANGELOG.md index 7c1b5396a9ec3..5deb31bab93d1 100644 --- a/packages/cubejs-snowflake-driver/CHANGELOG.md +++ b/packages/cubejs-snowflake-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/snowflake-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/snowflake-driver diff --git a/packages/cubejs-snowflake-driver/package.json b/packages/cubejs-snowflake-driver/package.json index d3ffb96a6804c..ba4e576bc3b19 100644 --- a/packages/cubejs-snowflake-driver/package.json +++ b/packages/cubejs-snowflake-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/snowflake-driver", "description": "Cube.js Snowflake database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -26,8 +26,8 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.726.0", - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "date-fns-timezone": "^0.1.4", "snowflake-sdk": "^2.2.0" }, @@ -39,7 +39,7 @@ "extends": "../cubejs-linter" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "typescript": "~5.2.2" } } diff --git a/packages/cubejs-sqlite-driver/CHANGELOG.md b/packages/cubejs-sqlite-driver/CHANGELOG.md index 905f2dde864f3..bb6da03a243cb 100644 --- a/packages/cubejs-sqlite-driver/CHANGELOG.md +++ b/packages/cubejs-sqlite-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/sqlite-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/sqlite-driver diff --git a/packages/cubejs-sqlite-driver/package.json b/packages/cubejs-sqlite-driver/package.json index 352fbae33e690..571188c2f8bb8 100644 --- a/packages/cubejs-sqlite-driver/package.json +++ b/packages/cubejs-sqlite-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/sqlite-driver", "description": "Cube.js Sqlite database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -18,13 +18,13 @@ "unit": "jest" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "sqlite3": "^5.1.7" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "jest": "^29" }, "publishConfig": { diff --git a/packages/cubejs-templates/CHANGELOG.md b/packages/cubejs-templates/CHANGELOG.md index ffbed8c7725a9..c9a1d8c062f54 100644 --- a/packages/cubejs-templates/CHANGELOG.md +++ b/packages/cubejs-templates/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/templates + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/templates diff --git a/packages/cubejs-templates/package.json b/packages/cubejs-templates/package.json index 25c890986dfdf..41eb239dd9bd7 100644 --- a/packages/cubejs-templates/package.json +++ b/packages/cubejs-templates/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/templates", - "version": "1.6.20", + "version": "1.6.21", "description": "Cube.js Templates helpers", "author": "Cube Dev, Inc.", "repository": { @@ -31,7 +31,7 @@ "extends": "../cubejs-linter" }, "dependencies": { - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/shared": "1.6.21", "cross-spawn": "^7.0.3", "decompress": "^4.2.1", "decompress-targz": "^4.1.1", @@ -41,7 +41,7 @@ "source-map-support": "^0.5.19" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "typescript": "~5.2.2" } } diff --git a/packages/cubejs-testing-drivers/CHANGELOG.md b/packages/cubejs-testing-drivers/CHANGELOG.md index e21e8787eeb11..15aa1da676dc5 100644 --- a/packages/cubejs-testing-drivers/CHANGELOG.md +++ b/packages/cubejs-testing-drivers/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/testing-drivers + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/testing-drivers diff --git a/packages/cubejs-testing-drivers/package.json b/packages/cubejs-testing-drivers/package.json index 7af2b41b8feb9..765a8cdef3aa1 100644 --- a/packages/cubejs-testing-drivers/package.json +++ b/packages/cubejs-testing-drivers/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/testing-drivers", - "version": "1.6.20", + "version": "1.6.21", "description": "Cube.js drivers test suite", "author": "Cube Dev, Inc.", "repository": { @@ -72,24 +72,24 @@ "dist/src" ], "dependencies": { - "@cubejs-backend/athena-driver": "1.6.20", - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/bigquery-driver": "1.6.20", - "@cubejs-backend/clickhouse-driver": "1.6.20", - "@cubejs-backend/cubestore-driver": "1.6.20", - "@cubejs-backend/databricks-jdbc-driver": "1.6.20", + "@cubejs-backend/athena-driver": "1.6.21", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/bigquery-driver": "1.6.21", + "@cubejs-backend/clickhouse-driver": "1.6.21", + "@cubejs-backend/cubestore-driver": "1.6.21", + "@cubejs-backend/databricks-jdbc-driver": "1.6.21", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/mssql-driver": "1.6.20", - "@cubejs-backend/mysql-driver": "1.6.20", - "@cubejs-backend/postgres-driver": "1.6.20", - "@cubejs-backend/query-orchestrator": "1.6.20", - "@cubejs-backend/server-core": "1.6.20", - "@cubejs-backend/shared": "1.6.20", - "@cubejs-backend/snowflake-driver": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", - "@cubejs-client/core": "1.6.20", - "@cubejs-client/ws-transport": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/mssql-driver": "1.6.21", + "@cubejs-backend/mysql-driver": "1.6.21", + "@cubejs-backend/postgres-driver": "1.6.21", + "@cubejs-backend/query-orchestrator": "1.6.21", + "@cubejs-backend/server-core": "1.6.21", + "@cubejs-backend/shared": "1.6.21", + "@cubejs-backend/snowflake-driver": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", + "@cubejs-client/core": "1.6.21", + "@cubejs-client/ws-transport": "1.6.21", "@jest/globals": "^29", "@types/jest": "^29", "@types/node": "^20", diff --git a/packages/cubejs-testing-shared/CHANGELOG.md b/packages/cubejs-testing-shared/CHANGELOG.md index e602d32598afa..39adfc4f64d36 100644 --- a/packages/cubejs-testing-shared/CHANGELOG.md +++ b/packages/cubejs-testing-shared/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/testing-shared + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/testing-shared diff --git a/packages/cubejs-testing-shared/package.json b/packages/cubejs-testing-shared/package.json index 7de3c762b24b3..f602f2b768542 100644 --- a/packages/cubejs-testing-shared/package.json +++ b/packages/cubejs-testing-shared/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/testing-shared", - "version": "1.6.20", + "version": "1.6.21", "description": "Cube.js Testing Helpers", "author": "Cube Dev, Inc.", "repository": { @@ -26,16 +26,16 @@ ], "dependencies": { "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/query-orchestrator": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/query-orchestrator": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "@testcontainers/kafka": "~10.28.0", "dedent": "^0.7.0", "node-fetch": "^2.6.7", "testcontainers": "^10.28.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@jest/globals": "^29", "@types/dedent": "^0.7.0", "@types/jest": "^29", diff --git a/packages/cubejs-testing/CHANGELOG.md b/packages/cubejs-testing/CHANGELOG.md index dcc2d8e5dec5b..30c4a8e57356b 100644 --- a/packages/cubejs-testing/CHANGELOG.md +++ b/packages/cubejs-testing/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/testing + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/testing diff --git a/packages/cubejs-testing/package.json b/packages/cubejs-testing/package.json index 09a94c7eed7f2..500b56277843c 100644 --- a/packages/cubejs-testing/package.json +++ b/packages/cubejs-testing/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/testing", - "version": "1.6.20", + "version": "1.6.21", "description": "Cube.js e2e tests", "author": "Cube Dev, Inc.", "repository": { @@ -99,15 +99,15 @@ "birdbox-fixtures" ], "dependencies": { - "@cubejs-backend/cubestore-driver": "1.6.20", + "@cubejs-backend/cubestore-driver": "1.6.21", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/ksql-driver": "1.6.20", - "@cubejs-backend/postgres-driver": "1.6.20", - "@cubejs-backend/query-orchestrator": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", - "@cubejs-client/ws-transport": "1.6.20", + "@cubejs-backend/ksql-driver": "1.6.21", + "@cubejs-backend/postgres-driver": "1.6.21", + "@cubejs-backend/query-orchestrator": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", + "@cubejs-client/ws-transport": "1.6.21", "dedent": "^0.7.0", "fs-extra": "^8.1.0", "http-proxy": "^1.18.1", @@ -118,8 +118,8 @@ }, "devDependencies": { "@4tw/cypress-drag-drop": "^1.6.0", - "@cubejs-backend/linter": "1.6.20", - "@cubejs-client/core": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-client/core": "1.6.21", "@jest/globals": "^29", "@types/dedent": "^0.7.0", "@types/http-proxy": "^1.17.5", diff --git a/packages/cubejs-trino-driver/CHANGELOG.md b/packages/cubejs-trino-driver/CHANGELOG.md index 2ae0bb739df3c..703b987a40325 100644 --- a/packages/cubejs-trino-driver/CHANGELOG.md +++ b/packages/cubejs-trino-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/trino-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/trino-driver diff --git a/packages/cubejs-trino-driver/package.json b/packages/cubejs-trino-driver/package.json index 00caf2017457f..4e80162e9fafc 100644 --- a/packages/cubejs-trino-driver/package.json +++ b/packages/cubejs-trino-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/trino-driver", "description": "Cube.js Trino database driver", "author": "Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,10 +27,10 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/prestodb-driver": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/prestodb-driver": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "node-fetch": "^2.6.1", "presto-client": "^1.1.0", "sqlstring": "^2.3.1" @@ -40,7 +40,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "jest": "^29", "testcontainers": "^10.28.0", diff --git a/packages/cubejs-vertica-driver/CHANGELOG.md b/packages/cubejs-vertica-driver/CHANGELOG.md index 199be7605d80c..3ab775fa16d8b 100644 --- a/packages/cubejs-vertica-driver/CHANGELOG.md +++ b/packages/cubejs-vertica-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +**Note:** Version bump only for package @cubejs-backend/vertica-driver + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) **Note:** Version bump only for package @cubejs-backend/vertica-driver diff --git a/packages/cubejs-vertica-driver/package.json b/packages/cubejs-vertica-driver/package.json index 831df2e93887a..a910b2e367e4c 100644 --- a/packages/cubejs-vertica-driver/package.json +++ b/packages/cubejs-vertica-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/vertica-driver", "description": "Cube.js Vertica database driver", "author": "Eduard Karacharov, Tim Brown, Cube Dev, Inc.", - "version": "1.6.20", + "version": "1.6.21", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -19,16 +19,16 @@ "lint:fix": "eslint --fix **/*.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.6.20", - "@cubejs-backend/query-orchestrator": "1.6.20", - "@cubejs-backend/schema-compiler": "1.6.20", - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/base-driver": "1.6.21", + "@cubejs-backend/query-orchestrator": "1.6.21", + "@cubejs-backend/schema-compiler": "1.6.21", + "@cubejs-backend/shared": "1.6.21", "vertica-nodejs": "^1.0.3" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", - "@cubejs-backend/testing-shared": "1.6.20", + "@cubejs-backend/linter": "1.6.21", + "@cubejs-backend/testing-shared": "1.6.21", "@types/jest": "^29", "jest": "^29", "testcontainers": "^10.28.0" diff --git a/rust/cubesql/CHANGELOG.md b/rust/cubesql/CHANGELOG.md index ca3a53578086a..d1cb1751dfb0b 100644 --- a/rust/cubesql/CHANGELOG.md +++ b/rust/cubesql/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +### Bug Fixes + +- **cubesql:** Support per-column format codes (Tableau Desktop compatibility) ([#10484](https://github.com/cube-js/cube/issues/10484)) ([d6ad885](https://github.com/cube-js/cube/commit/d6ad885f384d675f25ca8405dad4ee5a559e98c3)), closes [#10418](https://github.com/cube-js/cube/issues/10418) + +### Features + +- **cubesql:** Introduce `cube_cache` session variable ([#10483](https://github.com/cube-js/cube/issues/10483)) ([541aa34](https://github.com/cube-js/cube/commit/541aa3465f25cb55328249b95fa9c258c95f5e07)) + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) ### Bug Fixes diff --git a/rust/cubesql/package.json b/rust/cubesql/package.json index a1861d2019924..ae1296d557fd3 100644 --- a/rust/cubesql/package.json +++ b/rust/cubesql/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/cubesql", - "version": "1.6.20", + "version": "1.6.21", "description": "SQL API for Cube as proxy over MySQL protocol.", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" diff --git a/rust/cubestore/CHANGELOG.md b/rust/cubestore/CHANGELOG.md index acac98ffc8210..871d402a82be7 100644 --- a/rust/cubestore/CHANGELOG.md +++ b/rust/cubestore/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.6.21](https://github.com/cube-js/cube/compare/v1.6.20...v1.6.21) (2026-03-10) + +### Features + +- **cubestore:** Logarithmic probabilistic increment of LFU counter ([#10478](https://github.com/cube-js/cube/issues/10478)) ([4b8ac6f](https://github.com/cube-js/cube/commit/4b8ac6ff81b0ee114cd0bc4a5e8235fe294c53a2)) +- **cubestore:** Queue - add exclusivity support ([#10479](https://github.com/cube-js/cube/issues/10479)) ([b5a0ec3](https://github.com/cube-js/cube/commit/b5a0ec32c061834af0c30bd7def9097ae27af32f)) + ## [1.6.20](https://github.com/cube-js/cube/compare/v1.6.19...v1.6.20) (2026-03-09) ### Features diff --git a/rust/cubestore/package.json b/rust/cubestore/package.json index cbb91632acc2e..e92a085130e58 100644 --- a/rust/cubestore/package.json +++ b/rust/cubestore/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/cubestore", - "version": "1.6.20", + "version": "1.6.21", "description": "Cube.js pre-aggregation storage layer.", "main": "dist/src/index.js", "typings": "dist/src/index.d.ts", @@ -32,7 +32,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.6.20", + "@cubejs-backend/linter": "1.6.21", "@types/jest": "^29", "@types/node": "^18", "jest": "^29", @@ -42,7 +42,7 @@ "access": "public" }, "dependencies": { - "@cubejs-backend/shared": "1.6.20", + "@cubejs-backend/shared": "1.6.21", "@octokit/core": "^3.2.5", "source-map-support": "^0.5.19" },