From 9e716be8d777571d4b2566555a340f4765f27d54 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 22 Sep 2025 16:40:25 +0800 Subject: [PATCH] Add placeholder literal for prepare stage. --- .../rules/analysis/ExpressionAnalyzer.java | 3 +- .../functions/scalar/DateTrunc.java | 16 +++++---- .../literal/PlaceholderLiteral.java | 33 +++++++++++++++++++ .../data/prepared_stmt_p0/prepared_stmt.out | 6 ++++ .../prepared_stmt_p0/prepared_stmt.groovy | 20 +++++++++++ 5 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/PlaceholderLiteral.java 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 040224f7c54820..f8e1d2981e17ee 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 @@ -81,6 +81,7 @@ import org.apache.doris.nereids.trees.expressions.literal.IntegerLikeLiteral; import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; +import org.apache.doris.nereids.trees.expressions.literal.PlaceholderLiteral; import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes; import org.apache.doris.nereids.trees.plans.PlaceholderId; @@ -650,7 +651,7 @@ public Expression visitPlaceholder(Placeholder placeholder, ExpressionRewriteCon // In prepare stage, the realExpr has not been set, set it to StringLiteral 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 StringLiteral(String.valueOf(placeholder.getPlaceholderId().asInt())); + realExpr = new PlaceholderLiteral(String.valueOf(placeholder.getPlaceholderId().asInt())); } return visit(realExpr, context); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java index 6f46a34af8e124..998e71443ec3fd 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java @@ -24,6 +24,7 @@ import org.apache.doris.nereids.trees.expressions.functions.Monotonic; import org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral; import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable; +import org.apache.doris.nereids.trees.expressions.literal.PlaceholderLiteral; import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral; import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; @@ -65,16 +66,19 @@ public void checkLegalityBeforeTypeCoercion() { throw new AnalysisException("the time unit parameter of " + getName() + " function must be a string constant: " + toSql()); } else if (firstArgIsStringLiteral && secondArgIsStringLiteral) { - if (!LEGAL_TIME_UNIT.contains(((StringLikeLiteral) getArgument(0)).getStringValue().toLowerCase()) - && !LEGAL_TIME_UNIT.contains(((StringLikeLiteral) getArgument(1)) - .getStringValue().toLowerCase())) { + StringLikeLiteral argument0 = (StringLikeLiteral) getArgument(0); + StringLikeLiteral argument1 = (StringLikeLiteral) getArgument(1); + if (!LEGAL_TIME_UNIT.contains(argument0.getStringValue().toLowerCase()) + && !(argument0 instanceof PlaceholderLiteral) + && !LEGAL_TIME_UNIT.contains(argument1.getStringValue().toLowerCase()) + && !(argument1 instanceof PlaceholderLiteral)) { throw new AnalysisException("date_trunc function time unit param only support argument is " + String.join("|", LEGAL_TIME_UNIT)); } } else { - final String constParam = ((StringLikeLiteral) getArgument(firstArgIsStringLiteral ? 0 : 1)) - .getStringValue().toLowerCase(); - if (!LEGAL_TIME_UNIT.contains(constParam)) { + StringLikeLiteral argument = (StringLikeLiteral) getArgument(firstArgIsStringLiteral ? 0 : 1); + final String constParam = argument.getStringValue().toLowerCase(); + if (!LEGAL_TIME_UNIT.contains(constParam) && !(argument instanceof PlaceholderLiteral)) { throw new AnalysisException("date_trunc function time unit param only support argument is " + String.join("|", LEGAL_TIME_UNIT)); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/PlaceholderLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/PlaceholderLiteral.java new file mode 100644 index 00000000000000..17834102ff41b8 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/PlaceholderLiteral.java @@ -0,0 +1,33 @@ +// 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.expressions.literal; + +/** + * Placeholder literal only used for prepare stage. + */ +public class PlaceholderLiteral extends StringLiteral { + + /** + * Constructor for Literal. + * + * @param value real value stored in java object + */ + public PlaceholderLiteral(String value) { + super(value); + } +} diff --git a/regression-test/data/prepared_stmt_p0/prepared_stmt.out b/regression-test/data/prepared_stmt_p0/prepared_stmt.out index f5d8a45d6fc290..52c08da34e82af 100644 --- a/regression-test/data/prepared_stmt_p0/prepared_stmt.out +++ b/regression-test/data/prepared_stmt_p0/prepared_stmt.out @@ -142,6 +142,12 @@ a 0 1 1 2 +-- !select27 -- +2025-01-01T00:00 + +-- !select28 -- +2020-01-01T00:00 + -- !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 8c0cbd5cdff9e9..684a0162b6d501 100644 --- a/regression-test/suites/prepared_stmt_p0/prepared_stmt.groovy +++ b/regression-test/suites/prepared_stmt_p0/prepared_stmt.groovy @@ -356,6 +356,26 @@ suite("test_prepared_stmt", "nonConcurrent") { stmt_read.setString(2, "2") stmt_read.setString(3, "3") qe_select26 stmt_read + + sql """drop table if exists date_trunc_test""" + sql """ CREATE TABLE IF NOT EXISTS `date_trunc_test` ( + `pk` int NULL, + `value` datetime NOT NULL + ) ENGINE=OLAP + DUPLICATE KEY(`pk`) + DISTRIBUTED BY HASH(`pk`) BUCKETS 10 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + ); + """ + sql """insert into date_trunc_test values (1, "2025-09-21 15:43:25") """ + stmt_read = prepareStatement "select date_trunc(value, ?) from date_trunc_test" + stmt_read.setString(1, "year") + qe_select27 stmt_read + stmt_read = prepareStatement "select date_trunc(?, ?) from date_trunc_test" + stmt_read.setString(1, "year") + stmt_read.setString(2, "2020-01-01 11:22:33") + qe_select28 stmt_read } // test stmtId overflow