@@ -236,6 +236,16 @@ bool FlattenDependentJoins::DetectCorrelatedExpressions(LogicalOperator &op, boo
236236 if (DetectCorrelatedExpressions (*child, lateral, new_lateral_depth, condition)) {
237237 has_correlation = true ;
238238 }
239+
240+ if (op.type == LogicalOperatorType::LOGICAL_MATERIALIZED_CTE && child_idx == 0 ) {
241+ auto &setop = op.Cast <LogicalCTE>();
242+ binder.recursive_ctes [setop.table_index ] = &setop;
243+ has_correlated_expressions[op] = has_correlation;
244+ if (has_correlation) {
245+ setop.correlated_columns = correlated_columns;
246+ }
247+ }
248+
239249 child_idx++;
240250 }
241251
@@ -261,6 +271,7 @@ bool FlattenDependentJoins::DetectCorrelatedExpressions(LogicalOperator &op, boo
261271 return true ;
262272 }
263273 // Found a materialized CTE, subtree correlation depends on the CTE node
274+ has_correlated_expressions[op] = has_correlated_expressions[*cte_node];
264275 return has_correlated_expressions[*cte_node];
265276 }
266277 // No CTE found: subtree is correlated
@@ -279,47 +290,32 @@ bool FlattenDependentJoins::DetectCorrelatedExpressions(LogicalOperator &op, boo
279290 binder.recursive_ctes [setop.table_index ] = &setop;
280291 if (has_correlation) {
281292 setop.correlated_columns = correlated_columns;
282- MarkSubtreeCorrelated (*op.children [1 ].get ());
283- }
284- }
285-
286- if (op.type == LogicalOperatorType::LOGICAL_MATERIALIZED_CTE) {
287- auto &setop = op.Cast <LogicalCTE>();
288- binder.recursive_ctes [setop.table_index ] = &setop;
289- // only mark the entire subtree as correlated if the materializing side is correlated
290- auto entry = has_correlated_expressions.find (*op.children [0 ]);
291- if (entry != has_correlated_expressions.end ()) {
292- if (has_correlation && entry->second ) {
293- setop.correlated_columns = correlated_columns;
294- MarkSubtreeCorrelated (*op.children [1 ].get ());
295- }
293+ MarkSubtreeCorrelated (*op.children [1 ].get (), setop.table_index );
296294 }
297295 }
298296
299297 return has_correlation;
300298}
301299
302- bool FlattenDependentJoins::MarkSubtreeCorrelated (LogicalOperator &op) {
300+ bool FlattenDependentJoins::MarkSubtreeCorrelated (LogicalOperator &op, idx_t cte_index ) {
303301 // Do not mark base table scans as correlated
304302 auto entry = has_correlated_expressions.find (op);
305303 D_ASSERT (entry != has_correlated_expressions.end ());
306304 bool has_correlation = entry->second ;
307305 for (auto &child : op.children ) {
308- has_correlation |= MarkSubtreeCorrelated (*child.get ());
306+ has_correlation |= MarkSubtreeCorrelated (*child.get (), cte_index );
309307 }
310308 if (op.type != LogicalOperatorType::LOGICAL_GET || op.children .size () == 1 ) {
311309 if (op.type == LogicalOperatorType::LOGICAL_CTE_REF) {
312310 // There may be multiple recursive CTEs. Only mark CTE_REFs as correlated,
313311 // IFF the CTE that we are reading from is correlated.
314312 auto &cteref = op.Cast <LogicalCTERef>();
315- auto cte = binder.recursive_ctes .find (cteref.cte_index );
316- bool has_correlation = false ;
317- if (cte != binder.recursive_ctes .end ()) {
318- auto &rec_cte = cte->second ->Cast <LogicalCTE>();
319- has_correlation = !rec_cte.correlated_columns .empty ();
313+ if (cteref.cte_index != cte_index) {
314+ has_correlated_expressions[op] = has_correlation;
315+ return has_correlation;
320316 }
321- has_correlated_expressions[op] = has_correlation ;
322- return has_correlation ;
317+ has_correlated_expressions[op] = true ;
318+ return true ;
323319 } else {
324320 has_correlated_expressions[op] = has_correlation;
325321 }
@@ -695,6 +691,42 @@ unique_ptr<LogicalOperator> FlattenDependentJoins::PushDownDependentJoinInternal
695691 return plan;
696692 }
697693 } else if (join.join_type == JoinType::MARK) {
694+ if (!left_has_correlation && right_has_correlation) {
695+ // found a MARK join where the left side has no correlation
696+
697+ ColumnBinding right_binding;
698+
699+ // there may still be correlation on the right side that we have to deal with
700+ // push into the right side if necessary or decorrelate it independently otherwise
701+ plan->children [1 ] = PushDownDependentJoinInternal (std::move (plan->children [1 ]),
702+ parent_propagate_null_values, lateral_depth);
703+ right_binding = this ->base_binding ;
704+
705+ // now push into the left side of the MARK join even though it has no correlation
706+ // this is necessary to add the correlated columns to the column bindings and allow
707+ // the join condition to be rewritten correctly
708+ plan->children [0 ] = PushDownDependentJoinInternal (std::move (plan->children [0 ]),
709+ parent_propagate_null_values, lateral_depth);
710+
711+ auto left_binding = this ->base_binding ;
712+
713+ // add the correlated columns to the join conditions
714+ for (idx_t i = 0 ; i < correlated_columns.size (); i++) {
715+ JoinCondition cond;
716+ cond.left = make_uniq<BoundColumnRefExpression>(
717+ correlated_columns[i].type ,
718+ ColumnBinding (left_binding.table_index , left_binding.column_index + i));
719+ cond.right = make_uniq<BoundColumnRefExpression>(
720+ correlated_columns[i].type ,
721+ ColumnBinding (right_binding.table_index , right_binding.column_index + i));
722+ cond.comparison = ExpressionType::COMPARE_NOT_DISTINCT_FROM;
723+
724+ auto &comparison_join = join.Cast <LogicalComparisonJoin>();
725+ comparison_join.conditions .push_back (std::move (cond));
726+ }
727+ return plan;
728+ }
729+
698730 // push the child into the LHS
699731 plan->children [0 ] = PushDownDependentJoinInternal (std::move (plan->children [0 ]),
700732 parent_propagate_null_values, lateral_depth);
0 commit comments