diff --git a/src/query/sql/src/planner/binder/join.rs b/src/query/sql/src/planner/binder/join.rs index 22e2a8b6b04df..138a8e06aa634 100644 --- a/src/query/sql/src/planner/binder/join.rs +++ b/src/query/sql/src/planner/binder/join.rs @@ -56,13 +56,12 @@ impl Binder { pub(super) async fn bind_join( &mut self, bind_context: &BindContext, + left_context: BindContext, + right_context: BindContext, + left_child: SExpr, + right_child: SExpr, join: &common_ast::ast::Join, ) -> Result<(SExpr, BindContext)> { - let (left_child, left_context) = - self.bind_table_reference(bind_context, &join.left).await?; - let (right_child, right_context) = - self.bind_table_reference(bind_context, &join.right).await?; - check_duplicate_join_tables(&left_context, &right_context)?; let mut bind_context = bind_context.replace(); diff --git a/src/query/sql/src/planner/binder/table.rs b/src/query/sql/src/planner/binder/table.rs index 70998d70e2d37..472c574ff38f7 100644 --- a/src/query/sql/src/planner/binder/table.rs +++ b/src/query/sql/src/planner/binder/table.rs @@ -16,9 +16,11 @@ use std::collections::HashMap; use std::default::Default; use std::sync::Arc; +use async_recursion::async_recursion; use chrono::TimeZone; use chrono::Utc; use common_ast::ast::Indirection; +use common_ast::ast::Join; use common_ast::ast::SelectStmt; use common_ast::ast::SelectTarget; use common_ast::ast::Statement; @@ -112,7 +114,8 @@ impl Binder { .await } - pub(super) async fn bind_table_reference( + #[async_recursion] + async fn bind_single_table( &mut self, bind_context: &BindContext, table_ref: &TableReference, @@ -348,7 +351,6 @@ impl Binder { } Ok((s_expr, bind_context)) } - TableReference::Join { span: _, join } => self.bind_join(bind_context, join).await, TableReference::Subquery { span: _, subquery, @@ -382,6 +384,7 @@ impl Binder { self.bind_stage_table(bind_context, stage_info, files_info, alias, None) .await } + TableReference::Join { .. } => unreachable!(), } } @@ -431,6 +434,79 @@ impl Binder { } } + pub(super) async fn bind_table_reference( + &mut self, + bind_context: &BindContext, + table_ref: &TableReference, + ) -> Result<(SExpr, BindContext)> { + let mut current_ref = table_ref; + let current_ctx = bind_context; + + // Stack to keep track of the joins + let mut join_stack: Vec<&Join> = Vec::new(); + + // Traverse the table reference hierarchy to get to the innermost table + while let TableReference::Join { join, .. } = current_ref { + join_stack.push(join); + + // Check whether the right-hand side is a Join or a TableReference + match &*join.right { + TableReference::Join { .. } => { + // Traverse the right-hand side if the right-hand side is a Join + current_ref = &join.right; + } + _ => { + // Traverse the left-hand side if the right-hand side is a TableReference + current_ref = &join.left; + } + } + } + + // Bind the innermost table + // current_ref must be left table in its join + let (mut result_expr, mut result_ctx) = + self.bind_single_table(current_ctx, current_ref).await?; + + for join in join_stack.iter().rev() { + match &*join.right { + TableReference::Join { .. } => { + let (left_expr, left_ctx) = + self.bind_single_table(current_ctx, &join.left).await?; + let (join_expr, ctx) = self + .bind_join( + current_ctx, + left_ctx, + result_ctx, + left_expr, + result_expr, + join, + ) + .await?; + result_expr = join_expr; + result_ctx = ctx; + } + _ => { + let (right_expr, right_ctx) = + self.bind_single_table(current_ctx, &join.right).await?; + let (join_expr, ctx) = self + .bind_join( + current_ctx, + result_ctx, + right_ctx, + result_expr, + right_expr, + join, + ) + .await?; + result_expr = join_expr; + result_ctx = ctx; + } + } + } + + Ok((result_expr, result_ctx)) + } + async fn bind_cte( &mut self, bind_context: &BindContext, diff --git a/src/query/sql/src/planner/optimizer/optimizer.rs b/src/query/sql/src/planner/optimizer/optimizer.rs index b75d74d1a1823..9ecb5db796c25 100644 --- a/src/query/sql/src/planner/optimizer/optimizer.rs +++ b/src/query/sql/src/planner/optimizer/optimizer.rs @@ -149,10 +149,8 @@ pub fn optimize_query( let mut heuristic = HeuristicOptimizer::new(ctx.clone(), bind_context, metadata.clone()); let mut result = heuristic.optimize(s_expr)?; - let mut cascades = CascadesOptimizer::create(ctx.clone(), metadata)?; result = cascades.optimize(result)?; - // So far, we don't have ability to execute distributed query // with reading data from local tales(e.g. system tables). let enable_distributed_query = diff --git a/tests/sqllogictests/suites/mode/standalone/explain/join.test b/tests/sqllogictests/suites/mode/standalone/explain/join.test index c2b48e70f603b..22a2844892476 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/join.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/join.test @@ -547,3 +547,70 @@ drop table t1 statement ok drop table t2 + + +# https://github.com/datafuselabs/databend/issues/10523 +statement ok +create table t1 as select * from numbers(1) + +statement ok +set enable_cbo = 0 + +query I +select t1.number FROM +t1 +CROSS JOIN +t1 AS t2, +t1 AS t3, +t1 AS t4, +t1 AS t5, +t1 AS t6, +t1 AS t7, +t1 AS t8, +t1 AS t9, +t1 AS t10, +t1 AS t11, +t1 AS t12, +t1 AS t13, +t1 AS t14, +t1 AS t15, +t1 AS t16, +t1 AS t17, +t1 AS t18, +t1 AS t19, +t1 AS t20 +---- +0 + +query I +SELECT t20.number +FROM +(((((((((((((((((( +t1 AS t20 +CROSS JOIN t1 AS t19) +CROSS JOIN t1 AS t18) +CROSS JOIN t1 AS t17) +CROSS JOIN t1 AS t16) +CROSS JOIN t1 AS t15) +CROSS JOIN t1 AS t14) +CROSS JOIN t1 AS t13) +CROSS JOIN t1 AS t12) +CROSS JOIN t1 AS t11) +CROSS JOIN t1 AS t10) +CROSS JOIN t1 AS t9) +CROSS JOIN t1 AS t8) +CROSS JOIN t1 AS t7) +CROSS JOIN t1 AS t6) +CROSS JOIN t1 AS t5) +CROSS JOIN t1 AS t4) +CROSS JOIN t1 AS t3) +CROSS JOIN t1 AS t2) +CROSS JOIN t1 +---- +0 + +statement ok +set enable_cbo = 1 + +statement ok +drop table t1