Skip to content

Commit 45a65d8

Browse files
committed
fix(query): improve update handling about nondeterministic_update and add nullable columns
Added `replace_column_datatype_to_nullable` method to support updates involving nullable column types. Refactored `rewrite_nondeterministic_update` logic to use matcher-based pattern matching for enhanced clarity and maintainability. Updated tests to include scenarios with CTEs and nullable columns for improved test coverage. Handle non-nullable expressions in update bindings This update ensures non-nullable expressions are unified with nullable data types where required, maintaining consistency in type handling. Additionally, it includes error logging for cases when data type retrieval fails, improving debugging visibility.
1 parent c332b8b commit 45a65d8

File tree

3 files changed

+68
-22
lines changed

3 files changed

+68
-22
lines changed

Diff for: src/query/sql/src/planner/binder/bind_mutation/update.rs

+23-21
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ use crate::binder::bind_mutation::bind::MutationStrategy;
2828
use crate::binder::bind_mutation::mutation_expression::MutationExpression;
2929
use crate::binder::util::TableIdentifier;
3030
use crate::binder::Binder;
31+
use crate::optimizer::ir::Matcher;
3132
use crate::optimizer::ir::SExpr;
3233
use crate::plans::AggregateFunction;
3334
use crate::plans::BoundColumnRef;
3435
use crate::plans::Plan;
36+
use crate::plans::RelOp;
3537
use crate::plans::RelOperator;
3638
use crate::plans::ScalarItem;
3739
use crate::plans::VisitorMut;
@@ -118,17 +120,20 @@ impl Binder {
118120
let Plan::DataMutation { box s_expr, .. } = &plan else {
119121
return Ok(plan);
120122
};
121-
let RelOperator::Mutation(mutation) = &*s_expr.plan else {
123+
let RelOperator::Mutation(mutation) = s_expr.plan() else {
122124
return Ok(plan);
123125
};
124-
let filter_expr = &s_expr.children[0];
125-
let RelOperator::Filter(_) = &*filter_expr.plan else {
126-
return Ok(plan);
126+
let input_expr = s_expr.unary_child();
127+
let matcher = Matcher::MatchOp {
128+
op_type: RelOp::Filter,
129+
children: vec![Matcher::MatchOp {
130+
op_type: RelOp::Join,
131+
children: vec![Matcher::Leaf, Matcher::Leaf],
132+
}],
127133
};
128-
let input = &filter_expr.children[0];
129-
let RelOperator::Join(_) = &*input.plan else {
134+
if !matcher.matches(input_expr) {
130135
return Ok(plan);
131-
};
136+
}
132137

133138
let mut mutation = mutation.clone();
134139

@@ -176,7 +181,6 @@ impl Binder {
176181
.flat_map(|expr| expr.used_columns().into_iter())
177182
})
178183
})
179-
.chain(mutation.required_columns.iter().copied())
180184
.collect::<HashSet<_>>();
181185

