From b427a13e65b56213e7b91d114e996842c339c03d Mon Sep 17 00:00:00 2001 From: thijs-s Date: Mon, 13 Apr 2026 21:56:53 +0200 Subject: [PATCH] Fix optimizer crash in JDBC prepare by initializing active_query Initialize `active_query` in `ClientContext::PrepareInternal` to ensure the optimizer has a valid context during statement preparation. This fixes a null pointer dereference encountered by optimizer extensions when accessing `GetCurrentQuery()` via the JDBC driver. The implementation uses a new `ActiveQueryGuard` RAII helper to ensure strict exception safety and proper resource cleanup. --- src/main/client_context.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/client_context.cpp b/src/main/client_context.cpp index 073ef1bfe6c2..78921a2a7b95 100644 --- a/src/main/client_context.cpp +++ b/src/main/client_context.cpp @@ -99,6 +99,26 @@ struct ActiveQueryContext { BaseQueryResult *open_result = nullptr; }; +//! RAII wrapper that ensures the active query is reset if an exception occurs during preparation +struct ActiveQueryGuard { + unique_ptr &active_query; + bool set_active_query; + + ActiveQueryGuard(unique_ptr &active_query_p, const string &query) + : active_query(active_query_p), set_active_query(false) { + if (!active_query) { + active_query = make_uniq(); + set_active_query = true; + active_query->query = query; + } + } + ~ActiveQueryGuard() { + if (set_active_query) { + active_query.reset(); + } + } +}; + #ifdef DEBUG struct DebugClientContextState : public ClientContextState { ~DebugClientContextState() override { @@ -764,7 +784,10 @@ unique_ptr ClientContext::PrepareInternal(ClientContextLock & PendingQueryParameters parameters; RunFunctionInTransactionInternal( lock, - [&]() { prepared_data = CreatePreparedStatement(lock, statement_query, std::move(statement), parameters); }, + [&]() { + ActiveQueryGuard guard(active_query, statement_query); + prepared_data = CreatePreparedStatement(lock, statement_query, std::move(statement), parameters); + }, false); prepared_data->unbound_statement = std::move(unbound_statement); return make_uniq(shared_from_this(), std::move(prepared_data), std::move(statement_query),