diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index 9415f397af2c7..5247fe5d437f6 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -1855,6 +1855,7 @@ export class BaseQuery { 'collectSubQueryDimensionsFor' ) ), inlineWhereConditions); + return `SELECT ${this.selectAllDimensionsAndMeasures(measures)} FROM ${ query } ${this.baseWhere(filters.concat(inlineWhereConditions))}` + @@ -4001,6 +4002,23 @@ export class BaseQuery { ); } + filtersProxyForRust(usedFilters) { + const filters = this.extractFiltersAsTree(usedFilters || []); + const allFilters = filters.map(this.initFilter.bind(this)); + return BaseQuery.filterProxyFromAllFilters( + allFilters, + this.cubeEvaluator, + this.paramAllocator.allocateParam.bind(this.paramAllocator), + this.newGroupFilter.bind(this), + ); + } + + filterGroupFunctionForRust(usedFilters) { + const filters = this.extractFiltersAsTree(usedFilters || []); + const allFilters = filters.map(this.initFilter.bind(this)); + return this.filterGroupFunctionImpl(allFilters); + } + static renderFilterParams(filter, filterParamArgs, allocateParam, newGroupFilter, aliases) { if (!filter) { return BaseFilter.ALWAYS_TRUE; @@ -4046,6 +4064,10 @@ export class BaseQuery { filterGroupFunction() { const { allFilters } = this; + return this.filterGroupFunctionImpl(allFilters); + } + + filterGroupFunctionImpl(allFilters) { const allocateParam = this.paramAllocator.allocateParam.bind(this.paramAllocator); const newGroupFilter = this.newGroupFilter.bind(this); return (...filterParamArgs) => { diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts index 1fda04d9c0705..e11b7f01b3af3 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts @@ -1,3 +1,4 @@ +import { getEnv } from '@cubejs-backend/shared'; import { UserError } from '../../../src/compiler/UserError'; import { PostgresQuery } from '../../../src/adapter/PostgresQuery'; import { BigqueryQuery } from '../../../src/adapter/BigqueryQuery'; @@ -312,9 +313,12 @@ describe('SQL Generation', () => { cube('visitor_checkins', { sql: \` - select * from visitor_checkins WHERE - \${FILTER_PARAMS.visitor_checkins.created_at.filter('created_at')} AND - \${FILTER_GROUP(FILTER_PARAMS.visitor_checkins.created_at.filter("(created_at - INTERVAL '3 DAY')"), FILTER_PARAMS.visitor_checkins.source.filter('source'))} + select visitor_checkins.* from visitor_checkins left join visitors on visitor_checkins.visitor_id = visitors.id WHERE + \${FILTER_PARAMS.visitor_checkins.created_at.filter('visitor_checkins.created_at')} AND + \${FILTER_GROUP(FILTER_PARAMS.visitor_checkins.created_at.filter("(visitor_checkins.created_at - INTERVAL '3 DAY')"), FILTER_PARAMS.visitor_checkins.source.filter('visitor_checkins.source'))} + AND \${SECURITY_CONTEXT.source.filter('visitors.source')} AND + \${SECURITY_CONTEXT.sourceArray.filter(sourceArray => \`visitors.source in (\${sourceArray.join(',')})\`)} + \`, sql_alias: \`vc\`, @@ -567,6 +571,84 @@ describe('SQL Generation', () => { }, } }); + + cube('rollingWindowDates', { + sql: \` + SELECT cast('2024-01-13' AS timestamp) as time UNION ALL + SELECT cast('2024-02-13' AS timestamp) as time UNION ALL + SELECT cast('2024-03-13' AS timestamp) as time UNION ALL + SELECT cast('2024-04-13' AS timestamp) as time UNION ALL + SELECT cast('2024-05-13' AS timestamp) as time UNION ALL + SELECT cast('2024-06-13' AS timestamp) as time UNION ALL + SELECT cast('2024-07-13' AS timestamp) as time UNION ALL + SELECT cast('2024-08-13' AS timestamp) as time UNION ALL + SELECT cast('2024-09-13' AS timestamp) as time UNION ALL + SELECT cast('2024-10-13' AS timestamp) as time UNION ALL + SELECT cast('2024-11-13' AS timestamp) as time UNION ALL + SELECT cast('2024-12-13' AS timestamp) as time + \`, + + dimensions: { + time: { + type: 'time', + sql: 'time', + primaryKey: true + } + } + }); + + cube('rollingWindowTest', { + sql: \` +SELECT 1 AS revenue, cast('2024-01-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-02-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-03-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-04-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-05-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-06-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-07-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-08-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-09-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-10-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-11-01' AS timestamp) as time UNION ALL + SELECT 1 AS revenue, cast('2024-12-01' AS timestamp) as time + \`, + + dimensions: { + time: { + type: 'time', + sql: 'time', + primaryKey: true + } + }, + measures: { + revenue: { + sql: 'revenue', + type: 'sum', + filters: [{ + sql: \`\${rollingWindowDates.time} <= current_date\` + }] + }, + revenue_ytd: { + sql: \`\${CUBE.revenue}\`, + type: 'sum', + rolling_window: { + type: 'to_date', + granularity: 'year' + } + }, + revenue_ms: { + sql: \`\${CUBE.revenue}\`, + type: 'sum', + multi_stage: true, + }, + }, + joins: { + rollingWindowDates: { + relationship: 'manyToOne', + sql: \`\${CUBE}.time = date_trunc('month', \${rollingWindowDates.time})\` + } + } + }); `); it('simple join', async () => { @@ -1966,7 +2048,7 @@ describe('SQL Generation', () => { ])); it( - 'contains filter 1', + 'contains filter', () => runQueryTest({ measures: [], dimensions: [ @@ -2761,7 +2843,7 @@ describe('SQL Generation', () => { ] )); - it('rank measure 1', async () => runQueryTest( + it('rank measure', async () => runQueryTest( { measures: ['visitors.revenue_rank'], }, @@ -3152,6 +3234,37 @@ describe('SQL Generation', () => { }] )); + if (getEnv('nativeSqlPlanner')) { + it('nested aggregations with filtered measures and rolling windows', async () => runQueryTest( + { + measures: ['rollingWindowTest.revenue_ms'], + }, + [{ + rolling_window_test__revenue_ms: '12' + }] + )); + } + + it('multiplied sum and count no dimensions through view', async () => runQueryTest( + { + measures: ['visitors_visitors_checkins_view.revenue', 'visitors_visitors_checkins_view.visitor_checkins_count'], + }, + [{ + visitors_visitors_checkins_view__revenue: '2000', + visitors_visitors_checkins_view__visitor_checkins_count: '6' + }] + )); + + it('multiplied sum no dimensions through view', async () => runQueryTest( + { + measures: ['visitors_visitors_checkins_view.revenue', 'visitors_visitors_checkins_view.id_sum'], + }, + [{ + visitors_visitors_checkins_view__revenue: '2000', + visitors_visitors_checkins_view__id_sum: '21' + }] + )); + // Subquery aggregation for multiplied measure (and any `keysSelect` for that matter) // should pick up all dimensions, even through member expressions it('multiplied sum with dimension member expressions', async () => runQueryTest( diff --git a/rust/cubenativeutils/src/wrappers/context.rs b/rust/cubenativeutils/src/wrappers/context.rs index 9695b30717ec3..805e7923ffa54 100644 --- a/rust/cubenativeutils/src/wrappers/context.rs +++ b/rust/cubenativeutils/src/wrappers/context.rs @@ -6,6 +6,7 @@ pub trait NativeContext<IT: InnerTypes>: Clone { fn string(&self, v: String) -> Result<IT::String, CubeError>; fn number(&self, v: f64) -> Result<IT::Number, CubeError>; fn undefined(&self) -> Result<NativeObjectHandle<IT>, CubeError>; + fn null(&self) -> Result<NativeObjectHandle<IT>, CubeError>; fn empty_array(&self) -> Result<IT::Array, CubeError>; fn empty_struct(&self) -> Result<IT::Struct, CubeError>; //fn boxed<T: 'static>(&self, value: T) -> impl NativeBox<IT, T>; @@ -36,6 +37,9 @@ impl<IT: InnerTypes> NativeContextHolder<IT> { pub fn undefined(&self) -> Result<NativeObjectHandle<IT>, CubeError> { self.context.undefined() } + pub fn null(&self) -> Result<NativeObjectHandle<IT>, CubeError> { + self.context.null() + } pub fn empty_array(&self) -> Result<IT::Array, CubeError> { self.context.empty_array() } diff --git a/rust/cubenativeutils/src/wrappers/neon/context.rs b/rust/cubenativeutils/src/wrappers/neon/context.rs index aff6f55a693e3..10be1a53f98a2 100644 --- a/rust/cubenativeutils/src/wrappers/neon/context.rs +++ b/rust/cubenativeutils/src/wrappers/neon/context.rs @@ -144,6 +144,13 @@ impl<C: Context<'static> + 'static> NativeContext<NeonInnerTypes<C>> for Context ))) } + fn null(&self) -> Result<NativeObjectHandle<NeonInnerTypes<C>>, CubeError> { + Ok(NativeObjectHandle::new(NeonObject::new( + self.clone(), + self.with_context(|cx| cx.null().upcast())?, + ))) + } + fn empty_array(&self) -> Result<NeonArray<C>, CubeError> { let obj = NeonObject::new( self.clone(), diff --git a/rust/cubenativeutils/src/wrappers/serializer/serializer.rs b/rust/cubenativeutils/src/wrappers/serializer/serializer.rs index 6570df1e3ff5d..f8ee638db7d8d 100644 --- a/rust/cubenativeutils/src/wrappers/serializer/serializer.rs +++ b/rust/cubenativeutils/src/wrappers/serializer/serializer.rs @@ -138,7 +138,7 @@ impl<IT: InnerTypes> ser::Serializer for NativeSerdeSerializer<IT> { } fn serialize_none(self) -> Result<Self::Ok, Self::Error> { - Ok(self.context.undefined()?) + Ok(self.context.null()?) } fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error> diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs index e5d75e4fab299..1e87da9363565 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs @@ -23,7 +23,7 @@ pub struct TimeDimension { pub struct FilterItem { pub or: Option<Vec<FilterItem>>, pub and: Option<Vec<FilterItem>>, - member: Option<String>, + pub member: Option<String>, pub dimension: Option<String>, pub operator: Option<String>, pub values: Option<Vec<Option<String>>>, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs index 0ccc1567be747..94ec487c28637 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs @@ -1,3 +1,4 @@ +use super::base_query_options::FilterItem; use super::filter_group::{FilterGroup, NativeFilterGroup}; use super::filter_params::{FilterParams, NativeFilterParams}; use super::member_sql::{MemberSql, NativeMemberSql}; @@ -36,8 +37,14 @@ pub trait BaseTools { ) -> Result<Vec<CallDep>, CubeError>; fn security_context_for_rust(&self) -> Result<Rc<dyn SecurityContext>, CubeError>; fn sql_utils_for_rust(&self) -> Result<Rc<dyn SqlUtils>, CubeError>; - fn filters_proxy(&self) -> Result<Rc<dyn FilterParams>, CubeError>; - fn filter_group_function(&self) -> Result<Rc<dyn FilterGroup>, CubeError>; + fn filters_proxy_for_rust( + &self, + used_filters: Option<Vec<FilterItem>>, + ) -> Result<Rc<dyn FilterParams>, CubeError>; + fn filter_group_function_for_rust( + &self, + used_filters: Option<Vec<FilterItem>>, + ) -> Result<Rc<dyn FilterGroup>, CubeError>; fn timestamp_precision(&self) -> Result<u32, CubeError>; fn in_db_time_zone(&self, date: String) -> Result<String, CubeError>; fn generate_time_series( diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs index 0631a868cec13..4312aa48fd867 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs @@ -193,11 +193,11 @@ impl SelectBuilder { Select { projection_columns: self.projection_columns, from: self.from, - filter: self.filter, + filter: self.filter.clone(), group_by: self.group_by, having: self.having, order_by: self.order_by, - context: Rc::new(VisitorContext::new(&nodes_factory)), + context: Rc::new(VisitorContext::new(&nodes_factory, self.filter)), ctes: self.ctes, is_distinct: self.is_distinct, limit: self.limit, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/compiler.rs index e918fbb8d1b46..43109bd5ffb6e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/compiler.rs @@ -129,7 +129,7 @@ impl<'a> FilterCompiler<'a> { } else { Err(CubeError::user(format!( "Member and operator attributes is required for filter" - ))) //TODO pring condition + ))) //TODO print condition } } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/filter_operator.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/filter_operator.rs index 99d946e462b98..a713e1dee58e9 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/filter_operator.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/filter_operator.rs @@ -53,3 +53,31 @@ impl FromStr for FilterOperator { } } } + +impl ToString for FilterOperator { + fn to_string(&self) -> String { + let str = match self { + FilterOperator::Equal => "equals", + FilterOperator::NotEqual => "notEquals", + FilterOperator::InDateRange => "inDateRange", + FilterOperator::RegularRollingWindowDateRange => "inDateRange", + FilterOperator::ToDateRollingWindowDateRange => "inDateRange", + FilterOperator::In => "in", + FilterOperator::NotIn => "notIn", + FilterOperator::Set => "set", + FilterOperator::NotSet => "notSet", + FilterOperator::Gt => "gt", + FilterOperator::Gte => "gte", + FilterOperator::Lt => "lt", + FilterOperator::Lte => "lte", + FilterOperator::Contains => "contains", + FilterOperator::NotContains => "notContains", + FilterOperator::StartsWith => "startsWith", + FilterOperator::NotStartsWith => "notStartsWith", + FilterOperator::NotEndsWith => "notEndsWith", + FilterOperator::EndsWith => "endsWith", + FilterOperator::MeasureFilter => "measureFilter", + }; + str.to_string() + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/applied_state.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/applied_state.rs index a60b755293819..1401d33a08533 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/applied_state.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/applied_state.rs @@ -173,6 +173,7 @@ impl MultiStageAppliedState { &self.time_dimensions_filters, &operator, &values, + &None, ); } @@ -188,6 +189,24 @@ impl MultiStageAppliedState { &self.time_dimensions_filters, &operator, &values, + &None, + ); + } + + pub fn replace_range_in_date_filter( + &mut self, + member_name: &String, + new_from: String, + new_to: String, + ) { + let operator = FilterOperator::InDateRange; + let replacement_values = vec![Some(new_from), Some(new_to)]; + self.time_dimensions_filters = self.change_date_range_filter_impl( + member_name, + &self.time_dimensions_filters, + &operator, + &vec![], + &Some(replacement_values), ); } @@ -197,6 +216,7 @@ impl MultiStageAppliedState { filters: &Vec<FilterItem>, operator: &FilterOperator, additional_values: &Vec<Option<String>>, + replacement_values: &Option<Vec<Option<String>>>, ) -> Vec<FilterItem> { let mut result = Vec::new(); for item in filters.iter() { @@ -209,6 +229,7 @@ impl MultiStageAppliedState { filters, operator, additional_values, + replacement_values, ), ))); result.push(new_group); @@ -217,7 +238,11 @@ impl MultiStageAppliedState { let itm = if &itm.member_name() == member_name && matches!(itm.filter_operator(), FilterOperator::InDateRange) { - let mut values = itm.values().clone(); + let mut values = if let Some(values) = replacement_values { + values.clone() + } else { + itm.values().clone() + }; values.extend(additional_values.iter().cloned()); itm.change_operator(operator.clone(), values) } else { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs index 0a9218832cc63..79529e600c4a6 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs @@ -248,7 +248,26 @@ impl MultiStageQueryPlanner { &time_dimension.get_granularity(), )?; - new_state.change_time_dimension_granularity(&time_dimension_name, result_granularity); + if time_dimension.get_date_range().is_some() && result_granularity.is_some() { + let granularity = time_dimension.get_granularity().unwrap(); //FIXME remove this unwrap + let date_range = time_dimension.get_date_range().unwrap(); //FIXME remove this unwrap + let series = self + .query_tools + .base_tools() + .generate_time_series(granularity, date_range.clone())?; + if !series.is_empty() { + let new_from_date = series.first().unwrap()[0].clone(); + let new_to_date = series.last().unwrap()[1].clone(); + new_state.replace_range_in_date_filter( + &time_dimension_name, + new_from_date, + new_to_date, + ); + } + } + + new_state + .change_time_dimension_granularity(&time_dimension_name, result_granularity.clone()); if let Some(granularity) = self.get_to_date_rolling_granularity(rolling_window)? { new_state.replace_to_date_date_range_filter(&time_dimension_name, &granularity); @@ -375,9 +394,9 @@ impl MultiStageQueryPlanner { } let childs = member_childs(&member)?; - - let description = if childs.is_empty() { - if has_multi_stage_members(&member, false)? { + let has_multi_stage_members = has_multi_stage_members(&member, false)?; + let description = if childs.is_empty() || !has_multi_stage_members { + if has_multi_stage_members { return Err(CubeError::internal(format!( "Leaf multi stage query cannot contain multi stage member" ))); 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 63a9a906f4805..a084abbc6f790 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs @@ -1,7 +1,9 @@ use super::dependecy::{ContextSymbolDep, CubeDepProperty, CubeDependency, Dependency}; use super::sql_nodes::SqlNode; use super::{symbols::MemberSymbol, SqlEvaluatorVisitor}; +use crate::cube_bridge::base_query_options::FilterItem as NativeFilterItem; use crate::cube_bridge::member_sql::{ContextSymbolArg, MemberSql, MemberSqlArg, MemberSqlStruct}; +use crate::plan::{Filter, FilterItem}; use crate::planner::query_tools::QueryTools; use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::CubeError; @@ -167,7 +169,7 @@ impl SqlCall { templates, ), Dependency::ContextDependency(contex_symbol) => { - self.apply_context_symbol(contex_symbol, query_tools.clone()) + self.apply_context_symbol(visitor, contex_symbol, query_tools.clone()) } } } @@ -208,6 +210,7 @@ impl SqlCall { pub fn apply_context_symbol( &self, + visitor: &SqlEvaluatorVisitor, context_symbol: &ContextSymbolDep, query_tools: Rc<QueryTools>, ) -> Result<MemberSqlArg, CubeError> { @@ -217,16 +220,72 @@ impl SqlCall { query_tools.base_tools().security_context_for_rust()?, )) } - ContextSymbolDep::FilterParams => MemberSqlArg::ContextSymbol( - ContextSymbolArg::FilterParams(query_tools.base_tools().filters_proxy()?), - ), - ContextSymbolDep::FilterGroup => MemberSqlArg::ContextSymbol( - ContextSymbolArg::FilterGroup(query_tools.base_tools().filter_group_function()?), - ), + ContextSymbolDep::FilterParams => { + let filters = visitor.all_filters(); + let native_filters = self.filters_to_native_filter_item(filters); + let r = query_tools + .base_tools() + .filters_proxy_for_rust(native_filters)?; + MemberSqlArg::ContextSymbol(ContextSymbolArg::FilterParams(r)) + } + ContextSymbolDep::FilterGroup => { + let filters = visitor.all_filters(); + let native_filters = self.filters_to_native_filter_item(filters); + let r = query_tools + .base_tools() + .filter_group_function_for_rust(native_filters)?; + MemberSqlArg::ContextSymbol(ContextSymbolArg::FilterGroup(r)) + } ContextSymbolDep::SqlUtils => MemberSqlArg::ContextSymbol(ContextSymbolArg::SqlUtils( query_tools.base_tools().sql_utils_for_rust()?, )), }; Ok(res) } + + fn filters_to_native_filter_item( + &self, + filter: Option<Filter>, + ) -> Option<Vec<NativeFilterItem>> { + if let Some(filter) = filter { + let mut res = Vec::new(); + for item in filter.items.iter() { + res.push(self.filters_to_native_filter_item_impl(item)); + } + Some(res) + } else { + None + } + } + + fn filters_to_native_filter_item_impl(&self, filter_item: &FilterItem) -> NativeFilterItem { + match filter_item { + FilterItem::Group(group) => { + let mut native_items = Vec::new(); + for itm in group.items.iter() { + native_items.push(self.filters_to_native_filter_item_impl(itm)); + } + let (or, and) = match group.operator { + crate::plan::filter::FilterGroupOperator::Or => (Some(native_items), None), + crate::plan::filter::FilterGroupOperator::And => (None, Some(native_items)), + }; + NativeFilterItem { + or, + and, + member: None, + dimension: None, + operator: None, + values: None, + } + } + FilterItem::Item(filter) => NativeFilterItem { + or: None, + and: None, + member: Some(filter.member_name()), + dimension: None, + operator: Some(filter.filter_operator().to_string()), + values: Some(filter.values().clone()), + }, + } + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_visitor.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_visitor.rs index 2d9087c91c6e7..24d39e7326a10 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_visitor.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_visitor.rs @@ -1,5 +1,6 @@ use super::sql_nodes::SqlNode; use super::MemberSymbol; +use crate::plan::Filter; use crate::planner::query_tools::QueryTools; use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::CubeError; @@ -8,11 +9,19 @@ use std::rc::Rc; #[derive(Clone)] pub struct SqlEvaluatorVisitor { query_tools: Rc<QueryTools>, + all_filters: Option<Filter>, //To pass to FILTER_PARAMS and FILTER_GROUP } impl SqlEvaluatorVisitor { - pub fn new(query_tools: Rc<QueryTools>) -> Self { - Self { query_tools } + pub fn new(query_tools: Rc<QueryTools>, all_filters: Option<Filter>) -> Self { + Self { + query_tools, + all_filters, + } + } + + pub fn all_filters(&self) -> Option<Filter> { + self.all_filters.clone() } pub fn apply( 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 442e52312cb45..c8eefc7e22ea3 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 @@ -62,6 +62,15 @@ impl MemberSymbol { } } + pub fn is_multi_stage(&self) -> bool { + match self { + Self::Dimension(d) => d.is_multi_stage(), + Self::TimeDimension(d) => d.is_multi_stage(), + Self::Measure(m) => m.is_multi_stage(), + _ => false, + } + } + pub fn is_measure(&self) -> bool { matches!(self, Self::Measure(_)) } 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 590584e872730..82a14a8c84d2a 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 @@ -46,6 +46,10 @@ impl TimeDimensionSymbol { self.base_symbol.cube_name() } + pub fn is_multi_stage(&self) -> bool { + self.base_symbol.is_multi_stage() + } + pub fn name(&self) -> String { self.base_symbol.name() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs index 0eba5e2f4592a..1e0e9986e29bd 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs @@ -1,6 +1,7 @@ use super::query_tools::QueryTools; use super::sql_evaluator::sql_nodes::{SqlNode, SqlNodesFactory}; use super::sql_evaluator::{MemberSymbol, SqlCall}; +use crate::plan::Filter; use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::CubeError; @@ -8,17 +9,19 @@ use std::rc::Rc; pub struct VisitorContext { node_processor: Rc<dyn SqlNode>, + all_filters: Option<Filter>, //To pass to FILTER_PARAMS and FILTER_GROUP } impl VisitorContext { - pub fn new(nodes_factory: &SqlNodesFactory) -> Self { + pub fn new(nodes_factory: &SqlNodesFactory, all_filters: Option<Filter>) -> Self { Self { node_processor: nodes_factory.default_node_processor(), + all_filters, } } pub fn make_visitor(&self, query_tools: Rc<QueryTools>) -> SqlEvaluatorVisitor { - SqlEvaluatorVisitor::new(query_tools) + SqlEvaluatorVisitor::new(query_tools, self.all_filters.clone()) } pub fn node_processor(&self) -> Rc<dyn SqlNode> {