From 41603655211d4ad2e39323e0deb27fc423b4c650 Mon Sep 17 00:00:00 2001 From: ericyuanhui <285521263@qq.com> Date: Mon, 31 Aug 2026 19:41:28 +0800 Subject: [PATCH] Disable cached physical-plan reuse for parameterized write statements Signed-off-by: ericyuanhui <285521263@qq.com> --- src/main/client_context.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/client_context.cpp b/src/main/client_context.cpp index 936cae2d4..543b6f942 100644 --- a/src/main/client_context.cpp +++ b/src/main/client_context.cpp @@ -376,7 +376,11 @@ std::unique_ptr ClientContext::executeWithParams(PreparedStatement* if (!preparedStatement->isSuccess()) { return QueryResult::getQueryResultWithError(preparedStatement->errMsg); } - const auto useCachedPlan = preparedStatement->canReuseCachedPlanWith(inputParams); + // Physical-plan reuse is currently safe only for read-only statements. Write operators can + // project transaction-local IDs (for example, MERGE relation IDs consumed by a following + // SET), which must not survive into the next auto-commit transaction. + const auto useCachedPlan = + preparedStatement->isReadOnly() && preparedStatement->canReuseCachedPlanWith(inputParams); try { bindParametersNoLock(*preparedStatement, inputParams); } catch (std::exception& e) { @@ -399,7 +403,8 @@ std::unique_ptr ClientContext::executeWithParams(PreparedStatement* prepareNoLock(cachedStatement->parsedStatement, false /*shouldCommitNewTransaction*/, preparedStatement->parameterMap); useInternalCatalogEntry_ = false; - return executeNoLock(newPreparedStatement.get(), newCachedStatement.get(), queryID, {}, true); + return executeNoLock(newPreparedStatement.get(), newCachedStatement.get(), queryID, {}, + preparedStatement->isReadOnly()); } std::unique_ptr ClientContext::query(std::string_view query,