This is an automated email from the ASF dual-hosted git repository. dataroaring 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 380d5355a21 branch-3.0: [fix](sql cache) fix prepare statement with sql cache throw NullPointerException #48902 (#48976) 380d5355a21 is described below commit 380d5355a21e898a45939ca1740d546125070c97 Author: 924060929 <lanhuaj...@selectdb.com> AuthorDate: Sat Mar 15 10:12:05 2025 +0800 branch-3.0: [fix](sql cache) fix prepare statement with sql cache throw NullPointerException #48902 (#48976) cherry pick from #48902 --- .../doris/common/NereidsSqlCacheManager.java | 19 +++++++++++++ .../org/apache/doris/nereids/StatementContext.java | 2 +- .../trees/plans/commands/ExecuteCommand.java | 12 ++++++-- .../java/org/apache/doris/qe/ConnectProcessor.java | 11 ++++++++ .../doris/regression/action/TestAction.groovy | 2 +- .../cache/parse_sql_from_sql_cache.groovy | 7 +---- .../cache/prepare_stmt_with_sql_cache.groovy | 33 ++++++++++++++++------ 7 files changed, 66 insertions(+), 20 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/NereidsSqlCacheManager.java b/fe/fe-core/src/main/java/org/apache/doris/common/NereidsSqlCacheManager.java index 93e4aebb8f8..62d052f18b6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/common/NereidsSqlCacheManager.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/NereidsSqlCacheManager.java @@ -127,6 +127,13 @@ public class NereidsSqlCacheManager { * tryAddFeCache */ public void tryAddFeSqlCache(ConnectContext connectContext, String sql) { + switch (connectContext.getCommand()) { + case COM_STMT_EXECUTE: + case COM_STMT_PREPARE: + return; + default: { } + } + Optional<SqlCacheContext> sqlCacheContextOpt = connectContext.getStatementContext().getSqlCacheContext(); if (!sqlCacheContextOpt.isPresent()) { return; @@ -146,6 +153,12 @@ public class NereidsSqlCacheManager { * tryAddBeCache */ public void tryAddBeCache(ConnectContext connectContext, String sql, CacheAnalyzer analyzer) { + switch (connectContext.getCommand()) { + case COM_STMT_EXECUTE: + case COM_STMT_PREPARE: + return; + default: { } + } Optional<SqlCacheContext> sqlCacheContextOpt = connectContext.getStatementContext().getSqlCacheContext(); if (!sqlCacheContextOpt.isPresent()) { return; @@ -177,6 +190,12 @@ public class NereidsSqlCacheManager { * tryParseSql */ public Optional<LogicalSqlCache> tryParseSql(ConnectContext connectContext, String sql) { + switch (connectContext.getCommand()) { + case COM_STMT_EXECUTE: + case COM_STMT_PREPARE: + return Optional.empty(); + default: { } + } String key = generateCacheKey(connectContext, normalizeSql(sql.trim())); SqlCacheContext sqlCacheContext = sqlCaches.getIfPresent(key); if (sqlCacheContext == null) { 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 b82c48a7fda..ffe9f38a262 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 @@ -164,7 +164,7 @@ public class StatementContext implements Closeable { private final Stack<CloseableResource> plannerResources = new Stack<>(); // placeholder params for prepared statement - private List<Placeholder> placeholders; + private List<Placeholder> placeholders = new ArrayList<>(); // all tables in query private boolean needLockTables = true; 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 c4031c0f9e5..c67ab1597cc 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 @@ -25,6 +25,8 @@ import org.apache.doris.nereids.glue.LogicalPlanAdapter; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.plans.PlanType; import org.apache.doris.nereids.trees.plans.commands.insert.OlapGroupCommitInsertExecutor; +import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; +import org.apache.doris.nereids.trees.plans.logical.LogicalSqlCache; import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; import org.apache.doris.planner.GroupCommitPlanner; import org.apache.doris.qe.ConnectContext; @@ -68,9 +70,13 @@ public class ExecuteCommand extends Command { throw new AnalysisException( "prepare statement " + stmtName + " not found, maybe expired"); } - PrepareCommand prepareCommand = (PrepareCommand) preparedStmtCtx.command; - LogicalPlanAdapter planAdapter = new LogicalPlanAdapter(prepareCommand.getLogicalPlan(), executor.getContext() - .getStatementContext()); + PrepareCommand prepareCommand = preparedStmtCtx.command; + LogicalPlan logicalPlan = prepareCommand.getLogicalPlan(); + if (logicalPlan instanceof LogicalSqlCache) { + throw new AnalysisException("Unsupported sql cache for server prepared statement"); + } + LogicalPlanAdapter planAdapter = new LogicalPlanAdapter( + logicalPlan, executor.getContext().getStatementContext()); executor.setParsedStmt(planAdapter); // 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 diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java index 6da6a703e69..55c4488e7ac 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java @@ -400,6 +400,17 @@ public abstract class ConnectProcessor { private List<StatementBase> parseFromSqlCache(String originStmt) { StatementContext statementContext = new StatementContext(ctx, new OriginStatement(originStmt, 0)); ctx.setStatementContext(statementContext); + + // the mysql protocol has different between COM_QUERY and COM_STMT_EXECUTE, + // the sql cache use the result of COM_QUERY, so we can not provide the + // result of sql cache for COM_STMT_EXECUTE/COM_STMT_PREPARE + switch (ctx.getCommand()) { + case COM_STMT_EXECUTE: + case COM_STMT_PREPARE: + return null; + default: { } + } + try { Optional<Pair<ExplainOptions, String>> explainPlan = NereidsParser.tryParseExplainPlan(originStmt); String cacheSqlKey = originStmt; diff --git a/regression-test/framework/src/main/groovy/org/apache/doris/regression/action/TestAction.groovy b/regression-test/framework/src/main/groovy/org/apache/doris/regression/action/TestAction.groovy index 3febe6bbad5..ebee498da98 100644 --- a/regression-test/framework/src/main/groovy/org/apache/doris/regression/action/TestAction.groovy +++ b/regression-test/framework/src/main/groovy/org/apache/doris/regression/action/TestAction.groovy @@ -68,7 +68,7 @@ class TestAction implements SuiteAction { } else { if (exception != null || result.exception != null) { def msg = result.exception?.toString() - log.info("Exception: ${msg}") + log.error("Exception: ${msg}", exception != null ? exception : result.exception) Assert.assertTrue("Expect exception msg contains '${exception}', but meet '${msg}'", msg != null && exception != null && msg.contains(exception)) } diff --git a/regression-test/suites/nereids_p0/cache/parse_sql_from_sql_cache.groovy b/regression-test/suites/nereids_p0/cache/parse_sql_from_sql_cache.groovy index 765d1208426..1542efe984a 100644 --- a/regression-test/suites/nereids_p0/cache/parse_sql_from_sql_cache.groovy +++ b/regression-test/suites/nereids_p0/cache/parse_sql_from_sql_cache.groovy @@ -33,12 +33,7 @@ suite("parse_sql_from_sql_cache") { } def dbName = (sql "select database()")[0][0].toString() - foreachFrontends { fe -> - def url = "jdbc:mysql://${fe.Host}:${fe.QueryPort}/${dbName}" - connect(context.config.jdbcUser, context.config.jdbcPassword, url) { - sql "ADMIN SET FRONTEND CONFIG ('cache_last_version_interval_second' = '10')" - } - } + sql "ADMIN SET ALL FRONTENDS CONFIG ('cache_last_version_interval_second' = '10')" // make sure if the table has been dropped, the cache should invalidate, // so we should retry multiple times to check diff --git a/regression-test/suites/nereids_p0/cache/prepare_stmt_with_sql_cache.groovy b/regression-test/suites/nereids_p0/cache/prepare_stmt_with_sql_cache.groovy index 7819a6ca09d..1d358adfbff 100644 --- a/regression-test/suites/nereids_p0/cache/prepare_stmt_with_sql_cache.groovy +++ b/regression-test/suites/nereids_p0/cache/prepare_stmt_with_sql_cache.groovy @@ -15,17 +15,10 @@ // specific language governing permissions and limitations // under the License. -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("prepare_stmt_with_sql_cache") { @@ -38,11 +31,13 @@ suite("prepare_stmt_with_sql_cache") { insert into test_prepare_stmt_with_sql_cache select * from numbers('number'='100'); """ + sql "ADMIN SET ALL FRONTENDS CONFIG ('cache_last_version_interval_second' = '10')" + def db = (sql "select database()")[0][0].toString() - def url = getServerPrepareJdbcUrl(context.config.jdbcUrl, db) + def serverPrepareUrl = getServerPrepareJdbcUrl(context.config.jdbcUrl, db) - connect(context.config.jdbcUser, context.config.jdbcPassword, url) { + connect(context.config.jdbcUser, context.config.jdbcPassword, serverPrepareUrl) { sql "set enable_sql_cache=true" for (def i in 0..<10) { try (PreparedStatement pstmt = prepareStatement("select * from test_prepare_stmt_with_sql_cache where id=?")) { @@ -54,4 +49,24 @@ suite("prepare_stmt_with_sql_cache") { } } } + + sleep(10 * 1000) + + connect(context.config.jdbcUser, context.config.jdbcPassword, context.config.jdbcUrl) { + sql "use ${db}" + sql "set enable_sql_cache=true" + test { + sql "select * from test_prepare_stmt_with_sql_cache where id=10" + result([[10]]) + } + } + + connect(context.config.jdbcUser, context.config.jdbcPassword, serverPrepareUrl) { + sql "use ${db}" + sql "set enable_sql_cache=true" + test { + sql "select * from test_prepare_stmt_with_sql_cache where id=10" + result(([[10]])) + } + } } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org