This is an automated email from the ASF dual-hosted git repository.
morrySnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 3e878ed0676 [fix](analyzer) Preserve bound signatures during insert
folding (#68154)
3e878ed0676 is described below
commit 3e878ed06764c53f6f1af3573a8de0b4fc8f5fa8
Author: morrySnow <[email protected]>
AuthorDate: Fri Sep 18 16:56:03 2026 +0800
[fix](analyzer) Preserve bound signatures during insert folding (#68154)
### What problem does this PR solve?
Problem Summary:
INSERT value normalization directly folds already bound expressions
without enabling `keepFunctionSignature`. When `repeat` inside `struct`
is folded from an always-nullable function to a non-null string literal,
`CreateStruct.withChildren` can recompute the nested field nullability
while surrounding array and cast nodes retain their bound types. FE then
sends an inconsistent nested column layout and BE aborts when casting
the string column to `ColumnNullable`.
Preserve the bound function signature around both post-bind
constant-folding entry points in `InsertUtils`. For the fast INSERT
VALUES analyzer, split binding jobs from post-bind jobs: binding remains
under `keepFunctionSignature(false)`, while ordinary and batch post-bind
rewrites run under `keepFunctionSignature(true)`. Add regression
coverage for both fast and normal INSERT VALUES analysis paths.
### Release note
Fix BE crashes when inserting arrays of structs containing foldable
nullable functions.
---
.../jobs/executor/AbstractBatchJobExecutor.java | 7 ++-
.../commands/insert/InsertIntoValuesAnalyzer.java | 36 ++++++++++++---
.../trees/plans/commands/insert/InsertUtils.java | 11 ++++-
...test_insert_array_struct_function_signature.out | 5 +++
...t_insert_array_struct_function_signature.groovy | 52 ++++++++++++++++++++++
5 files changed, 103 insertions(+), 8 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/AbstractBatchJobExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/AbstractBatchJobExecutor.java
index 8cd266e3b73..f0c7515834f 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/AbstractBatchJobExecutor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/AbstractBatchJobExecutor.java
@@ -142,7 +142,12 @@ public abstract class AbstractBatchJobExecutor {
* execute.
*/
public void execute() {
- List<RewriteJob> jobs = Lists.newArrayList(getJobs());
+ executeJobs(getJobs());
+ }
+
+ /** Execute the specified rewrite jobs. */
+ protected void executeJobs(List<RewriteJob> rewriteJobs) {
+ List<RewriteJob> jobs = Lists.newArrayList(rewriteJobs);
for (int i = 0; i < jobs.size(); i++) {
JobContext jobContext = cascadesContext.getCurrentJobContext();
RewriteJob currentJob = jobs.get(i);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoValuesAnalyzer.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoValuesAnalyzer.java
index 772f0c5f001..aee33ba5774 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoValuesAnalyzer.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoValuesAnalyzer.java
@@ -39,6 +39,7 @@ import
org.apache.doris.nereids.trees.plans.algebra.SetOperation.Qualifier;
import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.nereids.util.MoreFieldsThread;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -47,10 +48,15 @@ import java.util.List;
/** InsertIntoValuesAnalyzer */
public class InsertIntoValuesAnalyzer extends AbstractBatchJobExecutor {
- public static final List<RewriteJob> INSERT_JOBS = jobs(
+ private static final List<RewriteJob> BINDING_JOBS = jobs(
bottomUp(
new InlineTableToUnionOrOneRowRelation(),
- new BindSink(),
+ new BindSink()
+ )
+ );
+
+ private static final List<RewriteJob> POST_BIND_JOBS = jobs(
+ bottomUp(
new MergeProjectable(),
// after bind olap table sink, the LogicalProject will be
generated under LogicalOlapTableSink,
// we should convert the agg state function in the
project, and evaluate some env parameters
@@ -63,10 +69,8 @@ public class InsertIntoValuesAnalyzer extends
AbstractBatchJobExecutor {
)
);
- public static final List<RewriteJob> BATCH_INSERT_JOBS = jobs(
+ private static final List<RewriteJob> BATCH_INSERT_POST_BIND_JOBS = jobs(
bottomUp(
- new InlineTableToUnionOrOneRowRelation(),
- new BindSink(),
new MergeProjectable(),
// the BatchInsertIntoTableCommand need send StringLiteral
to backend,
@@ -84,6 +88,12 @@ public class InsertIntoValuesAnalyzer extends
AbstractBatchJobExecutor {
)
);
+ public static final List<RewriteJob> INSERT_JOBS =
ImmutableList.<RewriteJob>builder()
+ .addAll(BINDING_JOBS).addAll(POST_BIND_JOBS).build();
+
+ public static final List<RewriteJob> BATCH_INSERT_JOBS =
ImmutableList.<RewriteJob>builder()
+ .addAll(BINDING_JOBS).addAll(BATCH_INSERT_POST_BIND_JOBS).build();
+
private final boolean batchInsert;
public InsertIntoValuesAnalyzer(CascadesContext cascadesContext, boolean
batchInsert) {
@@ -96,6 +106,22 @@ public class InsertIntoValuesAnalyzer extends
AbstractBatchJobExecutor {
return batchInsert ? BATCH_INSERT_JOBS : INSERT_JOBS;
}
+ @Override
+ public void execute() {
+ MoreFieldsThread.keepFunctionSignature(false, () -> {
+ executeJobs(BINDING_JOBS);
+ return null;
+ });
+ MoreFieldsThread.keepFunctionSignature(() -> {
+ executeJobs(getPostBindJobs());
+ return null;
+ });
+ }
+
+ private List<RewriteJob> getPostBindJobs() {
+ return batchInsert ? BATCH_INSERT_POST_BIND_JOBS : POST_BIND_JOBS;
+ }
+
private static class RewriteInsertIntoExpressions extends
ExpressionRewrite {
public RewriteInsertIntoExpressions(ExpressionRewriteRule... rules) {
super(rules);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
index aee5f6565fb..848c7059a32 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
@@ -67,6 +67,7 @@ import
org.apache.doris.nereids.trees.plans.logical.UnboundLogicalSink;
import org.apache.doris.nereids.types.AggStateType;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.VarcharType;
+import org.apache.doris.nereids.util.MoreFieldsThread;
import org.apache.doris.nereids.util.RelationUtil;
import org.apache.doris.nereids.util.TypeCoercionUtils;
import org.apache.doris.proto.InternalService;
@@ -535,7 +536,7 @@ public class InsertUtils {
if (expr.child(0).getDataType() instanceof AggStateType) {
expr = ConvertAggStateCast.convert((Cast) expr);
} else {
- expr = FoldConstantRuleOnFE.evaluate(expr, context);
+ expr = foldConstantAfterBind(expr, context);
}
}
return expr;
@@ -612,11 +613,17 @@ public class InsertUtils {
);
value = rewriteContext == null
? value
- : (NamedExpression) FoldConstantRuleOnFE.evaluate(value,
rewriteContext);
+ : (NamedExpression) foldConstantAfterBind(value,
rewriteContext);
}
optimizedRowConstructor.add(value);
}
+ private static Expression foldConstantAfterBind(
+ Expression expression, ExpressionRewriteContext rewriteContext) {
+ return MoreFieldsThread.keepFunctionSignature(
+ () -> FoldConstantRuleOnFE.evaluate(expression,
rewriteContext));
+ }
+
private static NamedExpression castValue(Expression value, DataType
targetType) {
if (value instanceof Alias) {
Expression oldChild = value.child(0);
diff --git
a/regression-test/data/query_p0/insert_into_table/test_insert_array_struct_function_signature.out
b/regression-test/data/query_p0/insert_into_table/test_insert_array_struct_function_signature.out
new file mode 100644
index 00000000000..0ebc86a88c6
--- /dev/null
+++
b/regression-test/data/query_p0/insert_into_table/test_insert_array_struct_function_signature.out
@@ -0,0 +1,5 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !array_struct_function_signature --
+1 [{"i":100, "s":"xx"}, {"i":200, "s":"yy"}]
+2 [{"i":100, "s":"xx"}, {"i":200, "s":"yy"}]
+
diff --git
a/regression-test/suites/query_p0/insert_into_table/test_insert_array_struct_function_signature.groovy
b/regression-test/suites/query_p0/insert_into_table/test_insert_array_struct_function_signature.groovy
new file mode 100644
index 00000000000..370b4b750a1
--- /dev/null
+++
b/regression-test/suites/query_p0/insert_into_table/test_insert_array_struct_function_signature.groovy
@@ -0,0 +1,52 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_insert_array_struct_function_signature") {
+ sql "drop table if exists array_struct_function_signature"
+ sql """
+ create table array_struct_function_signature (
+ id int,
+ a array<struct<i: int, s: varchar(16)>>
+ ) duplicate key(id)
+ distributed by hash(id) buckets 1
+ properties("replication_num" = "1")
+ """
+
+ sql "set enable_fast_analyze_into_values = true"
+ sql """
+ insert into array_struct_function_signature values (
+ 1,
+ array(
+ struct(100, repeat('x', 2)),
+ struct(200, repeat('y', 2))
+ )
+ )
+ """
+
+ sql "set enable_fast_analyze_into_values = false"
+ sql """
+ insert into array_struct_function_signature values (
+ 2,
+ array(
+ struct(100, repeat('x', 2)),
+ struct(200, repeat('y', 2))
+ )
+ )
+ """
+
+ order_qt_array_struct_function_signature "select * from
array_struct_function_signature"
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]