diff --git a/rust/cubesql/cubesql/src/compile/mod.rs b/rust/cubesql/cubesql/src/compile/mod.rs index e12459f789d2..b483e6e6e733 100644 --- a/rust/cubesql/cubesql/src/compile/mod.rs +++ b/rust/cubesql/cubesql/src/compile/mod.rs @@ -1445,6 +1445,13 @@ struct QueryPlanner { session_manager: Arc, } +lazy_static! { + static ref METABASE_WORKAROUND: regex::Regex = regex::Regex::new( + r#"SELECT true AS "_" FROM "public"\."(?P.*)" WHERE 1 <> 1 LIMIT 0"# + ) + .unwrap(); +} + impl QueryPlanner { pub fn new( state: Arc, @@ -1466,6 +1473,24 @@ impl QueryPlanner { stmt: &ast::Statement, q: &Box, ) -> CompilationResult { + // Metabase + if let Some(c) = METABASE_WORKAROUND.captures(&stmt.to_string()) { + let tblname = c.name("tblname").unwrap().as_str(); + if self.meta.find_cube_with_name(tblname).is_some() { + return Ok(QueryPlan::MetaTabular( + StatusFlags::empty(), + Box::new(dataframe::DataFrame::new( + vec![dataframe::Column::new( + "_".to_string(), + ColumnType::Int8, + ColumnFlags::empty(), + )], + vec![], + )), + )); + } + } + // TODO move CUBESQL_REWRITE_ENGINE env to config let rewrite_engine = env::var("CUBESQL_REWRITE_ENGINE") .ok() @@ -1619,7 +1644,7 @@ impl QueryPlanner { )); }; - if let Some(cube) = self.meta.find_cube_with_name(table_name.clone()) { + if let Some(cube) = self.meta.find_cube_with_name(&table_name) { let mut ctx = QueryContext::new(&cube); let mut builder = compile_select(select, &mut ctx)?; @@ -8696,6 +8721,21 @@ ORDER BY \"COUNT(count)\" DESC" Ok(()) } + #[tokio::test] + async fn test_metabase_table_exists() -> Result<(), CubeError> { + insta::assert_snapshot!( + "metabase_table_exists", + execute_query( + r#"SELECT TRUE AS "_" FROM "public"."KibanaSampleDataEcommerce" WHERE 1 <> 1 LIMIT 0;"# + .to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await? + ); + + Ok(()) + } + #[tokio::test] async fn test_subquery_with_same_name_excel() -> Result<(), CubeError> { insta::assert_snapshot!( diff --git a/rust/cubesql/cubesql/src/compile/rewrite/rules/filters.rs b/rust/cubesql/cubesql/src/compile/rewrite/rules/filters.rs index ab8859350f6e..e1cfa4241a6c 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/rules/filters.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/rules/filters.rs @@ -1279,7 +1279,7 @@ impl FilterRules { for cube in var_iter!(egraph[subst[cube_var]], FilterReplacerCube) { if let Some(cube) = cube .as_ref() - .and_then(|cube| meta_context.find_cube_with_name(cube.to_string())) + .and_then(|cube| meta_context.find_cube_with_name(cube)) { for column in var_iter!(egraph[subst[column_var]], ColumnExprColumn).cloned() { for table_name in diff --git a/rust/cubesql/cubesql/src/compile/rewrite/rules/split.rs b/rust/cubesql/cubesql/src/compile/rewrite/rules/split.rs index 86ee8b754ebf..4517af230c33 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/rules/split.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/rules/split.rs @@ -570,7 +570,7 @@ impl SplitRules { for fun in var_iter!(egraph[subst[fun_expr_var]], AggregateFunctionExprFun).cloned() { if fun == AggregateFunction::Min || fun == AggregateFunction::Max { - if let Some(cube) = meta.find_cube_with_name(cube.to_string()) { + if let Some(cube) = meta.find_cube_with_name(&cube) { for column in var_iter!(egraph[subst[column_var]], ColumnExprColumn) { if let Some(dimension) = cube.lookup_dimension(&column.name) { if is_time_dimension && dimension._type == "time" @@ -614,7 +614,7 @@ impl SplitRules { ) .cloned() { - if let Some(cube) = meta.find_cube_with_name(cube) { + if let Some(cube) = meta.find_cube_with_name(&cube) { for column in column_var .map(|column_var| { var_iter!(egraph[subst[column_var]], ColumnExprColumn) @@ -646,7 +646,7 @@ impl SplitRules { ) .cloned() { - if let Some(cube) = meta.find_cube_with_name(cube) { + if let Some(cube) = meta.find_cube_with_name(&cube) { if cube .lookup_measure(&MemberRules::default_count_measure_name()) .is_none() @@ -769,7 +769,7 @@ impl SplitRules { var_iter!(egraph[subst[cube_var]], OuterAggregateSplitReplacerCube).cloned() { if let Some(name) = original_expr_name(egraph, subst[original_expr_var]) { - if let Some(cube) = meta.find_cube_with_name(cube) { + if let Some(cube) = meta.find_cube_with_name(&cube) { for column in var_iter!(egraph[subst[column_var]], ColumnExprColumn).cloned() { @@ -830,7 +830,7 @@ impl SplitRules { for cube in var_iter!(egraph[subst[cube_var]], OuterAggregateSplitReplacerCube).cloned() { - if let Some(cube) = meta.find_cube_with_name(cube) { + if let Some(cube) = meta.find_cube_with_name(&cube) { if cube .lookup_measure(&MemberRules::default_count_measure_name()) .is_none() @@ -861,7 +861,7 @@ impl SplitRules { for cube in var_iter!(egraph[subst[cube_var]], OuterAggregateSplitReplacerCube).cloned() { if let Some(name) = original_expr_name(egraph, subst[original_expr_var]) { - if let Some(cube) = meta.find_cube_with_name(cube) { + if let Some(cube) = meta.find_cube_with_name(&cube) { for column in var_iter!(egraph[subst[column_var]], ColumnExprColumn).cloned() { diff --git a/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__metabase_table_exists.snap b/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__metabase_table_exists.snap new file mode 100644 index 000000000000..8b21fe76b6e2 --- /dev/null +++ b/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__metabase_table_exists.snap @@ -0,0 +1,9 @@ +--- +source: cubesql/src/compile/mod.rs +assertion_line: 8726 +expression: "execute_query(r#\"SELECT TRUE AS \"_\" FROM \"public\".\"KibanaSampleDataEcommerce\" WHERE 1 <> 1 LIMIT 0;\"#.to_string(),\n DatabaseProtocol::PostgreSQL).await?" +--- ++---+ +| _ | ++---+ ++---+ diff --git a/rust/cubesql/cubesql/src/transport/ctx.rs b/rust/cubesql/cubesql/src/transport/ctx.rs index 790655dc6fb7..6930c4462a60 100644 --- a/rust/cubesql/cubesql/src/transport/ctx.rs +++ b/rust/cubesql/cubesql/src/transport/ctx.rs @@ -57,7 +57,7 @@ impl MetaContext { Self { cubes, tables } } - pub fn find_cube_with_name(&self, name: String) -> Option { + pub fn find_cube_with_name(&self, name: &str) -> Option { for cube in self.cubes.iter() { if cube.name.eq(&name) { return Some(cube.clone()); @@ -69,7 +69,7 @@ impl MetaContext { pub fn find_measure_with_name(&self, name: String) -> Option { let cube_and_member_name = name.split(".").collect::>(); - if let Some(cube) = self.find_cube_with_name(cube_and_member_name[0].to_string()) { + if let Some(cube) = self.find_cube_with_name(cube_and_member_name[0]) { cube.lookup_measure(cube_and_member_name[1]).cloned() } else { None @@ -77,7 +77,7 @@ impl MetaContext { } pub fn find_df_data_type(&self, member_name: String) -> Option { - self.find_cube_with_name(member_name.split(".").next()?.to_string())? + self.find_cube_with_name(member_name.split(".").next()?)? .df_data_type(member_name.as_str()) }