This is an automated email from the ASF dual-hosted git repository. eldenmoon pushed a commit to branch branch-3.0 in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-3.0 by this push: new fe564902378 branch-3.0: [Fix](PreparedStatement) nondeterministic functions in prepared statement should not be short circuited #46003 (#46108) fe564902378 is described below commit fe5649023785d02cbee5b90abe7921fec87925c8 Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> AuthorDate: Fri Dec 27 21:02:02 2024 +0800 branch-3.0: [Fix](PreparedStatement) nondeterministic functions in prepared statement should not be short circuited #46003 (#46108) Cherry-picked from #46003 Co-authored-by: lihangyu <lihan...@selectdb.com> --- .../org/apache/doris/nereids/StatementContext.java | 10 ++++++++ .../nereids/rules/analysis/ExpressionAnalyzer.java | 5 ++++ .../trees/plans/commands/ExecuteCommand.java | 7 +++--- .../suites/point_query_p0/test_point_query.groovy | 27 ++++++++++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java index 4cc4a6c8600..0add923eec5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java @@ -118,6 +118,8 @@ public class StatementContext implements Closeable { private boolean isDpHyp = false; + private boolean hasNondeterministic = false; + // hasUnknownColStats true if any column stats in the tables used by this sql is unknown // the algorithm to derive plan when column stats are unknown is implemented in cascading framework, not in dphyper. // And hence, when column stats are unknown, even if the tables used by a sql is more than @@ -303,6 +305,14 @@ public class StatementContext implements Closeable { this.connectContext = connectContext; } + public void setHasNondeterministic(boolean hasNondeterministic) { + this.hasNondeterministic = hasNondeterministic; + } + + public boolean hasNondeterministic() { + return hasNondeterministic; + } + public ConnectContext getConnectContext() { return connectContext; } 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 5ef3d0fbff3..26c25a4e012 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 @@ -403,6 +403,11 @@ public class ExpressionAnalyzer extends SubExprAnalyzer<ExpressionRewriteContext Pair<? extends Expression, ? extends BoundFunction> buildResult = builder.build(functionName, arguments); buildResult.second.checkOrderExprIsValid(); Optional<SqlCacheContext> sqlCacheContext = Optional.empty(); + + if (!buildResult.second.isDeterministic() && context != null) { + StatementContext statementContext = context.cascadesContext.getStatementContext(); + statementContext.setHasNondeterministic(true); + } if (wantToParseSqlFromSqlCache) { StatementContext statementContext = context.cascadesContext.getStatementContext(); if (!buildResult.second.isDeterministic()) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExecuteCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExecuteCommand.java index 08e351041a2..47ba6ed3f4c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExecuteCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExecuteCommand.java @@ -70,14 +70,15 @@ public class ExecuteCommand extends Command { LogicalPlanAdapter planAdapter = new LogicalPlanAdapter(prepareCommand.getLogicalPlan(), executor.getContext() .getStatementContext()); executor.setParsedStmt(planAdapter); - // If it's not a short circuit query or schema version is different(indicates schema changed), - // need to do reanalyze and plan + // If it's not a short circuit query or schema version is different(indicates schema changed) or + // has nondeterministic functions in statement, then need to do reanalyze and plan boolean isShortCircuit = executor.getContext().getStatementContext().isShortCircuitQuery(); boolean hasShortCircuitContext = preparedStmtCtx.shortCircuitQueryContext.isPresent(); boolean schemaVersionMismatch = hasShortCircuitContext && preparedStmtCtx.shortCircuitQueryContext.get().tbl.getBaseSchemaVersion() != preparedStmtCtx.shortCircuitQueryContext.get().schemaVersion; - boolean needAnalyze = !isShortCircuit || schemaVersionMismatch || !hasShortCircuitContext; + boolean needAnalyze = !isShortCircuit || schemaVersionMismatch || !hasShortCircuitContext + || executor.getContext().getStatementContext().hasNondeterministic(); if (needAnalyze) { // execute real statement preparedStmtCtx.shortCircuitQueryContext = Optional.empty(); diff --git a/regression-test/suites/point_query_p0/test_point_query.groovy b/regression-test/suites/point_query_p0/test_point_query.groovy index a15b48084ca..237103435a9 100644 --- a/regression-test/suites/point_query_p0/test_point_query.groovy +++ b/regression-test/suites/point_query_p0/test_point_query.groovy @@ -16,6 +16,17 @@ // under the License. import java.math.BigDecimal; +import com.mysql.cj.ServerPreparedQuery +import com.mysql.cj.jdbc.ConnectionImpl +import com.mysql.cj.jdbc.JdbcStatement +import com.mysql.cj.jdbc.ServerPreparedStatement +import com.mysql.cj.jdbc.StatementImpl +import org.apache.doris.regression.util.JdbcUtils + +import java.lang.reflect.Field +import java.sql.PreparedStatement +import java.sql.ResultSet +import java.util.concurrent.CopyOnWriteArrayList suite("test_point_query", "nonConcurrent") { def backendId_to_backendIP = [:] @@ -383,5 +394,21 @@ suite("test_point_query", "nonConcurrent") { partial_prepared_stmt.setString(2, "feature") qe_point_select partial_prepared_stmt qe_point_select partial_prepared_stmt + + // test prepared statement should not be short-circuited plan which use nondeterministic function + try (PreparedStatement pstmt = prepareStatement("select now(3) data_time from regression_test_point_query_p0.test_partial_prepared_statement where sk = 'sk' and user_guid = 'user_guid' and feature = 'feature'")) { + def result1 = "" + def result2 = "" + try (ResultSet rs = pstmt.executeQuery()) { + result1 = JdbcUtils.toList(rs).v1 + logger.info("result: {}", result1) + } + sleep(100) + try (ResultSet rs = pstmt.executeQuery()) { + result2 = JdbcUtils.toList(rs).v1 + logger.info("result: {}", result2) + } + assertNotEquals(result1, result2) + } } } \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org