Skip to content

Commit 7e11c45

Browse files
committed
fix(query): match nullable scalar correlation keys
1 parent d4f3935 commit 7e11c45

8 files changed

Lines changed: 346 additions & 270 deletions

File tree

src/query/sql/src/planner/optimizer/optimizers/hyper_dp/dphyp.rs

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ use super::algorithm::JoinEdgeRef;
2727
use super::algorithm::JoinNode;
2828
use super::algorithm::JoinOrderModel;
2929
use crate::IndexType;
30-
use crate::ScalarExpr;
3130
use crate::optimizer::Optimizer;
3231
use crate::optimizer::OptimizerContext;
3332
use crate::optimizer::ir::RelExpr;
@@ -59,7 +58,7 @@ pub struct DPhpyOptimizer {
5958

6059
struct DPhypJoinOrderModel<'a> {
6160
join_relations: &'a [JoinRelation],
62-
join_conditions: &'a [(ScalarExpr, ScalarExpr)],
61+
join_conditions: &'a [JoinEquiCondition],
6362
}
6463

6564
impl DPhypJoinOrderModel<'_> {
@@ -71,17 +70,14 @@ impl DPhypJoinOrderModel<'_> {
7170
) -> SExpr {
7271
let left_expr = left.state().clone();
7372
let right_expr = right.state().clone();
74-
let mut left_conditions = Vec::with_capacity(edge_refs.len());
75-
let mut right_conditions = Vec::with_capacity(edge_refs.len());
73+
let mut conditions = Vec::with_capacity(edge_refs.len());
7674

7775
for edge_ref in edge_refs {
78-
let (mut left_condition, mut right_condition) =
79-
self.join_conditions[edge_ref.id].clone();
76+
let mut condition = self.join_conditions[edge_ref.id].clone();
8077
if edge_ref.reversed {
81-
std::mem::swap(&mut left_condition, &mut right_condition);
78+
std::mem::swap(&mut condition.left, &mut condition.right);
8279
}
83-
left_conditions.push(left_condition);
84-
right_conditions.push(right_condition);
80+
conditions.push(condition);
8581
}
8682

8783
let join_type = if edge_refs.is_empty() {
@@ -90,11 +86,7 @@ impl DPhypJoinOrderModel<'_> {
9086
JoinType::Inner
9187
};
9288
let rel_op = RelOperator::Join(Join {
93-
equi_conditions: JoinEquiCondition::new_conditions(
94-
left_conditions,
95-
right_conditions,
96-
vec![],
97-
),
89+
equi_conditions: conditions,
9890
non_equi_conditions: vec![],
9991
join_type,
10092
marker_index: None,
@@ -269,7 +261,7 @@ impl DPhpyOptimizer {
269261
async fn process_join_node(
270262
&mut self,
271263
s_expr: &SExpr,
272-
join_conditions: &mut Vec<(ScalarExpr, ScalarExpr)>,
264+
join_conditions: &mut Vec<JoinEquiCondition>,
273265
) -> Result<(Arc<SExpr>, bool)> {
274266
let op = match s_expr.plan() {
275267
RelOperator::Join(op) => op,
@@ -304,7 +296,7 @@ impl DPhpyOptimizer {
304296
break;
305297
}
306298

307-
join_conditions.push((condition.left.clone(), condition.right.clone()));
299+
join_conditions.push(condition.clone());
308300
}
309301

310302
// Add non-equi conditions to filters
@@ -571,7 +563,7 @@ impl DPhpyOptimizer {
571563
async fn process_unary_node(
572564
&mut self,
573565
s_expr: &SExpr,
574-
join_conditions: &mut Vec<(ScalarExpr, ScalarExpr)>,
566+
join_conditions: &mut Vec<JoinEquiCondition>,
575567
join_child: bool,
576568
join_relation: Option<&SExpr>,
577569
) -> Result<(Arc<SExpr>, bool)> {
@@ -611,7 +603,7 @@ impl DPhpyOptimizer {
611603
async fn get_base_relations(
612604
&mut self,
613605
s_expr: &SExpr,
614-
join_conditions: &mut Vec<(ScalarExpr, ScalarExpr)>,
606+
join_conditions: &mut Vec<JoinEquiCondition>,
615607
join_child: bool,
616608
join_relation: Option<&SExpr>,
617609
is_subquery: bool,
@@ -673,7 +665,6 @@ impl DPhpyOptimizer {
673665
}
674666

675667
// Firstly, we need to extract all join conditions and base tables
676-
// `join_condition` is pair, left is left_condition, right is right_condition
677668
let mut join_conditions = vec![];
678669
let (s_expr, optimized) = self
679670
.get_base_relations(s_expr, &mut join_conditions, false, None, false)
@@ -715,18 +706,18 @@ impl DPhpyOptimizer {
715706
fn build_join_order_edges(
716707
&self,
717708
hyper_dp: &mut HyperDp<'_, DPhypJoinOrderModel<'_>>,
718-
join_conditions: &[(ScalarExpr, ScalarExpr)],
709+
join_conditions: &[JoinEquiCondition],
719710
) -> Result<bool> {
720-
for (edge_id, (left_condition, right_condition)) in join_conditions.iter().enumerate() {
711+
for (edge_id, condition) in join_conditions.iter().enumerate() {
721712
let mut left_relation_set = HashSet::new();
722713
let mut right_relation_set = HashSet::new();
723714

724-
let left_used_tables = left_condition.used_tables()?;
715+
let left_used_tables = condition.left.used_tables()?;
725716
for table in left_used_tables.iter() {
726717
left_relation_set.insert(self.table_index_map[table]);
727718
}
728719

729-
let right_used_tables = right_condition.used_tables()?;
720+
let right_used_tables = condition.right.used_tables()?;
730721
for table in right_used_tables.iter() {
731722
right_relation_set.insert(self.table_index_map[table]);
732723
}
@@ -904,7 +895,7 @@ mod tests {
904895
use crate::plans::MaterializedCTERef;
905896
use crate::plans::Sequence;
906897

907-
fn bool_constant(value: bool) -> ScalarExpr {
898+
fn bool_constant(value: bool) -> crate::ScalarExpr {
908899
ConstantExpr {
909900
span: None,
910901
value: Scalar::Boolean(value),

src/query/sql/src/planner/optimizer/optimizers/operator/decorrelate/decorrelate.rs

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,12 @@ impl SubqueryDecorrelatorOptimizer {
275275
&mut left_conditions,
276276
)?;
277277

278+
// These conditions reconnect the flattened subquery result to the outer row.
279+
// They are internal correlation keys rather than user-written equality
280+
// predicates, so NULL correlation groups must match each other.
281+
let is_null_equal =
282+
Self::nullable_condition_indexes(&left_conditions, &right_conditions);
283+
278284
let join_type = if matches!(subquery.contain_agg, Some(true)) && {
279285
let rel_expr = RelExpr::with_s_expr(&subquery.subquery);
280286
rel_expr
@@ -292,7 +298,7 @@ impl SubqueryDecorrelatorOptimizer {
292298
equi_conditions: JoinEquiCondition::new_conditions(
293299
left_conditions,
294300
right_conditions,
295-
vec![],
301+
is_null_equal,
296302
),
297303
non_equi_conditions: vec![],
298304
join_type,
@@ -339,16 +345,8 @@ impl SubqueryDecorrelatorOptimizer {
339345
&mut left_conditions,
340346
&mut right_conditions,
341347
)?;
342-
let mut is_null_equal = Vec::new();
343-
for (i, (l, r)) in left_conditions
344-
.iter()
345-
.zip(right_conditions.iter())
346-
.enumerate()
347-
{
348-
if l.data_type().is_nullable() || r.data_type().is_nullable() {
349-
is_null_equal.push(i);
350-
}
351-
}
348+
let is_null_equal =
349+
Self::nullable_condition_indexes(&left_conditions, &right_conditions);
352350

353351
let marker_index = if let Some(idx) = subquery.projection_index {
354352
idx
@@ -404,16 +402,8 @@ impl SubqueryDecorrelatorOptimizer {
404402
&mut right_conditions,
405403
)?;
406404

407-
let mut is_null_equal = Vec::new();
408-
for (i, (l, r)) in left_conditions
409-
.iter()
410-
.zip(right_conditions.iter())
411-
.enumerate()
412-
{
413-
if l.data_type().is_nullable() || r.data_type().is_nullable() {
414-
is_null_equal.push(i);
415-
}
416-
}
405+
let is_null_equal =
406+
Self::nullable_condition_indexes(&left_conditions, &right_conditions);
417407

418408
let output_column = subquery.output_column.clone();
419409
let column_name = format!("subquery_{}", output_column.index);
@@ -522,6 +512,20 @@ impl SubqueryDecorrelatorOptimizer {
522512
Ok(())
523513
}
524514

515+
pub(crate) fn nullable_condition_indexes(
516+
left_conditions: &[ScalarExpr],
517+
right_conditions: &[ScalarExpr],
518+
) -> Vec<usize> {
519+
left_conditions
520+
.iter()
521+
.zip(right_conditions)
522+
.enumerate()
523+
.filter_map(|(index, (left, right))| {
524+
(left.data_type().is_nullable() || right.data_type().is_nullable()).then_some(index)
525+
})
526+
.collect()
527+
}
528+
525529
// Check if need to join outer and inner table
526530
// If correlated_columns only occur in equi-conditions, such as `where t1.a = t.a and t1.b = t.b`(t1 is outer table)
527531
// Then we won't join outer and inner table.

src/query/sql/src/planner/optimizer/optimizers/operator/decorrelate/subquery_decorrelator.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -736,16 +736,8 @@ impl SubqueryDecorrelatorOptimizer {
736736
)
737737
};
738738

739-
let mut is_null_equal = Vec::new();
740-
for (i, (l, r)) in left_conditions
741-
.iter()
742-
.zip(right_conditions.iter())
743-
.enumerate()
744-
{
745-
if l.data_type().is_nullable() || r.data_type().is_nullable() {
746-
is_null_equal.push(i);
747-
}
748-
}
739+
let is_null_equal =
740+
Self::nullable_condition_indexes(&left_conditions, &right_conditions);
749741

750742
// Consider the sql: select * from t1 where t1.a = any(select t2.a from t2);
751743
// Will be transferred to:select t1.a, t2.a, marker_index from t1, t2 where t2.a = t1.a;

0 commit comments

Comments
 (0)