182186
let used_columns = used_columns
@@ -201,7 +205,7 @@ impl Binder {
201205

202206
let display_name = format!("any({})", binding.index);
203207
let old = binding.index;
204-
let mut aggr_func = ScalarExpr::AggregateFunction(AggregateFunction {
208+
let mut aggr_func: ScalarExpr = AggregateFunction {
205209
span: None,
206210
func_name: "any".to_string(),
207211
distinct: false,
@@ -213,7 +217,8 @@ impl Binder {
213217
return_type: binding.data_type.clone(),
214218
sort_descs: vec![],
215219
display_name: display_name.clone(),
216-
});
220+
}
221+
.into();
217222

218223
let mut rewriter =
219224
AggregateRewriter::new(&mut mutation.bind_context, self.metadata.clone());
@@ -242,14 +247,14 @@ impl Binder {
242247
for eval in &mut mutation.matched_evaluators {
243248
if let Some(expr) = &mut eval.condition {
244249
for (_, old, new) in &aggr_columns {
245-
expr.replace_column(*old, *new)?
250+
expr.replace_column_datatype_to_nullable(*old, *new)?
246251
}
247252
}
248253

249254
if let Some(update) = &mut eval.update {
250255
for (_, expr) in update.iter_mut() {
251256
for (_, old, new) in &aggr_columns {
252-
expr.replace_column(*old, *new)?
257+
expr.replace_column_datatype_to_nullable(*old, *new)?
253258
}
254259
}
255260
}
@@ -270,26 +275,23 @@ impl Binder {
270275
.collect(),
271276
);
272277

273-
let aggr_expr = self.bind_aggregate(&mut mutation.bind_context, (**filter_expr).clone())?;
278+
let aggr_expr = self.bind_aggregate(&mut mutation.bind_context, input_expr.clone())?;
274279

275-
let s_expr = SExpr::create_unary(
280+
let s_expr = Box::new(SExpr::create_unary(
276281
Arc::new(RelOperator::Mutation(mutation)),
277282
Arc::new(aggr_expr),
278-
);
283+
));
279284

280285
let Plan::DataMutation {
281286
schema, metadata, ..
282287
} = plan
283288
else {
284289
unreachable!()
285290
};
286-
287-
let plan = Plan::DataMutation {
288-
s_expr: Box::new(s_expr),
291+
Ok(Plan::DataMutation {
292+
s_expr,
289293
schema,
290294
metadata,
291-
};
292-
293-
Ok(plan)
295+
})
294296
}
295297
}

Diff for: src/query/sql/src/planner/plans/scalar_expr.rs

+27
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,33 @@ impl ScalarExpr {
293293
Ok(())
294294
}
295295

296+
pub fn replace_column_datatype_to_nullable(
297+
&mut self,
298+
old: IndexType,
299+
new: IndexType,
300+
) -> Result<()> {
301+
struct Replace {
302+
old: IndexType,
303+
new: IndexType,
304+
}
305+
306+
impl VisitorMut<'_> for Replace {
307+
fn visit_bound_column_ref(&mut self, col: &mut BoundColumnRef) -> Result<()> {
308+
if col.column.index == self.old {
309+
col.column.index = self.new;
310+
if !col.column.data_type.is_nullable() {
311+
col.column.data_type = Box::new(col.column.data_type.wrap_nullable())
312+
}
313+
}
314+
Ok(())
315+
}
316+
}
317+
318+
let mut visitor = Replace { old, new };
319+
visitor.visit(self)?;
320+
Ok(())
321+
}
322+
296323
pub fn columns_and_data_types(&self, metadata: MetadataRef) -> HashMap<usize, DataType> {
297324
struct UsedColumnsVisitor {
298325
columns: HashMap<IndexType, DataType>,

Diff for: tests/sqllogictests/suites/query/cte/update_cte.test

+18-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,23 @@ select * from t2;
7979
statement error (?s)1065.*?column a doesn't exist
8080
with tt1 as (select * from t1) update t2 set a = tt1.a;
8181

82-
8382
statement ok
8483
drop table t2;
84+
85+
statement ok
86+
create or replace table test_merge(col1 varchar, col2 varchar, col3 varchar);
87+
88+
statement ok
89+
insert into test_merge values(2,'abc',2),(3,'abc',3),(4,'abc',4);
90+
91+
statement ok
92+
with tbb("col1", "col2", "col3") as (values ('1', 'add', '11'), ('4', 'add', '44')) update test_merge tba set tba.col1 =tbb.col1, tba.col2 = 'update', tba.col3 = tbb.col3 from tbb where tba.col1 = tbb.col1;
93+
94+
statement ok
95+
with tbb as (select col0::string null col1,col1::string null col2,col2::string null col3 from (values ('1', 'add', '11'), ('4', 'add', '44'))) update test_merge tba set tba.col1 =tbb.col1, tba.col2 = 'update', tba.col3 = tbb.col3 from tbb where tba.col1 = tbb.col1;
96+
97+
statement ok
98+
with tbb("col1", "col2", "col3") as (values ('1', 'add', '11'), ('4', 'add', '44')) update test_merge tba set tba.col1 =tbb.col1::string null, tba.col2 = 'update', tba.col3 = tbb.col3::string null from tbb where tba.col1 = tbb.col1::string null;
99+
100+
statement ok
101+
drop table test_merge;

0 commit comments

Comments
 (0)