diff --git a/include/PgSQL_ExplicitTxnStateMgr.h b/include/PgSQL_ExplicitTxnStateMgr.h index 5594e24f3..ea50cb28c 100644 --- a/include/PgSQL_ExplicitTxnStateMgr.h +++ b/include/PgSQL_ExplicitTxnStateMgr.h @@ -1,112 +1,112 @@ -#ifndef PGSQL_EXPLICIT_TRANSACTION_STATE_MANAGER_H -#define PGSQL_EXPLICIT_TRANSACTION_STATE_MANAGER_H -#include -#include -#include -#include "proxysql.h" -#include "cpp.h" -#include "PgSQL_Connection.h" - -#ifndef PROXYJSON -#define PROXYJSON -#include "../deps/json/json_fwd.hpp" -#endif // PROXYJSON - -/** - * @struct PgSQL_Variable_Snapshot - * @brief Represents a snapshot of PostgreSQL variables during a transaction. - * - * This structure is used to store the state of PostgreSQL variables, including - * their values and hash representations, at a specific point in time during a transaction. - */ -typedef struct PgSQL_Variable_Snapshot { - char* var_value[PGSQL_NAME_LAST_HIGH_WM] = {}; - uint32_t var_hash[PGSQL_NAME_LAST_HIGH_WM] = {}; -} PgSQL_Variable_Snapshot_t; - -/** - * @struct TxnCmd - * @brief Represents a transaction command type begin executed and its associated metadata. - * - */ -struct TxnCmd { - /** - * @enum Type - * @brief Enumerates the types of transaction commands. - */ - enum Type { - UNKNOWN = -1, - BEGIN, - COMMIT, - ROLLBACK, - SAVEPOINT, - RELEASE, - ROLLBACK_TO - } type = Type::UNKNOWN; - std::string savepoint; //< The name of the savepoint, if applicable. -}; - -/** - * @class PgSQL_TxnCmdParser - * @brief Parses transaction-related commands for PostgreSQL. - * - * This class is responsible for tokenizing and interpreting transaction-related - * commands such as BEGIN, COMMIT, ROLLBACK, SAVEPOINT, etc. - */ -class PgSQL_TxnCmdParser { -public: - TxnCmd parse(std::string_view input, bool in_transaction_mode) noexcept; - -private: - std::vector tokens; - - TxnCmd parse_rollback(size_t& pos) noexcept; - TxnCmd parse_savepoint(size_t& pos) noexcept; - TxnCmd parse_release(size_t& pos) noexcept; - - // Helpers - static std::string to_lower(std::string_view s) noexcept { - std::string s_copy(s); - std::transform(s_copy.begin(), s_copy.end(), s_copy.begin(), ::tolower); - return s_copy; - } - - inline static bool contains(std::vector&& list, std::string_view value) noexcept { - for (const auto& item : list) if (item == value) return true; - return false; - } -}; - -/** - * @class PgSQL_ExplicitTxnStateMgr - * @brief Manages the state of explicit transactions in PostgreSQL. - * - * This class is responsible for handling explicit transaction commands such as - * BEGIN, COMMIT, ROLLBACK, SAVEPOINT, and managing the associated state. - */ -class PgSQL_ExplicitTxnStateMgr { -public: - PgSQL_ExplicitTxnStateMgr(PgSQL_Session* sess); - ~PgSQL_ExplicitTxnStateMgr(); - - bool handle_transaction(std::string_view input); - int get_savepoint_count() const { return savepoint.size(); } - void fill_internal_session(nlohmann::json& j); - -private: - PgSQL_Session* session; - std::vector transaction_state; - std::vector savepoint; - PgSQL_TxnCmdParser tx_parser; - - void start_transaction(); - void commit(); - void rollback(); - bool add_savepoint(std::string_view name); - bool rollback_to_savepoint(std::string_view name); - bool release_savepoint(std::string_view name); - - static void reset_variable_snapshot(PgSQL_Variable_Snapshot_t& var_snapshot) noexcept; -}; - -#endif // PGSQL_EXPLICIT_TRANSACTION_STATE_MANAGER_H +#ifndef PGSQL_EXPLICIT_TRANSACTION_STATE_MANAGER_H +#define PGSQL_EXPLICIT_TRANSACTION_STATE_MANAGER_H +#include +#include +#include +#include "proxysql.h" +#include "cpp.h" +#include "PgSQL_Connection.h" + +#ifndef PROXYJSON +#define PROXYJSON +#include "../deps/json/json_fwd.hpp" +#endif // PROXYJSON + +/** + * @struct PgSQL_Variable_Snapshot + * @brief Represents a snapshot of PostgreSQL variables during a transaction. + * + * This structure is used to store the state of PostgreSQL variables, including + * their values and hash representations, at a specific point in time during a transaction. + */ +struct PgSQL_Variable_Snapshot { + char* var_value[PGSQL_NAME_LAST_HIGH_WM] = {}; // Not using smart pointers because we need fine-grained control over hashing when values change + uint32_t var_hash[PGSQL_NAME_LAST_HIGH_WM] = {}; +}; + +/** + * @struct TxnCmd + * @brief Represents a transaction command type begin executed and its associated metadata. + * + */ +struct TxnCmd { + /** + * @enum Type + * @brief Enumerates the types of transaction commands. + */ + enum Type { + UNKNOWN = -1, + BEGIN, + COMMIT, + ROLLBACK, + SAVEPOINT, + RELEASE, + ROLLBACK_TO + } type = Type::UNKNOWN; + std::string savepoint; //< The name of the savepoint, if applicable. +}; + +/** + * @class PgSQL_TxnCmdParser + * @brief Parses transaction-related commands for PostgreSQL. + * + * This class is responsible for tokenizing and interpreting transaction-related + * commands such as BEGIN, COMMIT, ROLLBACK, SAVEPOINT, etc. + */ +class PgSQL_TxnCmdParser { +public: + TxnCmd parse(std::string_view input, bool in_transaction_mode) noexcept; + +private: + std::vector tokens; + + TxnCmd parse_rollback(size_t& pos) noexcept; + TxnCmd parse_savepoint(size_t& pos) noexcept; + TxnCmd parse_release(size_t& pos) noexcept; + + // Helpers + static std::string to_lower(std::string_view s) noexcept { + std::string s_copy(s); + std::transform(s_copy.begin(), s_copy.end(), s_copy.begin(), ::tolower); + return s_copy; + } + + inline static bool contains(std::vector&& list, std::string_view value) noexcept { + for (const auto& item : list) if (item == value) return true; + return false; + } +}; + +/** + * @class PgSQL_ExplicitTxnStateMgr + * @brief Manages the state of explicit transactions in PostgreSQL. + * + * This class is responsible for handling explicit transaction commands such as + * BEGIN, COMMIT, ROLLBACK, SAVEPOINT, and managing the associated state. + */ +class PgSQL_ExplicitTxnStateMgr { +public: + PgSQL_ExplicitTxnStateMgr(PgSQL_Session* sess); + ~PgSQL_ExplicitTxnStateMgr(); + + bool handle_transaction(std::string_view input); + int get_savepoint_count() const { return savepoint.size(); } + void fill_internal_session(nlohmann::json& j); + +private: + PgSQL_Session* session; + std::vector transaction_state; + std::vector savepoint; + PgSQL_TxnCmdParser tx_parser; + + void start_transaction(); + void commit(); + void rollback(); + bool add_savepoint(std::string_view name); + bool rollback_to_savepoint(std::string_view name); + bool release_savepoint(std::string_view name); + + static void reset_variable_snapshot(PgSQL_Variable_Snapshot& var_snapshot) noexcept; +}; + +#endif // PGSQL_EXPLICIT_TRANSACTION_STATE_MANAGER_H diff --git a/lib/PgSQL_ExplicitTxnStateMgr.cpp b/lib/PgSQL_ExplicitTxnStateMgr.cpp index f29cb1cc9..268a11101 100644 --- a/lib/PgSQL_ExplicitTxnStateMgr.cpp +++ b/lib/PgSQL_ExplicitTxnStateMgr.cpp @@ -1,4 +1,4 @@ -#include "PgSQL_ExplicitTxnStateMgr.h" +#include "PgSQL_ExplicitTxnStateMgr.h" #include "proxysql.h" #include "PgSQL_Session.h" #include "PgSQL_Data_Stream.h" @@ -18,7 +18,7 @@ PgSQL_ExplicitTxnStateMgr::~PgSQL_ExplicitTxnStateMgr() { savepoint.clear(); } -void PgSQL_ExplicitTxnStateMgr::reset_variable_snapshot(PgSQL_Variable_Snapshot_t& var_snapshot) noexcept { +void PgSQL_ExplicitTxnStateMgr::reset_variable_snapshot(PgSQL_Variable_Snapshot& var_snapshot) noexcept { for (int idx = 0; idx < PGSQL_NAME_LAST_HIGH_WM; idx++) { if (var_snapshot.var_value[idx]) { free(var_snapshot.var_value[idx]); @@ -50,7 +50,7 @@ void PgSQL_ExplicitTxnStateMgr::start_transaction() { assert(session->client_myds && session->client_myds->myconn); - PgSQL_Variable_Snapshot_t var_snapshot{}; + PgSQL_Variable_Snapshot var_snapshot{}; // check if already in transaction, if yes then do nothing for (int idx = 0; idx < PGSQL_NAME_LAST_HIGH_WM; idx++) { @@ -96,7 +96,7 @@ void PgSQL_ExplicitTxnStateMgr::rollback() { assert(session->client_myds && session->client_myds->myconn); - const PgSQL_Variable_Snapshot_t& var_snapshot = transaction_state.front(); + const PgSQL_Variable_Snapshot& var_snapshot = transaction_state.front(); for (int idx = 0; idx < PGSQL_NAME_LAST_HIGH_WM; idx++) { uint32_t hash = var_snapshot.var_hash[idx]; @@ -159,7 +159,7 @@ bool PgSQL_ExplicitTxnStateMgr::rollback_to_savepoint(std::string_view name) { assert(tran_state_idx + 1 < (int)transaction_state.size()); - PgSQL_Variable_Snapshot_t& var_snapshot = transaction_state[tran_state_idx+1]; + PgSQL_Variable_Snapshot& var_snapshot = transaction_state[tran_state_idx+1]; for (int idx = 0; idx < PGSQL_NAME_LAST_HIGH_WM; idx++) { uint32_t hash = var_snapshot.var_hash[idx]; if (hash != 0) { @@ -177,6 +177,14 @@ bool PgSQL_ExplicitTxnStateMgr::rollback_to_savepoint(std::string_view name) { pgsql_variables.server_reset_value(session, idx, false); } } + + session->client_myds->myconn->reorder_dynamic_variables_idx(); + if (session->mybe) { + session->mybe->server_myds->myconn->reorder_dynamic_variables_idx(); + + verify_server_variables(session); + } + for (size_t idx = tran_state_idx + 1; idx < transaction_state.size(); idx++) { reset_variable_snapshot(transaction_state[idx]); } @@ -236,7 +244,7 @@ bool PgSQL_ExplicitTxnStateMgr::add_savepoint(std::string_view name) { }); if (it != savepoint.end()) return false; - PgSQL_Variable_Snapshot_t var_snapshot{}; + PgSQL_Variable_Snapshot var_snapshot{}; for (int idx = 0; idx < PGSQL_NAME_LAST_HIGH_WM; idx++) { uint32_t hash = pgsql_variables.client_get_hash(session, idx); @@ -344,6 +352,7 @@ TxnCmd PgSQL_TxnCmdParser::parse(std::string_view input, bool in_transaction_mod if (first == "begin") cmd.type = TxnCmd::BEGIN; else if (first == "savepoint") cmd = parse_savepoint(pos); else if (first == "release") cmd = parse_release(pos); + else if (first == "rollback") cmd = parse_rollback(pos); } else { if (first == "commit") cmd.type = TxnCmd::COMMIT; else if (first == "rollback" || (first == "abort")) cmd = parse_rollback(pos);