diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java index c2410e51d8364e..0cd604cbd80d4c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java @@ -831,7 +831,7 @@ public static PhysicalProperties buildInitRequireProperties() { return PhysicalProperties.GATHER; } - private ExplainLevel getExplainLevel(ExplainOptions explainOptions) { + protected ExplainLevel getExplainLevel(ExplainOptions explainOptions) { if (explainOptions == null) { return ExplainLevel.NONE; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java index ea9638e9360074..51fee5c1bba404 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java @@ -682,7 +682,7 @@ public Expression visitPlaceholder(Placeholder placeholder, ExpressionRewriteCon // In prepare stage, the realExpr has not been set, set it to NullLiteral so that we can plan the statement // and get the output slots in prepare stage, which is required by Mysql api definition. if (realExpr == null && context.cascadesContext.getStatementContext().isPrepareStage()) { - realExpr = new NullLiteral(); + realExpr = new StringLiteral(String.valueOf(placeholder.getPlaceholderId().asInt())); } return visit(realExpr, context); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Placeholder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Placeholder.java index 27769750ad0499..caa3fd07e30322 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Placeholder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Placeholder.java @@ -24,7 +24,7 @@ import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.trees.plans.PlaceholderId; import org.apache.doris.nereids.types.DataType; -import org.apache.doris.nereids.types.NullType; +import org.apache.doris.nereids.types.StringType; import java.util.Optional; @@ -78,7 +78,7 @@ public int fastChildrenHashCode() { @Override public DataType getDataType() throws UnboundException { - return NullType.INSTANCE; + return StringType.INSTANCE; } public Placeholder withNewMysqlColType(int mysqlTypeCode) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PrepareCommandPlanner.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PrepareCommandPlanner.java new file mode 100644 index 00000000000000..65ad7cd1bb944a --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PrepareCommandPlanner.java @@ -0,0 +1,39 @@ +// 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. + +package org.apache.doris.nereids.trees.plans; + +import org.apache.doris.analysis.ExplainOptions; +import org.apache.doris.nereids.NereidsPlanner; +import org.apache.doris.nereids.StatementContext; +import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel; + +/** + * Planner for prepare command. Used for PrepareCommand to get output slots metadata. + * In prepare stage, we only need to do analyze stage of plan to get the output slots metadata. + * And we skip all the other stages to avoid errors. + */ +public class PrepareCommandPlanner extends NereidsPlanner { + public PrepareCommandPlanner(StatementContext statementContext) { + super(statementContext); + } + + protected ExplainLevel getExplainLevel(ExplainOptions explainOptions) { + return ExplainLevel.ANALYZED_PLAN; + } + +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java index 3b2aeb116ff6e5..d9ccfbe7650237 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java @@ -150,6 +150,7 @@ import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.expressions.SlotReference; import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal; +import org.apache.doris.nereids.trees.plans.PrepareCommandPlanner; import org.apache.doris.nereids.trees.plans.commands.Command; import org.apache.doris.nereids.trees.plans.commands.CreatePolicyCommand; import org.apache.doris.nereids.trees.plans.commands.CreateTableCommand; @@ -3491,9 +3492,9 @@ public List planPrepareStatementSlots() throws Exception { Preconditions.checkState(parsedStmt instanceof LogicalPlanAdapter, "Nereids only process LogicalPlanAdapter," + " but parsedStmt is " + parsedStmt.getClass().getName()); - NereidsPlanner nereidsPlanner = new NereidsPlanner(statementContext); + NereidsPlanner nereidsPlanner = new PrepareCommandPlanner(statementContext); nereidsPlanner.plan(parsedStmt, context.getSessionVariable().toThrift()); - return nereidsPlanner.getPhysicalPlan().getOutput(); + return nereidsPlanner.getCascadesContext().getRewritePlan().getOutput(); } public List executeInternalQuery() { diff --git a/regression-test/data/prepared_stmt_p0/prepared_stmt.out b/regression-test/data/prepared_stmt_p0/prepared_stmt.out index a940c924e64b2a..f5d8a45d6fc290 100644 --- a/regression-test/data/prepared_stmt_p0/prepared_stmt.out +++ b/regression-test/data/prepared_stmt_p0/prepared_stmt.out @@ -138,6 +138,10 @@ a -- !select25 -- 2025-08-15T00:00 +-- !select26 -- +0 1 +1 2 + -- !overflow_2 -- 2 diff --git a/regression-test/suites/prepared_stmt_p0/prepared_stmt.groovy b/regression-test/suites/prepared_stmt_p0/prepared_stmt.groovy index 9a350ce6dc6434..615098d30c2244 100644 --- a/regression-test/suites/prepared_stmt_p0/prepared_stmt.groovy +++ b/regression-test/suites/prepared_stmt_p0/prepared_stmt.groovy @@ -319,6 +319,35 @@ suite("test_prepared_stmt", "nonConcurrent") { stmt_read.setString(1, "2025-08-15 11:22:33") stmt_read.setString(2, "DAY") qe_select25 stmt_read + + sql """drop table if exists table_20_undef_partitions2_keys3_properties4_distributed_by54""" + sql """ CREATE TABLE IF NOT EXISTS `table_20_undef_partitions2_keys3_properties4_distributed_by54` ( + `col_int_undef_signed` int NULL, + `col_int_undef_signed2` int NULL, + `pk` int NULL + ) ENGINE=OLAP + DUPLICATE KEY(`col_int_undef_signed`, `col_int_undef_signed2`, `pk`) + DISTRIBUTED BY HASH(`pk`) BUCKETS 10 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1", + "min_load_replica_num" = "-1", + "is_being_synced" = "false", + "storage_medium" = "hdd", + "storage_format" = "V2", + "inverted_index_storage_format" = "V2", + "light_schema_change" = "true", + "disable_auto_compaction" = "false", + "enable_single_replica_compaction" = "false", + "group_commit_interval_ms" = "10000", + "group_commit_data_bytes" = "134217728" + ); + """ + sql """insert into table_20_undef_partitions2_keys3_properties4_distributed_by54 values (1, 1, 1), (2, 2, 2)""" + stmt_read = prepareStatement "select min ( pk - ? ) pk , pk as pk from table_20_undef_partitions2_keys3_properties4_distributed_by54 tbl_alias1 group by pk having ( pk >= pk ) or ( round ( sign ( sign ( pk ) ) ) - ? < ? ) order by pk " + stmt_read.setString(1, "1") + stmt_read.setString(2, "2") + stmt_read.setString(3, "3") + qe_select26 stmt_read } // test stmtId overflow