diff --git a/include/PgSQL_Thread.h b/include/PgSQL_Thread.h index dc3f951e4..2e8f0a8cd 100644 --- a/include/PgSQL_Thread.h +++ b/include/PgSQL_Thread.h @@ -957,7 +957,7 @@ public: char* ldap_user_variable; char* add_ldap_user_comment; char* default_session_track_gtids; - char* default_variables[PGSQL_NAME_LAST_LOW_WM]; + char* default_variables[PGSQL_NAME_LAST_HIGH_WM]; char* firewall_whitelist_errormsg; #ifdef DEBUG bool session_debug; diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 21ea86b29..45a8fa123 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -1182,7 +1182,7 @@ __thread int pgsql_thread___query_cache_soft_ttl_pct; __thread int pgsql_thread___query_cache_handle_warnings; __thread bool pgsql_thread___session_idle_show_processlist; -__thread char* pgsql_thread___default_variables[PGSQL_NAME_LAST_LOW_WM]; +__thread char* pgsql_thread___default_variables[PGSQL_NAME_LAST_HIGH_WM]; __thread int pgsql_thread___handle_unknown_charset; //--------------------------- @@ -1482,7 +1482,7 @@ extern __thread int pgsql_thread___query_cache_soft_ttl_pct; extern __thread int pgsql_thread___query_cache_handle_warnings; extern __thread bool pgsql_thread___session_idle_show_processlist; -extern __thread char* pgsql_thread___default_variables[PGSQL_NAME_LAST_LOW_WM]; +extern __thread char* pgsql_thread___default_variables[PGSQL_NAME_LAST_HIGH_WM]; extern __thread int pgsql_thread___handle_unknown_charset; //--------------------------- @@ -1769,13 +1769,13 @@ pgsql_variable_st pgsql_tracked_variables[] { { PGSQL_STANDARD_CONFORMING_STRINGS, SETTING_VARIABLE, "standard_conforming_strings", "standard_conforming_strings", "on", (PGTRACKED_VAR_OPT_BOOL | PGTRACKED_VAR_OPT_GLOBAL_VARIABLE | PGTRACKED_VAR_OPT_PARAM_STATUS), nullptr }, { PGSQL_TIMEZONE, SETTING_VARIABLE, "timezone", "timezone", "GMT" , (PGTRACKED_VAR_OPT_QUOTE | PGTRACKED_VAR_OPT_GLOBAL_VARIABLE | PGTRACKED_VAR_OPT_PARAM_STATUS), { "TIME ZONE", nullptr } }, { PGSQL_NAME_LAST_LOW_WM, session_status___NONE, "placeholder", "placeholder", "0" , 0, nullptr }, // this is just a placeholder to separate the previous index from the next block - { PGSQL_BYTEA_OUTPUT, SETTING_VARIABLE, "bytea_output", "bytea_output", "hex", (PGTRACKED_VAR_OPT_QUOTE), nullptr }, - { PGSQL_CLIENT_MIN_MESSAGES, SETTING_VARIABLE, "client_min_messages", "client_min_messages", "NOTICE", (PGTRACKED_VAR_OPT_QUOTE), nullptr }, - { PGSQL_ENABLE_BITMAPSCAN, SETTING_VARIABLE, "enable_bitmapscan", "enable_bitmapscan", "on", (PGTRACKED_VAR_OPT_BOOL), nullptr }, - { PGSQL_ENABLE_INDEXSCAN, SETTING_VARIABLE, "enable_indexscan", "enable_indexscan", "on", (PGTRACKED_VAR_OPT_BOOL), nullptr }, - { PGSQL_ENABLE_SEQSCAN, SETTING_VARIABLE, "enable_seqscan", "enable_seqscan", "on", (PGTRACKED_VAR_OPT_BOOL), nullptr }, - { PGSQL_ESCAPE_STRING_WARNING, SETTING_VARIABLE, "escape_string_warning", "escape_string_warning", "on", (PGTRACKED_VAR_OPT_BOOL), nullptr }, - { PGSQL_MAINTENANCE_WORK_MEM, SETTING_VARIABLE, "maintenance_work_mem", "maintenance_work_mem", "64MB", (PGTRACKED_VAR_OPT_QUOTE), nullptr }, + { PGSQL_BYTEA_OUTPUT, SETTING_VARIABLE, "bytea_output", "bytea_output", "hex", (PGTRACKED_VAR_OPT_QUOTE | PGTRACKED_VAR_OPT_GLOBAL_VARIABLE), nullptr }, + { PGSQL_CLIENT_MIN_MESSAGES, SETTING_VARIABLE, "client_min_messages", "client_min_messages", "NOTICE", (PGTRACKED_VAR_OPT_QUOTE | PGTRACKED_VAR_OPT_GLOBAL_VARIABLE), nullptr }, + { PGSQL_ENABLE_BITMAPSCAN, SETTING_VARIABLE, "enable_bitmapscan", "enable_bitmapscan", "on", (PGTRACKED_VAR_OPT_BOOL | PGTRACKED_VAR_OPT_GLOBAL_VARIABLE), nullptr }, + { PGSQL_ENABLE_INDEXSCAN, SETTING_VARIABLE, "enable_indexscan", "enable_indexscan", "on", (PGTRACKED_VAR_OPT_BOOL | PGTRACKED_VAR_OPT_GLOBAL_VARIABLE), nullptr }, + { PGSQL_ENABLE_SEQSCAN, SETTING_VARIABLE, "enable_seqscan", "enable_seqscan", "on", (PGTRACKED_VAR_OPT_BOOL | PGTRACKED_VAR_OPT_GLOBAL_VARIABLE), nullptr }, + { PGSQL_ESCAPE_STRING_WARNING, SETTING_VARIABLE, "escape_string_warning", "escape_string_warning", "on", (PGTRACKED_VAR_OPT_BOOL | PGTRACKED_VAR_OPT_GLOBAL_VARIABLE), nullptr }, + { PGSQL_MAINTENANCE_WORK_MEM, SETTING_VARIABLE, "maintenance_work_mem", "maintenance_work_mem", "64MB", (PGTRACKED_VAR_OPT_QUOTE | PGTRACKED_VAR_OPT_GLOBAL_VARIABLE), nullptr }, }; #else diff --git a/lib/PgSQL_Protocol.cpp b/lib/PgSQL_Protocol.cpp index 1d4589169..8b933f750 100644 --- a/lib/PgSQL_Protocol.cpp +++ b/lib/PgSQL_Protocol.cpp @@ -1123,7 +1123,7 @@ EXECUTION_STATE PgSQL_Protocol::process_handshake_response_packet(unsigned char* for (auto& option : options_list) { int idx = PGSQL_NAME_LAST_HIGH_WM; for (int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { - if (i == PGSQL_NAME_LAST_LOW_WM) + if (i == PGSQL_NAME_LAST_LOW_WM) continue; if (variable_name_exists(pgsql_tracked_variables[i], option.first.c_str()) == true) { idx = i; diff --git a/lib/PgSQL_Session.cpp b/lib/PgSQL_Session.cpp index a4dd7b534..d9d75280a 100644 --- a/lib/PgSQL_Session.cpp +++ b/lib/PgSQL_Session.cpp @@ -4896,20 +4896,46 @@ bool PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C if (idx != PGSQL_NAME_LAST_HIGH_WM) { if ((value1.size() == sizeof("DEFAULT") - 1) && strncasecmp(value1.c_str(), (char*)"DEFAULT",sizeof("DEFAULT")-1) == 0) { - const char* value_tmp = get_default_session_variable((enum pgsql_variable_name)idx); - value1 = value_tmp ? value_tmp : ((idx < PGSQL_NAME_LAST_LOW_WM) ? pgsql_thread___default_variables[idx] : pgsql_tracked_variables[idx].default_value); + value1 = get_default_session_variable((enum pgsql_variable_name)idx); } if (idx == PGSQL_DATESTYLE) { assert(current_datestyle.format != DATESTYLE_FORMAT_NONE); assert(current_datestyle.order != DATESTYLE_ORDER_NONE); + + // PostgreSQL strangely accepts an empty value for datestyle, but it does not alter previously set vaclue. + if (value1.empty()) { + client_myds->DSS = STATE_QUERY_SENT_NET; + unsigned int nTrx = NumActiveTransactions(); + const char trx_state = (nTrx ? 'T' : 'I'); + client_myds->myprot.generate_ok_packet(true, true, NULL, 0, dig, trx_state, NULL, param_status); + RequestEnd(NULL); + l_free(pkt->size, pkt->ptr); + return true; + } + // Convert DateStyle to a string. Any missing parts will be filled using the current DateStyle value. // For example: // If current DateStyle is 'ISO, MDY' and the user sets 'DMY' the resulting DateStyle will be 'ISO, DMY' - value1 = PgSQL_DateStyle_Util::datestyle_to_string(value1, current_datestyle); - + const std::string& value_tmp = PgSQL_DateStyle_Util::datestyle_to_string(value1, current_datestyle); // if something goes wrong, the value will be empty - if (value1.empty()) return false; + if (value_tmp.empty()) { + char* m = NULL; + char* errmsg = NULL; + proxy_error("invalid value for parameter \"DateStyle\": \"%s\"\n", value1.c_str()); + m = (char*)"invalid value for parameter \"DateStyle\": \"%s\""; + errmsg = (char*)malloc(value1.length() + strlen(m)); + sprintf(errmsg, m, value1.c_str()); + + client_myds->DSS = STATE_QUERY_SENT_NET; + client_myds->myprot.generate_error_packet(true, true, errmsg, + PGSQL_ERROR_CODES::ERRCODE_INVALID_PARAMETER_VALUE, false, true); + client_myds->DSS = STATE_SLEEP; + status = WAITING_CLIENT_DATA; + free(errmsg); + return true; + } + value1 = value_tmp; } proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection %s to %s\n", var.c_str(), value1.c_str()); @@ -5233,8 +5259,8 @@ bool PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C if (!charset.empty()) { if ((charset.size() == sizeof("DEFAULT") - 1) && strncasecmp(charset.c_str(), (char*)"DEFAULT", sizeof("DEFAULT") - 1) == 0) { - const char* charset_tmp = get_default_session_variable(PGSQL_CLIENT_ENCODING); - charset = charset_tmp ? charset_tmp : pgsql_thread___default_variables[PGSQL_CLIENT_ENCODING]; + charset = get_default_session_variable(PGSQL_CLIENT_ENCODING); + assert(charset.empty() == false); } proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET CLIENT_ENCODING %s\n", charset.c_str()); @@ -5246,14 +5272,14 @@ bool PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C if (charset_encoding == -1) { char* m = NULL; char* errmsg = NULL; - proxy_error("Cannot find charset [%s]\n", charset.c_str()); - m = (char*)"Unknown character set: '%s'"; + proxy_error("invalid value for parameter \"Client_Encoding\": \"%s\"\n", charset.c_str()); + m = (char*)"invalid value for parameter \"Client_Encoding\": \"%s\""; errmsg = (char*)malloc(charset.length() + strlen(m)); sprintf(errmsg, m, charset.c_str()); client_myds->DSS = STATE_QUERY_SENT_NET; client_myds->myprot.generate_error_packet(true, true, errmsg, - PGSQL_ERROR_CODES::ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION, false, true); + PGSQL_ERROR_CODES::ERRCODE_INVALID_PARAMETER_VALUE, false, true); client_myds->DSS = STATE_SLEEP; status = WAITING_CLIENT_DATA; free(errmsg); @@ -5318,7 +5344,8 @@ bool PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C for (int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { - if (i == PGSQL_NAME_LAST_LOW_WM) continue; + if (i == PGSQL_NAME_LAST_LOW_WM) + continue; const char* name = pgsql_tracked_variables[i].set_variable_name; const char* value = get_default_session_variable((enum pgsql_variable_name)i); @@ -5349,7 +5376,8 @@ bool PgSQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C int idx = PGSQL_NAME_LAST_HIGH_WM; for (int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { - if (i == PGSQL_NAME_LAST_LOW_WM) continue; + if (i == PGSQL_NAME_LAST_LOW_WM) + continue; if (variable_name_exists(pgsql_tracked_variables[i], nq.c_str()) == true) { idx = i; @@ -6754,6 +6782,10 @@ void PgSQL_Session::reset_default_session_variable(enum pgsql_variable_name idx) // - "ISO" (a single token; the second string will be empty) // Leading and trailing whitespace is removed from each token. std::vector PgSQL_DateStyle_Util::split_datestyle(std::string_view input) { + + if (input.empty()) + return {}; + std::string token1, token2; // Reserve capacity in case the input is large (typically not needed for DateStyle) token1.reserve(input.size()); @@ -6897,7 +6929,6 @@ PgSQL_DateStyle_t PgSQL_DateStyle_Util::parse_datestyle(std::string_view input) std::string PgSQL_DateStyle_Util::datestyle_to_string(PgSQL_DateStyle_t datestyle, const PgSQL_DateStyle_t& default_datestyle) { if (datestyle.format == DATESTYLE_FORMAT_NONE && datestyle.order == DATESTYLE_ORDER_NONE) { - assert(0); return {}; } diff --git a/lib/PgSQL_Thread.cpp b/lib/PgSQL_Thread.cpp index 342a99aef..01aef09d6 100644 --- a/lib/PgSQL_Thread.cpp +++ b/lib/PgSQL_Thread.cpp @@ -1020,7 +1020,7 @@ PgSQL_Threads_Handler::PgSQL_Threads_Handler() { variables.init_connect = NULL; variables.ldap_user_variable = NULL; variables.add_ldap_user_comment = NULL; - for (int i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { variables.default_variables[i] = strdup(pgsql_tracked_variables[i].default_value); } variables.default_session_track_gtids = strdup((char*)MYSQL_DEFAULT_SESSION_TRACK_GTIDS); @@ -1289,7 +1289,7 @@ char* PgSQL_Threads_Handler::get_variable_string(char* name) { } } if (!strncmp(name, "default_", 8)) { - for (int i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { if (IS_PGTRACKED_VAR_OPTION_SET_GLOBAL_VARIABLE(pgsql_tracked_variables[i]) == false) continue; char buf[128]; @@ -1422,7 +1422,7 @@ char* PgSQL_Threads_Handler::get_variable(char* name) { // this is the public fu } if (strlen(name) > 8) { if (strncmp(name, "default_", 8) == 0) { - for (unsigned int i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (unsigned int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { if (IS_PGTRACKED_VAR_OPTION_SET_GLOBAL_VARIABLE(pgsql_tracked_variables[i])) { size_t var_len = strlen(pgsql_tracked_variables[i].internal_variable_name); if (strlen(name) == (var_len + 8)) { @@ -1802,7 +1802,11 @@ bool PgSQL_Threads_Handler::set_variable(char* name, const char* value) { // thi } if (!strncmp(name, "default_", 8)) { - for (int i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { + + if (i == PGSQL_NAME_LAST_LOW_WM) + continue; + if (IS_PGTRACKED_VAR_OPTION_SET_GLOBAL_VARIABLE(pgsql_tracked_variables[i]) == false) continue; char buf[128]; @@ -1810,13 +1814,11 @@ bool PgSQL_Threads_Handler::set_variable(char* name, const char* value) { // thi if (!strcmp(name, buf)) { if (i == PGSQL_DATESTYLE) { - if (vallen) { - // Ensure a complete DateStyle value is provided by validating both format and order. - PgSQL_DateStyle_t datestyle = PgSQL_DateStyle_Util::parse_datestyle(value); - if (datestyle.format == DATESTYLE_FORMAT_NONE || datestyle.order == DATESTYLE_ORDER_NONE) { - proxy_error("Invalid DateStyle value. Please provide both format and order (e.g., 'ISO, DMY'). %s\n", value); - return false; - } + // Ensure a complete DateStyle value is provided by validating both format and order. + PgSQL_DateStyle_t datestyle = PgSQL_DateStyle_Util::parse_datestyle(value); + if (datestyle.format == DATESTYLE_FORMAT_NONE || datestyle.order == DATESTYLE_ORDER_NONE) { + proxy_error("Invalid DateStyle value. Please provide both format and order (e.g., 'ISO, DMY'). %s\n", value); + return false; } } @@ -2241,13 +2243,13 @@ char** PgSQL_Threads_Handler::get_variables_list() { const size_t l = sizeof(pgsql_thread_variables_names) / sizeof(char*); unsigned int i; size_t ltv = 0; - for (i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { if (IS_PGTRACKED_VAR_OPTION_SET_GLOBAL_VARIABLE(pgsql_tracked_variables[i])) ltv++; } char** ret = (char**)malloc(sizeof(char*) * (l + ltv)); // not adding + 1 because pgsql_thread_variables_names is already NULL terminated size_t fv = 0; - for (i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { if (IS_PGTRACKED_VAR_OPTION_SET_GLOBAL_VARIABLE(pgsql_tracked_variables[i])) { char* m = (char*)malloc(strlen(pgsql_tracked_variables[i].internal_variable_name) + 1 + strlen((char*)"default_")); sprintf(m, "default_%s", pgsql_tracked_variables[i].internal_variable_name); @@ -2269,7 +2271,7 @@ char** PgSQL_Threads_Handler::get_variables_list() { bool PgSQL_Threads_Handler::has_variable(const char* name) { if (strlen(name) > 8) { if (strncmp(name, "default_", 8) == 0) { - for (unsigned int i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (unsigned int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { if (IS_PGTRACKED_VAR_OPTION_SET_GLOBAL_VARIABLE(pgsql_tracked_variables[i])) { size_t var_len = strlen(pgsql_tracked_variables[i].internal_variable_name); if (strlen(name) == (var_len + 8)) { @@ -2648,7 +2650,7 @@ PgSQL_Threads_Handler::~PgSQL_Threads_Handler() { if (variables.ssl_p2s_crl) free(variables.ssl_p2s_crl); if (variables.ssl_p2s_crlpath) free(variables.ssl_p2s_crlpath); - for (int i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { if (variables.default_variables[i]) { free(variables.default_variables[i]); variables.default_variables[i] = NULL; @@ -2777,7 +2779,7 @@ PgSQL_Thread::~PgSQL_Thread() { if (pgsql_thread___server_version) { free(pgsql_thread___server_version); pgsql_thread___server_version = NULL; } if (pgsql_thread___server_encoding) { free(pgsql_thread___server_encoding); pgsql_thread___server_encoding = NULL; } - for (int i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { if (pgsql_thread___default_variables[i]) { free(pgsql_thread___default_variables[i]); pgsql_thread___default_variables[i] = NULL; @@ -3902,7 +3904,7 @@ void PgSQL_Thread::refresh_variables() { mysql_thread___default_session_track_gtids = GloPTH->get_variable_string((char*)"default_session_track_gtids"); */ - for (int i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { if (pgsql_thread___default_variables[i]) { free(pgsql_thread___default_variables[i]); pgsql_thread___default_variables[i] = NULL; @@ -4067,7 +4069,7 @@ PgSQL_Thread::PgSQL_Thread() { variables.stats_time_query_processor = false; variables.query_cache_stores_empty_result = true; - for (int i = 0; i < PGSQL_NAME_LAST_LOW_WM; i++) { + for (int i = 0; i < PGSQL_NAME_LAST_HIGH_WM; i++) { pgsql_thread___default_variables[i] = NULL; } shutdown = 0;