From a1fd4af1a64785c432deefddbfd67e7f33aeb5c5 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 3 Jul 2024 20:15:46 +0000 Subject: [PATCH] Splitting ProxySQL_Admin --- include/Admin_ifaces.h | 150 + lib/Admin_Bootstrap.cpp | 1037 ++++ lib/Admin_FlushVariables.cpp | 1174 +++++ lib/Admin_Handler.cpp | 3658 ++++++++++++++ lib/Makefile | 1 + lib/ProxySQL_Admin.cpp | 8846 +++++++--------------------------- lib/ProxySQL_Admin_Stats.cpp | 10 +- 7 files changed, 7767 insertions(+), 7109 deletions(-) create mode 100644 include/Admin_ifaces.h create mode 100644 lib/Admin_Bootstrap.cpp create mode 100644 lib/Admin_FlushVariables.cpp create mode 100644 lib/Admin_Handler.cpp diff --git a/include/Admin_ifaces.h b/include/Admin_ifaces.h new file mode 100644 index 000000000..7d009d0d4 --- /dev/null +++ b/include/Admin_ifaces.h @@ -0,0 +1,150 @@ +#ifndef ADMIN_IFACES_H +#define ADMIN_IFACES_H + +#define MAX_IFACES 8 +#define MAX_ADMIN_LISTENERS 16 + +typedef struct _main_args { + int nfds; + struct pollfd *fds; + int *callback_func; + volatile int *shutdown; +} main_args; + +typedef struct _ifaces_desc_t { + char **mysql_ifaces; + char **pgsql_ifaces; + char **telnet_admin_ifaces; + char **telnet_stats_ifaces; +} ifaces_desc_t; + +class ifaces_desc { + public: + PtrArray *ifaces; + ifaces_desc() { + ifaces=new PtrArray(); + } + bool add(const char *iface) { + for (unsigned int i=0; ilen; i++) { + if (strcmp((const char *)ifaces->index(i),iface)==0) { + return false; + } + } + ifaces->add(strdup(iface)); + return true; + } + ~ifaces_desc() { + while(ifaces->len) { + char *d=(char *)ifaces->remove_index_fast(0); + free(d); + } + delete ifaces; + } +}; + +class admin_main_loop_listeners { + private: + int version; +#ifdef PA_PTHREAD_MUTEX + pthread_rwlock_t rwlock; +#else + rwlock_t rwlock; +#endif + + char ** reset_ifaces(char **ifaces) { + int i; + if (ifaces) { + for (i=0; iadd(token); + i++; + } + free_tokenizer( &tok ); + version++; + wrunlock(); + } + + + bool update_ifaces(char *list, char ***_ifaces) { + wrlock(); + int i; + char **ifaces=*_ifaces; + tokenizer_t tok; + tokenizer( &tok, list, ";", TOKENIZER_NO_EMPTIES ); + const char* token; + ifaces=reset_ifaces(ifaces); + i=0; + for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { + ifaces[i]=(char *)malloc(strlen(token)+1); + strcpy(ifaces[i],token); + i++; + } + free_tokenizer( &tok ); + version++; + wrunlock(); + return true; + } +}; +#endif // ADMIN_IFACES_H diff --git a/lib/Admin_Bootstrap.cpp b/lib/Admin_Bootstrap.cpp new file mode 100644 index 000000000..861f12490 --- /dev/null +++ b/lib/Admin_Bootstrap.cpp @@ -0,0 +1,1037 @@ +#include "../deps/json/json.hpp" +using json = nlohmann::json; +#define PROXYJSON + +#include // std::cout +#include // std::stringstream +#include +#include // std::sort +#include +#include // std::vector +#include +#include "prometheus/exposer.h" +#include "prometheus/counter.h" +#include "openssl/ssl.h" +#include "openssl/err.h" + +#include "Base_Thread.h" + +#include "MySQL_HostGroups_Manager.h" +#include "PgSQL_HostGroups_Manager.h" +#include "mysql.h" +#include "proxysql_admin.h" +#include "re2/re2.h" +#include "re2/regexp.h" +#include "proxysql.h" +#include "proxysql_config.h" +#include "proxysql_restapi.h" +#include "proxysql_utils.h" +#include "prometheus_helpers.h" +#include "cpp.h" + +#include "MySQL_Data_Stream.h" +#include "PgSQL_Data_Stream.h" +#include "query_processor.h" +#include "ProxySQL_HTTP_Server.hpp" // HTTP server +#include "MySQL_Authentication.hpp" +#include "PgSQL_Authentication.h" +#include "MySQL_LDAP_Authentication.hpp" +#include "MySQL_PreparedStatement.h" +#include "ProxySQL_Cluster.hpp" +#include "ProxySQL_Statistics.hpp" +#include "MySQL_Logger.hpp" +#include "PgSQL_Logger.hpp" +#include "SQLite3_Server.h" +#include "Web_Interface.hpp" +#include "Client_Session.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef SPOOKYV2 +#include "SpookyV2.h" +#define SPOOKYV2 +#endif + +#include +#include + +#include "platform.h" +#include "microhttpd.h" + +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__)) && defined(__linux) +// currently only support x86-32, x86-64, ARM, and MIPS on Linux +#include "coredumper/coredumper.h" +#endif + +#include + +#include "PgSQL_Protocol.h" +//#include "usual/time.h" + +using std::string; +using std::unique_ptr; + +#ifdef WITHGCOV +extern "C" void __gcov_dump(); +extern "C" void __gcov_reset(); +#endif + + +#ifdef DEBUG +//#define BENCHMARK_FASTROUTING_LOAD +#endif // DEBUG + +//#define MYSQL_THREAD_IMPLEMENTATION + +#define SELECT_VERSION_COMMENT "select @@version_comment limit 1" +#define SELECT_VERSION_COMMENT_LEN 32 +#define SELECT_DB_USER "select DATABASE(), USER() limit 1" +#define SELECT_DB_USER_LEN 33 +#define SELECT_CHARSET_VARIOUS "select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1" +#define SELECT_CHARSET_VARIOUS_LEN 115 + +#define READ_ONLY_OFF "\x01\x00\x00\x01\x02\x23\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x0d\x56\x61\x72\x69\x61\x62\x6c\x65\x5f\x6e\x61\x6d\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x1b\x00\x00\x03\x03\x64\x65\x66\x00\x00\x00\x05\x56\x61\x6c\x75\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x05\x00\x00\x04\xfe\x00\x00\x02\x00\x0e\x00\x00\x05\x09\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x03\x4f\x46\x46\x05\x00\x00\x06\xfe\x00\x00\x02\x00" +#define READ_ONLY_ON "\x01\x00\x00\x01\x02\x23\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x0d\x56\x61\x72\x69\x61\x62\x6c\x65\x5f\x6e\x61\x6d\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x1b\x00\x00\x03\x03\x64\x65\x66\x00\x00\x00\x05\x56\x61\x6c\x75\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x05\x00\x00\x04\xfe\x00\x00\x02\x00\x0d\x00\x00\x05\x09\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x02\x4f\x4e\x05\x00\x00\x06\xfe\x00\x00\x02\x00" + +#define READ_ONLY_0 "\x01\x00\x00\x01\x01\x28\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x12\x40\x40\x67\x6c\x6f\x62\x61\x6c\x2e\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x00\x0c\x3f\x00\x01\x00\x00\x00\x08\x80\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x02\x00\x00\x04\x01\x30\x05\x00\x00\x05\xfe\x00\x00\x02\x00" + +#define READ_ONLY_1 "\x01\x00\x00\x01\x01\x28\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x12\x40\x40\x67\x6c\x6f\x62\x61\x6c\x2e\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x00\x0c\x3f\x00\x01\x00\x00\x00\x08\x80\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x02\x00\x00\x04\x01\x31\x05\x00\x00\x05\xfe\x00\x00\x02\x00" + +extern struct MHD_Daemon *Admin_HTTP_Server; + +extern ProxySQL_Statistics *GloProxyStats; + +int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char **msg); + +extern char *ssl_key_fp; +extern char *ssl_cert_fp; +extern char *ssl_ca_fp; + +// ProxySQL_Admin shared variables +extern int admin___web_verbosity; +extern char * proxysql_version; + +#include "proxysql_find_charset.h" + +template ::value, bool>::type = true> +T j_get_srv_default_int_val( +const json& j, uint32_t hid, const string& key, const function& val_check); + +struct cpu_timer +{ + cpu_timer() { + begin = monotonic_time(); + } + ~cpu_timer() + { + unsigned long long end = monotonic_time(); +#ifdef DEBUG + std::cerr << double( end - begin ) / 1000000 << " secs.\n" ; +#endif + begin=end-begin; // make the compiler happy + }; + unsigned long long begin; +}; + +extern int admin_load_main_; +extern bool admin_nostart_; + +extern Query_Cache *GloQC; +extern MySQL_Authentication *GloMyAuth; +extern PgSQL_Authentication *GloPgAuth; +extern MySQL_LDAP_Authentication *GloMyLdapAuth; +extern ProxySQL_Admin *GloAdmin; +extern Query_Processor *GloQPro; +extern MySQL_Threads_Handler *GloMTH; +extern MySQL_Logger *GloMyLogger; +extern PgSQL_Logger* GloPgSQL_Logger; +extern MySQL_STMT_Manager_v14 *GloMyStmt; +extern MySQL_Monitor *GloMyMon; +extern PgSQL_Threads_Handler* GloPTH; + +extern void (*flush_logs_function)(); + +extern Web_Interface *GloWebInterface; + +extern ProxySQL_Cluster *GloProxyCluster; +#ifdef PROXYSQLCLICKHOUSE +extern ClickHouse_Authentication *GloClickHouseAuth; +extern ClickHouse_Server *GloClickHouseServer; +#endif /* PROXYSQLCLICKHOUSE */ + +extern SQLite3_Server *GloSQLite3Server; + +extern char * binary_sha1; + +extern int ProxySQL_create_or_load_TLS(bool bootstrap, std::string& msg); + +#include "ProxySQL_Admin_Tables_Definitions.h" + +extern void * (*child_func[3]) (void *arg); + +bootstrap_info_t::~bootstrap_info_t() { + if (servers != nullptr) { + mysql_free_result(servers); + } + if (users != nullptr) { + mysql_free_result(users); + } +} + +#include "Admin_ifaces.h" +extern admin_main_loop_listeners S_amll; + +static void flush_logs_handler() { + GloAdmin->flush_logs(); +} + +extern void * admin_main_loop(void *arg); + +struct boot_srv_info_t { + string member_id; + string member_host; + uint32_t member_port; + string member_state; + string member_role; + string member_version; +}; + +struct BOOT_SRV_INFO_T { + enum { + MEMBER_ID, + MEMBER_HOST, + MEMBER_PORT, + MEMBER_STATE, + MEMBER_ROLE, + MEMBER_VERSION + }; +}; + +struct boot_user_info_t { + string user; + string ssl_type; + string auth_string; + string auth_plugin; + bool password_expired; +}; + +struct BOOT_USER_INFO_T { + enum { + USER, + SSL_TYPE, + AUTH_STRING, + AUTH_PLUGIN, + PASSWORD_EXPIRED + }; +}; + +struct srv_defs_t { + int64_t weight; + int64_t max_conns; + int32_t use_ssl; +}; + +using boot_srv_cnf_t = pair; + +vector extract_boot_servers_info(MYSQL_RES* servers) { + vector servers_info {}; + + while (MYSQL_ROW row = mysql_fetch_row(servers)) { + servers_info.push_back({ + string { row[BOOT_SRV_INFO_T::MEMBER_ID] }, + string { row[BOOT_SRV_INFO_T::MEMBER_HOST] }, + static_cast(stoi(row[BOOT_SRV_INFO_T::MEMBER_PORT])), + string { row[BOOT_SRV_INFO_T::MEMBER_STATE] }, + string { row[BOOT_SRV_INFO_T::MEMBER_ROLE] }, + string { row[BOOT_SRV_INFO_T::MEMBER_VERSION] }, + }); + } + + return servers_info; +} + +string build_boot_servers_insert(const vector& srvs_info_defs) { + const string t_srvs_insert { + "INSERT INTO mysql_servers (hostgroup_id,hostname,port,status,weight,max_connections,use_ssl) VALUES " + }; + string t_srvs_values {}; + + for (const auto& info_defs : srvs_info_defs) { + const boot_srv_info_t& srv_info = info_defs.first; + const srv_defs_t& srv_defs = info_defs.second; + + const char t_values[] { "(%d, \"%s\", %d, \"%s\", %ld, %ld, %d)" }; + string srv_values = cstr_format( + t_values, + srv_info.member_role == "PRIMARY" ? 0 : 1, // HOSTGROUP_ID + srv_info.member_host.c_str(), // HOSTNAME + srv_info.member_port, // PORT + srv_info.member_state.c_str(), // STATUS + srv_defs.weight, // Weight + srv_defs.max_conns, // Max Connections + srv_defs.use_ssl // UseSSL + ).str; + + if (&info_defs != &srvs_info_defs.back()) { + srv_values += ","; + } + + t_srvs_values += srv_values; + } + + const string servers_insert { t_srvs_insert + t_srvs_values }; + + return servers_insert; +} + +string build_boot_users_insert(MYSQL_RES* users) { + vector users_info {}; + + while (MYSQL_ROW row = mysql_fetch_row(users)) { + users_info.push_back({ + string { row[BOOT_USER_INFO_T::USER] }, + string { row[BOOT_USER_INFO_T::SSL_TYPE] }, + string { row[BOOT_USER_INFO_T::AUTH_STRING] }, + string { row[BOOT_USER_INFO_T::AUTH_PLUGIN] }, + static_cast(atoi(row[BOOT_USER_INFO_T::PASSWORD_EXPIRED])) + }); + } + + // MySQL Users + const string t_users_insert { + "INSERT INTO mysql_users (username,password,active,use_ssl) VALUES " + }; + string t_users_values {}; + + for (const boot_user_info_t& user : users_info) { + uint32_t use_ssl = user.ssl_type.empty() ? 0 : 1; + const char t_values[] { "(\"%s\", \"%s\", %d, %d)" }; + + string srv_values = cstr_format( + t_values, + user.user.c_str(), // USERNAME + user.auth_string.c_str(), // HOSTNAME + 1, // ACTIVE: Always ON + use_ssl // USE_SSL: Dependent on backend user + ).str; + + if (&user != &users_info.back()) { + srv_values += ","; + } + + t_users_values += srv_values; + } + + const string users_insert { t_users_insert + t_users_values }; + + return users_insert; +} + +map get_cur_hg_attrs(SQLite3DB* admindb) { + map res {}; + + char* error = nullptr; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = NULL; + + admindb->execute_statement( + "SELECT hostgroup_id,servers_defaults FROM mysql_hostgroup_attributes", + &error, &cols, &affected_rows, &resultset + ); + + for (SQLite3_row* row : resultset->rows) { + const int32_t hid = atoi(row->fields[0]); + srv_defs_t srv_defs {}; + srv_defs.weight = 1; + srv_defs.max_conns = 512; + srv_defs.use_ssl = 1; + + nlohmann::json j_srv_defs = nlohmann::json::parse(row->fields[1]); + + const auto weight_check = [] (int64_t weight) -> bool { return weight >= 0; }; + srv_defs.weight = j_get_srv_default_int_val(j_srv_defs, hid, "weight", weight_check); + + const auto max_conns_check = [] (int64_t max_conns) -> bool { return max_conns >= 0; }; + srv_defs.max_conns = j_get_srv_default_int_val(j_srv_defs, hid, "max_connections", max_conns_check); + + const auto use_ssl_check = [] (int32_t use_ssl) -> bool { return use_ssl == 0 || use_ssl == 1; }; + srv_defs.use_ssl = j_get_srv_default_int_val(j_srv_defs, hid, "use_ssl", use_ssl_check); + + res.insert({ hid , srv_defs }); + } + + delete resultset; + + return res; +} + +vector build_srvs_info_with_defs( + const vector& srvs_info, + const map& hgid_defs, + const srv_defs_t global_defs +) { + vector res {}; + + for (const boot_srv_info_t& srv_info : srvs_info) { + if (srv_info.member_role == "PRIMARY") { + const auto hg_it = hgid_defs.find(0); + + if (hg_it != hgid_defs.end()) { + res.push_back({ srv_info, hg_it->second }); + } else { + res.push_back({ srv_info, global_defs }); + } + } else { + const auto hg_it = hgid_defs.find(1); + + if (hg_it != hgid_defs.end()) { + res.push_back({ srv_info, hg_it->second }); + } else { + res.push_back({ srv_info, global_defs }); + } + } + } + + return res; +} + +/** + * @brief Helper function used to check if tables are already filled with data. + * @details Handles the boilerplate operations of executing 'SELECT COUNT(*)' alike queries. + * @param admindb An already initialized instance of a SQLite3DB object to 'mem_admindb'. + * @param query The query to be executed, it's required to be 'SELECT COUNT(*)' alike. + * @return The resulting int of the 'COUNT(*)' in case of success, '-1' otherwise. In case of error, error + * cause are logged, and `assert` is called. + */ +int check_if_user_config(SQLite3DB* admindb, const char* query) { + char* error = nullptr; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = NULL; + + admindb->execute_statement(query, &error, &cols, &affected_rows, &resultset); + if (error) { + proxy_error( + "Aborting due to failed query over SQLite3 - db: '%s', query: '%s', err: %s", admindb->get_url(), query, error + ); + assert(0); + } + + int count = -1; + + if (resultset != nullptr && !resultset->rows.empty() && resultset->rows[0]->cnt >= 0) { + const char* s_count = resultset->rows[0]->fields[0]; + char* p_end = nullptr; + + count = std::strtol(s_count, &p_end, 10); + + if (p_end == s_count || errno == ERANGE) { + proxy_error( + "Aborting due to invalid query output, expected single INT (E.g. 'COUNT(*)') - query: '%s'", query + ); + count = -1; + } + } + + if (count == -1) { + assert(0); + } + + delete resultset; + return count; +}; + +/** + * @brief Definition of an auxiliary table used to store bootstrap variables. + * @details Table is used only to store in configdb bootstrap variables that are required to persist between + * executions. + */ +#define ADMIN_SQLITE_TABLE_BOOTSTRAP_VARIABLES "CREATE TABLE IF NOT EXISTS bootstrap_variables (variable_name VARCHAR NOT NULL PRIMARY KEY , variable_value VARCHAR NOT NULL)" + + +extern void *child_mysql(void *arg); +extern void *child_telnet(void *arg); +extern void *child_postgres(void *arg); + + +bool ProxySQL_Admin::init(const bootstrap_info_t& bootstrap_info) { + cpu_timer cpt; + + if (flush_logs_function == NULL) { + flush_logs_function = flush_logs_handler; + } + + Admin_HTTP_Server = NULL; + AdminRestApiServer = NULL; + AdminHTTPServer = NULL; + +/* + AdminRestApiServer = new ProxySQL_RESTAPI_Server(); + AdminRestApiServer->print_version(); +*/ + + child_func[0]=child_mysql; + child_func[1]=child_telnet; + child_func[2]=child_postgres; + main_shutdown=0; + main_poll_nfds=0; + main_poll_fds=NULL; + main_callback_func=NULL; + + { + int rc=pipe(pipefd); + if (rc) { + perror("Call to pipe() failed"); + exit(EXIT_FAILURE); + } + } + + main_callback_func=(int *)malloc(sizeof(int)*MAX_ADMIN_LISTENERS); + main_poll_fds=(struct pollfd *)malloc(sizeof(struct pollfd)*MAX_ADMIN_LISTENERS); + main_poll_nfds=0; + + pthread_attr_t attr; + pthread_attr_init(&attr); + //pthread_attr_setstacksize (&attr, mystacksize); + + admindb=new SQLite3DB(); + admindb->open((char *)"file:mem_admindb?mode=memory&cache=shared", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); + admindb->execute("PRAGMA cache_size = -50000"); + //sqlite3_enable_load_extension(admindb->get_db(),1); + //sqlite3_auto_extension( (void(*)(void))sqlite3_json_init); + statsdb=new SQLite3DB(); + statsdb->open((char *)"file:mem_statsdb?mode=memory&cache=shared", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); + + // check if file exists , see #617 + bool admindb_file_exists=Proxy_file_exists(GloVars.admindb); + + configdb=new SQLite3DB(); + configdb->open((char *)GloVars.admindb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); + // Fully synchronous is not required. See to #1055 + // https://sqlite.org/pragma.html#pragma_synchronous + configdb->execute("PRAGMA synchronous=0"); + + monitordb = new SQLite3DB(); + monitordb->open((char *)"file:mem_monitordb?mode=memory&cache=shared", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); + + statsdb_disk = new SQLite3DB(); + statsdb_disk->open((char *)GloVars.statsdb_disk, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); +// char *dbname = (char *)malloc(strlen(GloVars.statsdb_disk)+50); +// sprintf(dbname,"%s?mode=memory&cache=shared",GloVars.statsdb_disk); +// statsdb_disk->open(dbname, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_FULLMUTEX); +// free(dbname); + + statsdb_disk->execute("PRAGMA synchronous=0"); +// GloProxyStats->statsdb_disk = configdb; + GloProxyStats->init(); + + tables_defs_admin=new std::vector; + tables_defs_stats=new std::vector; + tables_defs_config=new std::vector; + + insert_into_tables_defs(tables_defs_admin,"mysql_servers", ADMIN_SQLITE_TABLE_MYSQL_SERVERS); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_servers", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_SERVERS); + insert_into_tables_defs(tables_defs_admin,"mysql_users", ADMIN_SQLITE_TABLE_MYSQL_USERS); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_users", ADMIN_SQLITE_RUNTIME_MYSQL_USERS); + insert_into_tables_defs(tables_defs_admin,"runtime_checksums_values", ADMIN_SQLITE_RUNTIME_CHECKSUMS_VALUES); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_replication_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_REPLICATION_HOSTGROUPS); + insert_into_tables_defs(tables_defs_admin,"mysql_replication_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_REPLICATION_HOSTGROUPS); + insert_into_tables_defs(tables_defs_admin,"mysql_group_replication_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_GROUP_REPLICATION_HOSTGROUPS); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_group_replication_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_GROUP_REPLICATION_HOSTGROUPS); + insert_into_tables_defs(tables_defs_admin,"mysql_galera_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_GALERA_HOSTGROUPS); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_galera_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_GALERA_HOSTGROUPS); + insert_into_tables_defs(tables_defs_admin,"mysql_aws_aurora_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_AWS_AURORA_HOSTGROUPS); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_aws_aurora_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_AWS_AURORA_HOSTGROUPS); + insert_into_tables_defs(tables_defs_admin,"mysql_hostgroup_attributes", ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_hostgroup_attributes", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_HOSTGROUP_ATTRIBUTES); + insert_into_tables_defs(tables_defs_admin,"mysql_servers_ssl_params", ADMIN_SQLITE_TABLE_MYSQL_SERVERS_SSL_PARAMS); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_servers_ssl_params", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_SERVERS_SSL_PARAMS); + insert_into_tables_defs(tables_defs_admin,"mysql_query_rules", ADMIN_SQLITE_TABLE_MYSQL_QUERY_RULES); + insert_into_tables_defs(tables_defs_admin,"mysql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_MYSQL_QUERY_RULES_FAST_ROUTING); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_query_rules", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_QUERY_RULES); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_QUERY_RULES_FAST_ROUTING); + insert_into_tables_defs(tables_defs_admin,"global_variables", ADMIN_SQLITE_TABLE_GLOBAL_VARIABLES); + insert_into_tables_defs(tables_defs_admin,"runtime_global_variables", ADMIN_SQLITE_RUNTIME_GLOBAL_VARIABLES); + insert_into_tables_defs(tables_defs_admin,"mysql_collations", ADMIN_SQLITE_TABLE_MYSQL_COLLATIONS); + insert_into_tables_defs(tables_defs_admin,"scheduler", ADMIN_SQLITE_TABLE_SCHEDULER); + insert_into_tables_defs(tables_defs_admin,"runtime_scheduler", ADMIN_SQLITE_TABLE_RUNTIME_SCHEDULER); + insert_into_tables_defs(tables_defs_admin,"mysql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_USERS); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_FIREWALL_WHITELIST_USERS); + insert_into_tables_defs(tables_defs_admin,"mysql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_RULES); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_FIREWALL_WHITELIST_RULES); + insert_into_tables_defs(tables_defs_admin,"mysql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); + insert_into_tables_defs(tables_defs_admin,"restapi_routes", ADMIN_SQLITE_TABLE_RESTAPI_ROUTES); + insert_into_tables_defs(tables_defs_admin,"runtime_restapi_routes", ADMIN_SQLITE_TABLE_RUNTIME_RESTAPI_ROUTES); + insert_into_tables_defs(tables_defs_admin,"coredump_filters", ADMIN_SQLITE_TABLE_COREDUMP_FILTERS); + insert_into_tables_defs(tables_defs_admin,"runtime_coredump_filters", ADMIN_SQLITE_RUNTIME_COREDUMP_FILTERS); +#ifdef DEBUG + insert_into_tables_defs(tables_defs_admin,"debug_levels", ADMIN_SQLITE_TABLE_DEBUG_LEVELS); + insert_into_tables_defs(tables_defs_admin,"debug_filters", ADMIN_SQLITE_TABLE_DEBUG_FILTERS); +#endif /* DEBUG */ +#ifdef PROXYSQLCLICKHOUSE + // ClickHouse + if (GloVars.global.clickhouse_server) { + insert_into_tables_defs(tables_defs_admin,"clickhouse_users", ADMIN_SQLITE_TABLE_CLICKHOUSE_USERS); + insert_into_tables_defs(tables_defs_admin,"runtime_clickhouse_users", ADMIN_SQLITE_TABLE_RUNTIME_CLICKHOUSE_USERS); + } +#endif /* PROXYSQLCLICKHOUSE */ + + // PgSQL + insert_into_tables_defs(tables_defs_admin, "pgsql_servers", ADMIN_SQLITE_TABLE_PGSQL_SERVERS); + insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_servers", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_SERVERS); + insert_into_tables_defs(tables_defs_admin, "pgsql_users", ADMIN_SQLITE_TABLE_PGSQL_USERS); + insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_users", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_USERS); + insert_into_tables_defs(tables_defs_admin, "pgsql_ldap_mapping", ADMIN_SQLITE_TABLE_PGSQL_LDAP_MAPPING); + insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_ldap_mapping", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_LDAP_MAPPING); + insert_into_tables_defs(tables_defs_admin, "pgsql_query_rules", ADMIN_SQLITE_TABLE_PGSQL_QUERY_RULES); + insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_query_rules", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_QUERY_RULES); + insert_into_tables_defs(tables_defs_admin, "pgsql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_PGSQL_QUERY_RULES_FAST_ROUTING); + insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_QUERY_RULES_FAST_ROUTING); + insert_into_tables_defs(tables_defs_admin, "pgsql_hostgroup_attributes", ADMIN_SQLITE_TABLE_PGSQL_HOSTGROUP_ATTRIBUTES); + insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_hostgroup_attributes", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_HOSTGROUP_ATTRIBUTES); + insert_into_tables_defs(tables_defs_admin, "pgsql_replication_hostgroups", ADMIN_SQLITE_TABLE_PGSQL_REPLICATION_HOSTGROUPS); + insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_replication_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_REPLICATION_HOSTGROUPS); + + insert_into_tables_defs(tables_defs_admin, "pgsql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_USERS); + insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_FIREWALL_WHITELIST_USERS); + insert_into_tables_defs(tables_defs_admin, "pgsql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_RULES); + insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_FIREWALL_WHITELIST_RULES); + insert_into_tables_defs(tables_defs_admin, "pgsql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); + insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); + + insert_into_tables_defs(tables_defs_config, "pgsql_servers", ADMIN_SQLITE_TABLE_PGSQL_SERVERS); + insert_into_tables_defs(tables_defs_config, "pgsql_users", ADMIN_SQLITE_TABLE_PGSQL_USERS); + insert_into_tables_defs(tables_defs_config, "pgsql_ldap_mapping", ADMIN_SQLITE_TABLE_PGSQL_LDAP_MAPPING); + insert_into_tables_defs(tables_defs_config, "pgsql_query_rules", ADMIN_SQLITE_TABLE_PGSQL_QUERY_RULES); + insert_into_tables_defs(tables_defs_config, "pgsql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_PGSQL_QUERY_RULES_FAST_ROUTING); + insert_into_tables_defs(tables_defs_config, "pgsql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_USERS); + insert_into_tables_defs(tables_defs_config, "pgsql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_RULES); + insert_into_tables_defs(tables_defs_config, "pgsql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); + // + + insert_into_tables_defs(tables_defs_config,"mysql_servers", ADMIN_SQLITE_TABLE_MYSQL_SERVERS); + insert_into_tables_defs(tables_defs_config,"mysql_users", ADMIN_SQLITE_TABLE_MYSQL_USERS); + insert_into_tables_defs(tables_defs_config,"mysql_replication_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_REPLICATION_HOSTGROUPS); + insert_into_tables_defs(tables_defs_config,"mysql_group_replication_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_GROUP_REPLICATION_HOSTGROUPS); + insert_into_tables_defs(tables_defs_config,"mysql_galera_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_GALERA_HOSTGROUPS); + insert_into_tables_defs(tables_defs_config,"mysql_aws_aurora_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_AWS_AURORA_HOSTGROUPS); + insert_into_tables_defs(tables_defs_config,"mysql_hostgroup_attributes", ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES); + insert_into_tables_defs(tables_defs_config,"mysql_servers_ssl_params", ADMIN_SQLITE_TABLE_MYSQL_SERVERS_SSL_PARAMS); + insert_into_tables_defs(tables_defs_config,"mysql_query_rules", ADMIN_SQLITE_TABLE_MYSQL_QUERY_RULES); + insert_into_tables_defs(tables_defs_config,"mysql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_MYSQL_QUERY_RULES_FAST_ROUTING); + insert_into_tables_defs(tables_defs_config,"global_variables", ADMIN_SQLITE_TABLE_GLOBAL_VARIABLES); + insert_into_tables_defs(tables_defs_config,"global_settings", ADMIN_SQLITE_TABLE_GLOBAL_SETTINGS); + // the table is not required to be present on disk. Removing it due to #1055 + insert_into_tables_defs(tables_defs_config,"mysql_collations", ADMIN_SQLITE_TABLE_MYSQL_COLLATIONS); + insert_into_tables_defs(tables_defs_config,"scheduler", ADMIN_SQLITE_TABLE_SCHEDULER); + insert_into_tables_defs(tables_defs_config,"mysql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_USERS); + insert_into_tables_defs(tables_defs_config,"mysql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_RULES); + insert_into_tables_defs(tables_defs_config,"mysql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); + insert_into_tables_defs(tables_defs_config, "restapi_routes", ADMIN_SQLITE_TABLE_RESTAPI_ROUTES); +#ifdef DEBUG + insert_into_tables_defs(tables_defs_config,"debug_levels", ADMIN_SQLITE_TABLE_DEBUG_LEVELS); + insert_into_tables_defs(tables_defs_config,"debug_filters", ADMIN_SQLITE_TABLE_DEBUG_FILTERS); +#endif /* DEBUG */ +#ifdef PROXYSQLCLICKHOUSE + // ClickHouse + if (GloVars.global.clickhouse_server) { + insert_into_tables_defs(tables_defs_config,"clickhouse_users", ADMIN_SQLITE_TABLE_CLICKHOUSE_USERS); + } +#endif /* PROXYSQLCLICKHOUSE */ + + insert_into_tables_defs(tables_defs_stats,"stats_mysql_query_rules", STATS_SQLITE_TABLE_MYSQL_QUERY_RULES); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_commands_counters", STATS_SQLITE_TABLE_MYSQL_COMMANDS_COUNTERS); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_processlist", STATS_SQLITE_TABLE_MYSQL_PROCESSLIST); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_connection_pool", STATS_SQLITE_TABLE_MYSQL_CONNECTION_POOL); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_connection_pool_reset", STATS_SQLITE_TABLE_MYSQL_CONNECTION_POOL_RESET); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_free_connections", STATS_SQLITE_TABLE_MYSQL_FREE_CONNECTIONS); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_query_digest", STATS_SQLITE_TABLE_MYSQL_QUERY_DIGEST); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_query_digest_reset", STATS_SQLITE_TABLE_MYSQL_QUERY_DIGEST_RESET); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_errors", STATS_SQLITE_TABLE_MYSQL_ERRORS); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_errors_reset", STATS_SQLITE_TABLE_MYSQL_ERRORS_RESET); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_global", STATS_SQLITE_TABLE_MYSQL_GLOBAL); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_gtid_executed", STATS_SQLITE_TABLE_MYSQL_GTID_EXECUTED); + insert_into_tables_defs(tables_defs_stats,"stats_memory_metrics", STATS_SQLITE_TABLE_MEMORY_METRICS); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_users", STATS_SQLITE_TABLE_MYSQL_USERS); + insert_into_tables_defs(tables_defs_stats,"global_variables", ADMIN_SQLITE_TABLE_GLOBAL_VARIABLES); // workaround for issue #708 + insert_into_tables_defs(tables_defs_stats,"stats_mysql_prepared_statements_info", ADMIN_SQLITE_TABLE_STATS_MYSQL_PREPARED_STATEMENTS_INFO); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_client_host_cache", STATS_SQLITE_TABLE_MYSQL_CLIENT_HOST_CACHE); + insert_into_tables_defs(tables_defs_stats,"stats_mysql_client_host_cache_reset", STATS_SQLITE_TABLE_MYSQL_CLIENT_HOST_CACHE_RESET); + + // ProxySQL Cluster + insert_into_tables_defs(tables_defs_admin,"proxysql_servers", ADMIN_SQLITE_TABLE_PROXYSQL_SERVERS); + insert_into_tables_defs(tables_defs_config,"proxysql_servers", ADMIN_SQLITE_TABLE_PROXYSQL_SERVERS); + insert_into_tables_defs(tables_defs_admin,"runtime_proxysql_servers", ADMIN_SQLITE_TABLE_RUNTIME_PROXYSQL_SERVERS); + insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_checksums", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_CHECKSUMS); + insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_metrics", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_METRICS); + insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_status", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_STATUS); + insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_clients_status", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_CLIENTS_STATUS); + insert_into_tables_defs(tables_defs_stats,"stats_proxysql_message_metrics", STATS_SQLITE_TABLE_PROXYSQL_MESSAGE_METRICS); + insert_into_tables_defs(tables_defs_stats,"stats_proxysql_message_metrics_reset", STATS_SQLITE_TABLE_PROXYSQL_MESSAGE_METRICS_RESET); + + // init ldap here + init_ldap(); + + // upgrade mysql_servers if needed (upgrade from previous version) + disk_upgrade_mysql_servers(); + + // upgrade mysql_users if needed (upgrade from previous version) + disk_upgrade_mysql_users(); + + // upgrade mysql_query_rules if needed (upgrade from previous version) + disk_upgrade_mysql_query_rules(); + + // upgrade scheduler if needed (upgrade from previous version) + disk_upgrade_scheduler(); + + // upgrade restapi_routes if needed (upgrade from previous version) + disk_upgrade_rest_api_routes(); + + check_and_build_standard_tables(admindb, tables_defs_admin); + check_and_build_standard_tables(configdb, tables_defs_config); + check_and_build_standard_tables(statsdb, tables_defs_stats); + + __attach_db(admindb, configdb, (char *)"disk"); + __attach_db(admindb, statsdb, (char *)"stats"); + __attach_db(admindb, monitordb, (char *)"monitor"); + __attach_db(statsdb, monitordb, (char *)"monitor"); + __attach_db(admindb, statsdb_disk, (char *)"stats_history"); + __attach_db(statsdb, statsdb_disk, (char *)"stats_history"); + + dump_mysql_collations(); + +#ifdef DEBUG + admindb->execute("ATTACH DATABASE 'file:mem_mydb?mode=memory&cache=shared' AS myhgm"); + admindb->execute("ATTACH DATABASE 'file:mem_monitor_internal_db?mode=memory&cache=shared' AS 'monitor_internal'"); + { + string debugdb_disk_path = string(GloVars.datadir) + "/" + "proxysql_debug.db"; + debugdb_disk = new SQLite3DB(); + debugdb_disk->open((char *)debugdb_disk_path.c_str(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); + debugdb_disk->execute("CREATE TABLE IF NOT EXISTS debug_log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , time INT NOT NULL , lapse INT NOT NULL , thread INT NOT NULL , file VARCHAR NOT NULL , line INT NOT NULL , funct VARCHAR NOT NULL , modnum INT NOT NULL , modname VARCHAR NOT NULL , verbosity INT NOT NULL , message VARCHAR , note VARCHAR , backtrace VARCHAR)"); +/* + // DO NOT CREATE INDEX. + // We can create index on a running instance or an archived DB if needed + debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_time ON debug_log (time)"); + debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_thread ON debug_log (thread)"); + debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_file ON debug_log (file)"); + debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_file_line ON debug_log (file,line)"); + debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_funct ON debug_log (funct)"); + debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_modnum ON debug_log (modnum)"); +*/ + debugdb_disk->execute("PRAGMA synchronous=0"); + debugdb_disk->execute("PRAGMA journal_mode=OFF"); +/* + // DO NOT ATTACH DATABASE + // it seems sqlite starts randomly failing. For example these 2 TAP tests: + // - admin_show_fields_from-t + // - admin_show_table_status-t + string cmd = "ATTACH DATABASE '" + debugdb_disk_path + "' AS debugdb_disk"; + admindb->execute(cmd.c_str()); +*/ + proxysql_set_admin_debugdb_disk(debugdb_disk); + } +#endif /* DEBUG */ + +#ifdef DEBUG + flush_debug_levels_runtime_to_database(configdb, false); + flush_debug_levels_runtime_to_database(admindb, true); +#endif /* DEBUG */ + + // Set default values for the module variables in the target 'dbs' + flush_mysql_variables___runtime_to_database(configdb, false, false, false); + flush_mysql_variables___runtime_to_database(admindb, false, true, false); + + flush_admin_variables___runtime_to_database(configdb, false, false, false); + flush_admin_variables___runtime_to_database(admindb, false, true, false); + + flush_pgsql_variables___runtime_to_database(configdb, false, false, false); + flush_pgsql_variables___runtime_to_database(admindb, false, true, false); + + load_or_update_global_settings(configdb); + + // Insert or update the configuration from 'disk' + __insert_or_replace_maintable_select_disktable(); + + // removing this line of code. It seems redundant + //flush_admin_variables___database_to_runtime(admindb,true); + + // workaround for issue #708 + statsdb->execute("INSERT OR IGNORE INTO global_variables VALUES('mysql-max_allowed_packet',4194304)"); + + +#ifdef DEBUG + if (GloVars.global.gdbg==false && GloVars.__cmd_proxysql_gdbg) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Enabling GloVars.global.gdbg because GloVars.__cmd_proxysql_gdbg==%d\n", GloVars.__cmd_proxysql_gdbg); + GloVars.global.gdbg=true; + } + load_debug_to_runtime(); +#endif /* DEBUG */ + + if (GloVars.__cmd_proxysql_reload || GloVars.__cmd_proxysql_initial || admindb_file_exists==false) { // see #617 + if (GloVars.configfile_open) { + proxysql_config().Read_MySQL_Servers_from_configfile(); + proxysql_config().Read_MySQL_Users_from_configfile(); + proxysql_config().Read_MySQL_Query_Rules_from_configfile(); + proxysql_config().Read_Global_Variables_from_configfile("admin"); + proxysql_config().Read_Global_Variables_from_configfile("mysql"); + + proxysql_config().Read_PgSQL_Servers_from_configfile(); + proxysql_config().Read_PgSQL_Users_from_configfile(); + proxysql_config().Read_PgSQL_Query_Rules_from_configfile(); + proxysql_config().Read_Global_Variables_from_configfile("pgsql"); + + proxysql_config().Read_Scheduler_from_configfile(); + proxysql_config().Read_Restapi_from_configfile(); + proxysql_config().Read_ProxySQL_Servers_from_configfile(); + __insert_or_replace_disktable_select_maintable(); + } + } + + /** + * @brief Inserts a default 'mysql_group_replication_hostgroup'. + * @details Uses the following defaults: + * - writer_hostgroup: 0 + * - reader_hostgroup: 1 + * - backup_writer_hostgroup: 2 + * - offline_hostgroup: 3 + * - max_writers: 9 + * - writer_is_also_reader: 0 -> Keep hostgroups separated + * - max_transactions_behind: 0 + * + * The number of writers in 'multi_primary_mode' wont be restricted, user should tune this value to + * convenience. By default 'max_writers' is set to 9, as is the current member limitation for Group + * Replication. + */ + const char insert_def_gr_hgs[] { + "INSERT INTO mysql_group_replication_hostgroups (" + "writer_hostgroup,backup_writer_hostgroup,reader_hostgroup,offline_hostgroup,active,max_writers," + "writer_is_also_reader" + ") VALUES (0,2,1,3,1,9,0)" + }; + vector servers_info {}; + + if (GloVars.global.gr_bootstrap_mode) { + // Check if user config is present for 'mysql_group_replication_hostgroups' + bool user_gr_hg_cnf = check_if_user_config(admindb, "SELECT COUNT(*) FROM mysql_group_replication_hostgroups"); + if (user_gr_hg_cnf == false) { + admindb->execute(insert_def_gr_hgs); + } else { + proxy_info("Bootstrap config, found previous user 'mysql_group_replication_hostgroups' config, reusing...\n"); + } + + // Stores current user config for 'mysql_hostgroup_attributes::servers_defaults' + map hgid_defs {}; + // Check if user config is present for 'mysql_hostgroup_attributes' + bool user_gr_hg_attrs_cnf = check_if_user_config(admindb, "SELECT COUNT(*) FROM mysql_hostgroup_attributes"); + int32_t have_ssl = 1; + + // SSL explicitly disabled by user for backend connections + if (GloVars.global.gr_bootstrap_ssl_mode) { + if (strcasecmp(GloVars.global.gr_bootstrap_ssl_mode, "DISABLED") == 0) { + have_ssl = 0; + } + } + + const int64_t DEF_GR_SRV_WEIGHT = 1; + const int64_t DEF_GR_SRV_MAX_CONNS = 512; + const int32_t DEF_GR_SRV_USE_SSL = have_ssl; + + // Update 'mysql_hostgroup_attributes' with sensible defaults for the new discovered instances + if (user_gr_hg_attrs_cnf == false) { + const nlohmann::json j_def_attrs { + { "weight", DEF_GR_SRV_WEIGHT }, + { "max_connections", DEF_GR_SRV_MAX_CONNS }, + { "use_ssl", DEF_GR_SRV_USE_SSL } + }; + const string str_def_attrs { j_def_attrs.dump() }; + const string insert_def_hg_attrs { + "INSERT INTO mysql_hostgroup_attributes (hostgroup_id, servers_defaults) VALUES" + " (0,'"+ str_def_attrs + "'), (1,'" + str_def_attrs + "')" + }; + admindb->execute(insert_def_hg_attrs.c_str()); + } else { + proxy_info("Bootstrap config, found previous user 'mysql_hostgroup_attributes' config, reusing...\n"); + hgid_defs = get_cur_hg_attrs(admindb); + } + + // Define the 'global defaults'. Either pure defaults, or user specified (argument). These values are + // supersede if previous user config is found for 'mysql_hostgroup_attributes::servers_defaults'. + srv_defs_t global_srvs_defs {}; + global_srvs_defs.weight = DEF_GR_SRV_WEIGHT; + global_srvs_defs.max_conns = DEF_GR_SRV_MAX_CONNS; + global_srvs_defs.use_ssl = DEF_GR_SRV_USE_SSL; + + servers_info = extract_boot_servers_info(bootstrap_info.servers); + auto full_srvs_info = build_srvs_info_with_defs(servers_info, hgid_defs, global_srvs_defs); + const string servers_insert { build_boot_servers_insert(full_srvs_info) }; + + admindb->execute("DELETE FROM mysql_servers"); + admindb->execute(servers_insert.c_str()); + + const string users_insert { build_boot_users_insert(bootstrap_info.users) }; + admindb->execute("DELETE FROM mysql_users"); + admindb->execute(users_insert.c_str()); + + // Make the configuration persistent + flush_GENERIC__from_to("mysql_servers", "memory_to_disk"); + flush_mysql_users__from_memory_to_disk(); + } + + // Admin variables 'bootstrap' modifications + if (GloVars.global.gr_bootstrap_mode) { + // TODO-NOTE: This MUST go away; 'admin-hash_passwords' will be deprecated + admindb->execute("UPDATE global_variables SET variable_value='false' WHERE variable_name='admin-hash_passwords'"); + } + flush_admin_variables___database_to_runtime(admindb,true); + + if (GloVars.global.gr_bootstrap_mode) { + flush_admin_variables___runtime_to_database(configdb, false, true, false); + } + + // MySQL variables / MySQL Query Rules 'bootstrap' modifications + if (GloVars.global.gr_bootstrap_mode && !servers_info.empty()) { + const uint64_t base_port { + GloVars.global.gr_bootstrap_conf_base_port == 0 ? 6446 : + GloVars.global.gr_bootstrap_conf_base_port + }; + const string bind_addr { + GloVars.global.gr_bootstrap_conf_bind_address == nullptr ? "0.0.0.0" : + string { GloVars.global.gr_bootstrap_conf_bind_address } + }; + const string s_rw_port { std::to_string(base_port) }; + const string s_ro_port { std::to_string(base_port + 1) }; + const string rw_addr { bind_addr + ":" + s_rw_port }; + const string ro_addr { bind_addr + ":" + s_ro_port }; + const string mysql_interfaces { rw_addr + ";" + ro_addr }; + + // Look for the default collation + const MARIADB_CHARSET_INFO* charset_info = proxysql_find_charset_nr(bootstrap_info.server_language); + const char* server_charset = charset_info == nullptr ? "" : charset_info->csname; + const char* server_collation = charset_info == nullptr ? "" : charset_info->name; + + // Holds user specified values, defaults, and implications of variables over others + const map bootstrap_mysql_vars { + { "mysql-server_version", bootstrap_info.server_version.c_str() }, + { "mysql-default_charset", server_charset }, + { "mysql-default_collation_connection", server_collation }, + { "mysql-interfaces", mysql_interfaces.c_str() }, + { "mysql-monitor_username", bootstrap_info.mon_user.c_str() }, + { "mysql-monitor_password", bootstrap_info.mon_pass.c_str() }, + { "mysql-have_ssl", "true" }, + { "mysql-ssl_p2s_ca", GloVars.global.gr_bootstrap_ssl_ca }, + { "mysql-ssl_p2s_capath", GloVars.global.gr_bootstrap_ssl_capath }, + { "mysql-ssl_p2s_cert", GloVars.global.gr_bootstrap_ssl_cert }, + { "mysql-ssl_p2s_cipher", GloVars.global.gr_bootstrap_ssl_cipher }, + { "mysql-ssl_p2s_crl", GloVars.global.gr_bootstrap_ssl_crl }, + { "mysql-ssl_p2s_crlpath", GloVars.global.gr_bootstrap_ssl_crlpath }, + { "mysql-ssl_p2s_key", GloVars.global.gr_bootstrap_ssl_key } + }; + + for (const pair& p_var_val : bootstrap_mysql_vars) { + if (p_var_val.second != nullptr) { + const string& name { p_var_val.first }; + const string& value { p_var_val.second }; + const string update_mysql_var { + "UPDATE global_variables SET variable_value='" + value + "' WHERE variable_name='" + name + "'" + }; + + admindb->execute(update_mysql_var.c_str()); + } + } + + // MySQL Query Rules - Port based RW split + { + // TODO: This should be able to contain in the future Unix socket based rules + const string insert_rw_split_rules { + "INSERT INTO mysql_query_rules (rule_id,active,proxy_port,destination_hostgroup,apply) VALUES " + " (0,1," + s_rw_port + ",0,1), (1,1," + s_ro_port + ",1,1)" + }; + + // Preserve previous user config targeting hostgroups 0/1 + bool user_qr_cnf = check_if_user_config(admindb, "SELECT COUNT(*) FROM mysql_query_rules"); + if (user_qr_cnf == false) { + admindb->execute(insert_rw_split_rules.c_str()); + } else { + proxy_info("Bootstrap config, found previous user 'mysql_query_rules' config, reusing...\n"); + } + + flush_GENERIC__from_to("mysql_query_rules", "memory_to_disk"); + } + + // Store the 'bootstrap_variables' + if (bootstrap_info.rand_gen_user) { + configdb->execute(ADMIN_SQLITE_TABLE_BOOTSTRAP_VARIABLES); + + const string insert_bootstrap_user { + "INSERT INTO bootstrap_variables (variable_name,variable_value) VALUES" + " ('bootstrap_username','" + string { bootstrap_info.mon_user } + "')" + }; + const string insert_bootstrap_pass { + "INSERT INTO bootstrap_variables (variable_name,variable_value) VALUES" + " ('bootstrap_password','" + string { bootstrap_info.mon_pass } + "')" + }; + + configdb->execute("DELETE FROM bootstrap_variables WHERE variable_name='bootstrap_username'"); + configdb->execute(insert_bootstrap_user.c_str()); + configdb->execute("DELETE FROM bootstrap_variables WHERE variable_name='bootstrap_password'"); + configdb->execute(insert_bootstrap_pass.c_str()); + } + } + flush_mysql_variables___database_to_runtime(admindb,true); + if (GloVars.global.gr_bootstrap_mode) { + flush_mysql_variables___runtime_to_database(configdb, false, true, false); + } + flush_pgsql_variables___database_to_runtime(admindb, true); +#ifdef PROXYSQLCLICKHOUSE + flush_clickhouse_variables___database_to_runtime(admindb,true); +#endif /* PROXYSQLCLICKHOUSE */ + flush_sqliteserver_variables___database_to_runtime(admindb,true); + + if (GloVars.__cmd_proxysql_admin_socket) { + set_variable((char *)"mysql_ifaces",GloVars.__cmd_proxysql_admin_socket); + } + + S_amll.update_ifaces(variables.mysql_ifaces, &S_amll.ifaces_mysql); + S_amll.update_ifaces(variables.pgsql_ifaces, &S_amll.ifaces_pgsql); + S_amll.update_ifaces(variables.telnet_admin_ifaces, &S_amll.ifaces_telnet_admin); + S_amll.update_ifaces(variables.telnet_stats_ifaces, &S_amll.ifaces_telnet_stats); + + + +// pthread_t admin_thr; + struct _main_args *arg=(struct _main_args *)malloc(sizeof(struct _main_args)); + arg->nfds=main_poll_nfds; + arg->fds=main_poll_fds; + arg->shutdown=&main_shutdown; + arg->callback_func=main_callback_func; + if (pthread_create(&admin_thr, &attr, admin_main_loop, (void *)arg) !=0 ) { + perror("Thread creation"); + exit(EXIT_FAILURE); + } + do { usleep(50); } while (__sync_fetch_and_sub(&admin_load_main_,0)==0); + admin_load_main_=0; + + // Register the global prometheus registry in the 'serial_exposer' + if (registered_prometheus_collectable == false) { + this->serial_exposer.RegisterCollectable(GloVars.prometheus_registry); + registered_prometheus_collectable = true; + } + +#ifdef DEBUG + std::cerr << "Admin initialized in "; +#endif +return true; +}; diff --git a/lib/Admin_FlushVariables.cpp b/lib/Admin_FlushVariables.cpp new file mode 100644 index 000000000..183777308 --- /dev/null +++ b/lib/Admin_FlushVariables.cpp @@ -0,0 +1,1174 @@ +#include "../deps/json/json.hpp" +using json = nlohmann::json; +#define PROXYJSON + +#include // std::cout +#include // std::stringstream +#include +#include // std::sort +#include +#include // std::vector +#include +#include "prometheus/exposer.h" +#include "prometheus/counter.h" +#include "openssl/ssl.h" +#include "openssl/err.h" + +#include "Base_Thread.h" + +#include "MySQL_HostGroups_Manager.h" +#include "PgSQL_HostGroups_Manager.h" +#include "mysql.h" +#include "proxysql_admin.h" +#include "re2/re2.h" +#include "re2/regexp.h" +#include "proxysql.h" +#include "proxysql_config.h" +#include "proxysql_restapi.h" +#include "proxysql_utils.h" +#include "prometheus_helpers.h" +#include "cpp.h" + +#include "MySQL_Data_Stream.h" +#include "PgSQL_Data_Stream.h" +#include "query_processor.h" +#include "ProxySQL_HTTP_Server.hpp" // HTTP server +#include "MySQL_Authentication.hpp" +#include "PgSQL_Authentication.h" +#include "MySQL_LDAP_Authentication.hpp" +#include "MySQL_PreparedStatement.h" +#include "ProxySQL_Cluster.hpp" +#include "ProxySQL_Statistics.hpp" +#include "MySQL_Logger.hpp" +#include "PgSQL_Logger.hpp" +#include "SQLite3_Server.h" +#include "Web_Interface.hpp" +#include "Client_Session.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef SPOOKYV2 +#include "SpookyV2.h" +#define SPOOKYV2 +#endif + +#include +#include + +#include "platform.h" +#include "microhttpd.h" + +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__)) && defined(__linux) +// currently only support x86-32, x86-64, ARM, and MIPS on Linux +#include "coredumper/coredumper.h" +#endif + +#include + +#include "PgSQL_Protocol.h" +//#include "usual/time.h" + +using std::string; +using std::unique_ptr; + +#ifdef WITHGCOV +extern "C" void __gcov_dump(); +extern "C" void __gcov_reset(); +#endif + + +#ifdef DEBUG +//#define BENCHMARK_FASTROUTING_LOAD +#endif // DEBUG + +//#define MYSQL_THREAD_IMPLEMENTATION + +#define SELECT_VERSION_COMMENT "select @@version_comment limit 1" +#define SELECT_VERSION_COMMENT_LEN 32 +#define SELECT_DB_USER "select DATABASE(), USER() limit 1" +#define SELECT_DB_USER_LEN 33 +#define SELECT_CHARSET_VARIOUS "select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1" +#define SELECT_CHARSET_VARIOUS_LEN 115 + +#define READ_ONLY_OFF "\x01\x00\x00\x01\x02\x23\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x0d\x56\x61\x72\x69\x61\x62\x6c\x65\x5f\x6e\x61\x6d\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x1b\x00\x00\x03\x03\x64\x65\x66\x00\x00\x00\x05\x56\x61\x6c\x75\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x05\x00\x00\x04\xfe\x00\x00\x02\x00\x0e\x00\x00\x05\x09\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x03\x4f\x46\x46\x05\x00\x00\x06\xfe\x00\x00\x02\x00" +#define READ_ONLY_ON "\x01\x00\x00\x01\x02\x23\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x0d\x56\x61\x72\x69\x61\x62\x6c\x65\x5f\x6e\x61\x6d\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x1b\x00\x00\x03\x03\x64\x65\x66\x00\x00\x00\x05\x56\x61\x6c\x75\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x05\x00\x00\x04\xfe\x00\x00\x02\x00\x0d\x00\x00\x05\x09\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x02\x4f\x4e\x05\x00\x00\x06\xfe\x00\x00\x02\x00" + +#define READ_ONLY_0 "\x01\x00\x00\x01\x01\x28\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x12\x40\x40\x67\x6c\x6f\x62\x61\x6c\x2e\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x00\x0c\x3f\x00\x01\x00\x00\x00\x08\x80\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x02\x00\x00\x04\x01\x30\x05\x00\x00\x05\xfe\x00\x00\x02\x00" + +#define READ_ONLY_1 "\x01\x00\x00\x01\x01\x28\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x12\x40\x40\x67\x6c\x6f\x62\x61\x6c\x2e\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x00\x0c\x3f\x00\x01\x00\x00\x00\x08\x80\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x02\x00\x00\x04\x01\x31\x05\x00\x00\x05\xfe\x00\x00\x02\x00" + +extern struct MHD_Daemon *Admin_HTTP_Server; + +extern ProxySQL_Statistics *GloProxyStats; + +int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char **msg); + +extern char *ssl_key_fp; +extern char *ssl_cert_fp; +extern char *ssl_ca_fp; + +// ProxySQL_Admin shared variables +extern int admin___web_verbosity; +extern char * proxysql_version; + +#include "proxysql_find_charset.h" + +extern Query_Cache *GloQC; +extern MySQL_Authentication *GloMyAuth; +extern PgSQL_Authentication *GloPgAuth; +extern MySQL_LDAP_Authentication *GloMyLdapAuth; +extern ProxySQL_Admin *GloAdmin; +extern Query_Processor *GloQPro; +extern MySQL_Threads_Handler *GloMTH; +extern MySQL_Logger *GloMyLogger; +extern PgSQL_Logger* GloPgSQL_Logger; +extern MySQL_STMT_Manager_v14 *GloMyStmt; +extern MySQL_Monitor *GloMyMon; +extern PgSQL_Threads_Handler* GloPTH; + +extern void (*flush_logs_function)(); + +extern Web_Interface *GloWebInterface; + +extern ProxySQL_Cluster *GloProxyCluster; +#ifdef PROXYSQLCLICKHOUSE +extern ClickHouse_Authentication *GloClickHouseAuth; +extern ClickHouse_Server *GloClickHouseServer; +#endif /* PROXYSQLCLICKHOUSE */ + +extern SQLite3_Server *GloSQLite3Server; + +extern char * binary_sha1; + +extern int ProxySQL_create_or_load_TLS(bool bootstrap, std::string& msg); + +bool ProxySQL_Admin::flush_GENERIC_variables__retrieve__database_to_runtime(const std::string& modname, char* &error, int& cols, int& affected_rows, SQLite3_result* &resultset) { + string q = "SELECT substr(variable_name," + to_string(modname.length()+2) + ") vn, variable_value FROM global_variables WHERE variable_name LIKE '" + modname + "-%'"; + admindb->execute_statement(q.c_str(), &error , &cols , &affected_rows , &resultset); + if (error) { + proxy_error("Error on %s : %s\n", q.c_str(), error); + free(error); + return false; + } + return true; +} + +void ProxySQL_Admin::flush_GENERIC_variables__process__database_to_runtime( + const string& modname, SQLite3DB *db, SQLite3_result* resultset, + const bool& lock, const bool& replace, + const std::unordered_set& variables_read_only, + const std::unordered_set& variables_to_delete_silently, + const std::unordered_set& variables_deprecated, + const std::unordered_set& variables_special_values, + std::function special_variable_action +) { + for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { + SQLite3_row *r=*it; + bool rc = false; + if (modname == "admin") { + rc = set_variable(r->fields[0],r->fields[1], lock); + } else if (modname == "mysql") { + rc = GloMTH->set_variable(r->fields[0],r->fields[1]); + } else if (modname == "sqliteserver") { + rc = GloSQLite3Server->set_variable(r->fields[0],r->fields[1]); +#ifdef PROXYSQLCLICKHOUSE + } else if (modname == "clickhouse") { + rc = GloClickHouseServer->set_variable(r->fields[0],r->fields[1]); +#endif // PROXYSQLCLICKHOUSE + } else if (modname == "ldap") { + rc = GloMyLdapAuth->set_variable(r->fields[0],r->fields[1]); + } + const string v = string(r->fields[0]); + if (rc==false) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); + if (replace) { + char *val = NULL; + if (modname == "admin") { + val = get_variable(r->fields[0]); + } else if (modname == "mysql") { + val = GloMTH->get_variable(r->fields[0]); + } else if (modname == "sqliteserver") { + val = GloSQLite3Server->get_variable(r->fields[0]); +#ifdef PROXYSQLCLICKHOUSE + } else if (modname == "clickhouse") { + val = GloClickHouseServer->get_variable(r->fields[0]); +#endif // PROXYSQLCLICKHOUSE + } else if (modname == "ldap") { + val = GloMyLdapAuth->get_variable(r->fields[0]); + } + char q[1000]; + if (val) { + if (variables_read_only.count(v) > 0) { + proxy_warning("Impossible to set read-only variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],r->fields[1], val); + } else { + proxy_warning("Impossible to set variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],r->fields[1], val); + } + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"%s-%s\",\"%s\")", modname.c_str(), r->fields[0],val); + db->execute(q); + free(val); + } else { + if (variables_to_delete_silently.count(v) > 0) { + sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"%s-%s\"", modname.c_str(), r->fields[0]); + db->execute(q); + } else if (variables_deprecated.count(v) > 0) { + proxy_error("Global variable %s-%s is deprecated.\n", modname.c_str(), r->fields[0]); + sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"%s-%s\"", modname.c_str(), r->fields[0]); + db->execute(q); + } else { + proxy_warning("Impossible to set not existing variable %s with value \"%s\". Deleting. If the variable name is correct, this version doesn't support it\n", r->fields[0],r->fields[1]); + } + sprintf(q,"DELETE FROM global_variables WHERE variable_name=\"%s-%s\"", modname.c_str(), r->fields[0]); + db->execute(q); + } + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); + if (variables_special_values.count(v) > 0) { + if (special_variable_action != nullptr) { + special_variable_action(v, r->fields[1], db); + } + } + } + } +} + +void ProxySQL_Admin::flush_admin_variables___database_to_runtime( + SQLite3DB *db, bool replace, const string& checksum, const time_t epoch, bool lock +) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ADMIN variables. Replace:%d\n", replace); + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + if (flush_GENERIC_variables__retrieve__database_to_runtime("admin", error, cols, affected_rows, resultset) == true) { + wrlock(); + flush_GENERIC_variables__process__database_to_runtime("admin", db, resultset, lock, replace, {"version"}, {"debug"}, {}, {}); + //commit(); NOT IMPLEMENTED + + // Checksums are always generated - 'admin-checksum_*' deprecated + + { + // generate checksum for cluster + pthread_mutex_lock(&GloVars.checksum_mutex); + flush_admin_variables___runtime_to_database(admindb, false, false, false, true); + flush_GENERIC_variables__checksum__database_to_runtime("admin", checksum, epoch); + pthread_mutex_unlock(&GloVars.checksum_mutex); + } + wrunlock(); + { + load_http_server(); + load_restapi_server(); + // Update the admin variable for 'web_verbosity' + admin___web_verbosity = variables.web_verbosity; + } + } + if (resultset) delete resultset; +} + +void ProxySQL_Admin::flush_pgsql_variables___runtime_to_database(SQLite3DB* db, bool replace, bool del, bool onlyifempty, bool runtime, bool use_lock) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing PgSQL variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); + if (onlyifempty) { + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = NULL; + char* q = (char*)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'pgsql-%'"; + db->execute_statement(q, &error, &cols, &affected_rows, &resultset); + int matching_rows = 0; + if (error) { + proxy_error("Error on %s : %s\n", q, error); + return; + } + else { + for (std::vector::iterator it = resultset->rows.begin(); it != resultset->rows.end(); ++it) { + SQLite3_row* r = *it; + matching_rows += atoi(r->fields[0]); + } + } + if (resultset) delete resultset; + if (matching_rows) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has PgSQL variables - skipping\n"); + return; + } + } + if (del) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting PgSQL variables from global_variables\n"); + db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'pgsql-%'"); + } + static char* a; + static char* b; + if (replace) { + a = (char*)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(?1, ?2)"; + } + else { + a = (char*)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(?1, ?2)"; + } + int rc; + sqlite3_stmt* statement1 = NULL; + sqlite3_stmt* statement2 = NULL; + //sqlite3 *mydb3=db->get_db(); + //rc=(*proxy_sqlite3_prepare_v2)(mydb3, a, -1, &statement1, 0); + rc = db->prepare_v2(a, &statement1); + ASSERT_SQLITE_OK(rc, db); + if (runtime) { + db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'pgsql-%'"); + b = (char*)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(?1, ?2)"; + //rc=(*proxy_sqlite3_prepare_v2)(mydb3, b, -1, &statement2, 0); + rc = db->prepare_v2(b, &statement2); + ASSERT_SQLITE_OK(rc, db); + } + if (use_lock) { + GloPTH->wrlock(); + db->execute("BEGIN"); + } + char** varnames = GloPTH->get_variables_list(); + for (int i = 0; varnames[i]; i++) { + char* val = GloPTH->get_variable(varnames[i]); + char* qualified_name = (char*)malloc(strlen(varnames[i]) + 12); + sprintf(qualified_name, "pgsql-%s", varnames[i]); + rc = (*proxy_sqlite3_bind_text)(statement1, 1, qualified_name, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + rc = (*proxy_sqlite3_bind_text)(statement1, 2, (val ? val : (char*)""), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + SAFE_SQLITE3_STEP2(statement1); + rc = (*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, db); + rc = (*proxy_sqlite3_reset)(statement1); ASSERT_SQLITE_OK(rc, db); + if (runtime) { + rc = (*proxy_sqlite3_bind_text)(statement2, 1, qualified_name, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + rc = (*proxy_sqlite3_bind_text)(statement2, 2, (val ? val : (char*)""), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + SAFE_SQLITE3_STEP2(statement2); + rc = (*proxy_sqlite3_clear_bindings)(statement2); ASSERT_SQLITE_OK(rc, db); + rc = (*proxy_sqlite3_reset)(statement2); ASSERT_SQLITE_OK(rc, db); + } + if (val) + free(val); + free(qualified_name); + } + if (use_lock) { + db->execute("COMMIT"); + GloPTH->wrunlock(); + } + (*proxy_sqlite3_finalize)(statement1); + if (runtime) + (*proxy_sqlite3_finalize)(statement2); + for (int i = 0; varnames[i]; i++) { + free(varnames[i]); + } + free(varnames); +} + +void ProxySQL_Admin::flush_GENERIC_variables__checksum__database_to_runtime(const string& modname, const string& checksum, const time_t epoch) { + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + std::string q; + q="SELECT variable_name, variable_value FROM runtime_global_variables WHERE variable_name LIKE '" + modname + "-\%' "; + if (modname == "mysql") { + q += " AND variable_name NOT IN ('mysql-threads')"; + if (GloVars.cluster_sync_interfaces == false) { + q += " AND variable_name NOT IN " + string(CLUSTER_SYNC_INTERFACES_MYSQL); + } + } else if (modname == "admin") { + if (GloVars.cluster_sync_interfaces == false) { + q += " AND variable_name NOT IN " + string(CLUSTER_SYNC_INTERFACES_ADMIN); + } + } + q += " ORDER BY variable_name"; + admindb->execute_statement(q.c_str(), &error , &cols , &affected_rows , &resultset); + uint64_t hash1 = resultset->raw_checksum(); + uint32_t d32[2]; + char buf[20]; + memcpy(&d32, &hash1, sizeof(hash1)); + sprintf(buf,"0x%0X%0X", d32[0], d32[1]); + ProxySQL_Checksum_Value *checkvar = NULL; + if (modname == "admin") { + checkvar = &GloVars.checksums_values.admin_variables; + } else if (modname == "mysql") { + checkvar = &GloVars.checksums_values.mysql_variables; + } else if (modname == "ldap") { + checkvar = &GloVars.checksums_values.ldap_variables; + } + assert(checkvar != NULL); + checkvar->set_checksum(buf); + checkvar->version++; + time_t t = time(NULL); + if (epoch != 0 && checksum != "" && checkvar->checksum == checksum) { + checkvar->epoch = epoch; + } else { + checkvar->epoch = t; + } + GloVars.epoch_version = t; + GloVars.generate_global_checksum(); + GloVars.checksums_values.updates_cnt++; + string modnameupper = modname; + for (char &c : modnameupper) { c = std::toupper(c); } + proxy_info( + "Computed checksum for 'LOAD %s VARIABLES TO RUNTIME' was '%s', with epoch '%llu'\n", + modnameupper.c_str(), checkvar->checksum, checkvar->epoch + ); + delete resultset; +} + +void ProxySQL_Admin::flush_mysql_variables___database_to_runtime(SQLite3DB *db, bool replace, const std::string& checksum, const time_t epoch) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing MySQL variables. Replace:%d\n", replace); + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + if (flush_GENERIC_variables__retrieve__database_to_runtime("mysql", error, cols, affected_rows, resultset) == true) { + GloMTH->wrlock(); + char * previous_default_charset = GloMTH->get_variable_string((char *)"default_charset"); + char * previous_default_collation_connection = GloMTH->get_variable_string((char *)"default_collation_connection"); + assert(previous_default_charset); + assert(previous_default_collation_connection); + flush_GENERIC_variables__process__database_to_runtime("mysql", db, resultset, false, replace, {}, {"session_debug"}, {"forward_autocommit"}, + {"default_collation_connection", "default_charset", "show_processlist_extended"}, + [](const std::string& varname, const char *varvalue, SQLite3DB* db) { + if (varname == "default_collation_connection" || varname == "default_charset") { + char *val=GloMTH->get_variable((char *)varname.c_str()); + if (val) { + if (strcmp(val,varvalue)) { + char q[1000]; + proxy_warning("Variable %s with value \"%s\" is being replaced with value \"%s\".\n", varname.c_str(), varvalue, val); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-%s\",\"%s\")", varname.c_str() ,val); + db->execute(q); + } + free(val); + } + } else if (varname == "show_processlist_extended") { + GloAdmin->variables.mysql_show_processlist_extended = atoi(varvalue); + } + } + ); + char q[1000]; + char * default_charset = GloMTH->get_variable_string((char *)"default_charset"); + char * default_collation_connection = GloMTH->get_variable_string((char *)"default_collation_connection"); + assert(default_charset); + assert(default_collation_connection); + MARIADB_CHARSET_INFO * ci = NULL; + ci = proxysql_find_charset_name(default_charset); + if (ci == NULL) { + // invalid charset + proxy_error("Found an incorrect value for mysql-default_charset: %s\n", default_charset); + // let's try to get a charset from collation connection + ci = proxysql_find_charset_collate(default_collation_connection); + if (ci == NULL) { + proxy_error("Found an incorrect value for mysql-default_collation_connection: %s\n", default_collation_connection); + const char *p = mysql_tracked_variables[SQL_CHARACTER_SET].default_value; + ci = proxysql_find_charset_name(p); + assert(ci); + proxy_info("Resetting mysql-default_charset to hardcoded default value: %s\n", ci->csname); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_charset\",\"%s\")", ci->csname); + db->execute(q); + GloMTH->set_variable((char *)"default_charset",ci->csname); + proxy_info("Resetting mysql-default_collation_connection to hardcoded default value: %s\n", ci->name); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_collation_connection\",\"%s\")", ci->name); + db->execute(q); + GloMTH->set_variable((char *)"default_collation_connection",ci->name); + } else { + proxy_info("Changing mysql-default_charset to %s using configured mysql-default_collation_connection %s\n", ci->csname, ci->name); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_charset\",\"%s\")", ci->csname); + db->execute(q); + GloMTH->set_variable((char *)"default_charset",ci->csname); + } + } else { + MARIADB_CHARSET_INFO * cic = NULL; + cic = proxysql_find_charset_collate(default_collation_connection); + if (cic == NULL) { + proxy_error("Found an incorrect value for mysql-default_collation_connection: %s\n", default_collation_connection); + proxy_info("Changing mysql-default_collation_connection to %s using configured mysql-default_charset: %s\n", ci->name, ci->csname); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_collation_connection\",\"%s\")", ci->name); + db->execute(q); + GloMTH->set_variable((char *)"default_collation_connection",ci->name); + } else { + if (strcmp(cic->csname,ci->csname)==0) { + // mysql-default_collation_connection and mysql-default_charset are compatible + } else { + proxy_error("Found incompatible values for mysql-default_charset (%s) and mysql-default_collation_connection (%s)\n", default_charset, default_collation_connection); + bool use_collation = true; + if (strcmp(default_charset, previous_default_charset)) { // charset changed + if (strcmp(default_collation_connection, previous_default_collation_connection)==0) { // collation didn't change + // the user has changed the charset but not the collation + // we use charset as source of truth + use_collation = false; + } + } + if (use_collation) { + proxy_info("Changing mysql-default_charset to %s using configured mysql-default_collation_connection %s\n", cic->csname, cic->name); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_charset\",\"%s\")", cic->csname); + db->execute(q); + GloMTH->set_variable((char *)"default_charset",cic->csname); + } else { + proxy_info("Changing mysql-default_collation_connection to %s using configured mysql-default_charset: %s\n", ci->name, ci->csname); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_collation_connection\",\"%s\")", ci->name); + db->execute(q); + GloMTH->set_variable((char *)"default_collation_connection",ci->name); + } + } + } + } + free(default_charset); + free(default_collation_connection); + free(previous_default_charset); + free(previous_default_collation_connection); + GloMTH->commit(); + GloMTH->wrunlock(); + + { + // NOTE: 'GloMTH->wrunlock()' should have been called before this point to avoid possible + // deadlocks. See issue #3847. + pthread_mutex_lock(&GloVars.checksum_mutex); + // generate checksum for cluster + flush_mysql_variables___runtime_to_database(admindb, false, false, false, true, true); + flush_GENERIC_variables__checksum__database_to_runtime("mysql", checksum, epoch); + pthread_mutex_unlock(&GloVars.checksum_mutex); + } + } + if (resultset) delete resultset; +} + +void ProxySQL_Admin::flush_sqliteserver_variables___database_to_runtime(SQLite3DB *db, bool replace) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing SQLiteServer variables. Replace:%d\n", replace); + if ( + (GloVars.global.sqlite3_server == false) + || + ( GloSQLite3Server == NULL ) + ) { + return; + } + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + if (flush_GENERIC_variables__retrieve__database_to_runtime("sqliteserver", error, cols, affected_rows, resultset) == true) { + GloSQLite3Server->wrlock(); + flush_GENERIC_variables__process__database_to_runtime("sqliteserver", db, resultset, false, replace, {}, {"session_debug"}, {}, {}); + //GloClickHouse->commit(); + GloSQLite3Server->wrunlock(); + } + if (resultset) delete resultset; +} + +void ProxySQL_Admin::flush_sqliteserver_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ClickHouse variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); + if (GloVars.global.sqlite3_server == false) { + return; + } + if (onlyifempty) { + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + char *q=(char *)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'sqliteserver-%'"; + db->execute_statement(q, &error , &cols , &affected_rows , &resultset); + int matching_rows=0; + if (error) { + proxy_error("Error on %s : %s\n", q, error); + return; + } else { + for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { + SQLite3_row *r=*it; + matching_rows+=atoi(r->fields[0]); + } + } + if (resultset) delete resultset; + if (matching_rows) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has ClickHouse variables - skipping\n"); + return; + } + } + if (del) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting ClickHouse variables from global_variables\n"); + db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'sqliteserver-%'"); + } + if (runtime) { + db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'sqliteserver-%'"); + } + char *a; + char *b=(char *)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(\"sqliteserver-%s\",\"%s\")"; + if (replace) { + a=(char *)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(\"sqliteserver-%s\",\"%s\")"; + } else { + a=(char *)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(\"sqliteserver-%s\",\"%s\")"; + } + int l=strlen(a)+200; + GloSQLite3Server->wrlock(); + char **varnames=GloSQLite3Server->get_variables_list(); + for (int i=0; varnames[i]; i++) { + char *val=GloSQLite3Server->get_variable(varnames[i]); + l+=( varnames[i] ? strlen(varnames[i]) : 6); + l+=( val ? strlen(val) : 6); + char *query=(char *)malloc(l); + sprintf(query, a, varnames[i], val); + if (runtime) { + db->execute(query); + sprintf(query, b, varnames[i], val); + } + db->execute(query); + if (val) + free(val); + free(query); + } + GloSQLite3Server->wrunlock(); + for (int i=0; varnames[i]; i++) { + free(varnames[i]); + } + free(varnames); +} + + +#ifdef PROXYSQLCLICKHOUSE +void ProxySQL_Admin::flush_clickhouse_variables___database_to_runtime(SQLite3DB *db, bool replace) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ClickHouse variables. Replace:%d\n", replace); + if ( + (GloVars.global.clickhouse_server == false) + || + ( GloClickHouseServer == NULL ) + ) { + return; + } + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + if (flush_GENERIC_variables__retrieve__database_to_runtime("clickhouse", error, cols, affected_rows, resultset) == true) { + GloClickHouseServer->wrlock(); + flush_GENERIC_variables__process__database_to_runtime("clickhouse", db, resultset, false, replace, {}, {"session_debug"}, {}, {}); + //GloClickHouse->commit(); + GloClickHouseServer->wrunlock(); + } + if (resultset) delete resultset; +} + +void ProxySQL_Admin::flush_clickhouse_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ClickHouse variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); + if ( + (GloVars.global.clickhouse_server == false) + || + ( GloClickHouseServer == NULL ) + ) { + return; + } + if (onlyifempty) { + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + char *q=(char *)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'clickhouse-%'"; + db->execute_statement(q, &error , &cols , &affected_rows , &resultset); + int matching_rows=0; + if (error) { + proxy_error("Error on %s : %s\n", q, error); + return; + } else { + for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { + SQLite3_row *r=*it; + matching_rows+=atoi(r->fields[0]); + } + } + if (resultset) delete resultset; + if (matching_rows) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has ClickHouse variables - skipping\n"); + return; + } + } + if (del) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting ClickHouse variables from global_variables\n"); + db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'clickhouse-%'"); + } + if (runtime) { + db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'clickhouse-%'"); + } + char *a; + char *b=(char *)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(\"clickhouse-%s\",\"%s\")"; + if (replace) { + a=(char *)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(\"clickhouse-%s\",\"%s\")"; + } else { + a=(char *)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(\"clickhouse-%s\",\"%s\")"; + } + int l=strlen(a)+200; + GloClickHouseServer->wrlock(); + char **varnames=GloClickHouseServer->get_variables_list(); + for (int i=0; varnames[i]; i++) { + char *val=GloClickHouseServer->get_variable(varnames[i]); + l+=( varnames[i] ? strlen(varnames[i]) : 6); + l+=( val ? strlen(val) : 6); + char *query=(char *)malloc(l); + sprintf(query, a, varnames[i], val); + if (runtime) { + db->execute(query); + sprintf(query, b, varnames[i], val); + } + db->execute(query); + if (val) + free(val); + free(query); + } + GloClickHouseServer->wrunlock(); + for (int i=0; varnames[i]; i++) { + free(varnames[i]); + } + free(varnames); +} +#endif /* PROXYSQLCLICKHOUSE */ + +void ProxySQL_Admin::flush_pgsql_variables___database_to_runtime(SQLite3DB* db, bool replace, const std::string& checksum, const time_t epoch) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing PgSQL variables. Replace:%d\n", replace); + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = NULL; + char* q = (char*)"SELECT substr(variable_name,7) vn, variable_value FROM global_variables WHERE variable_name LIKE 'pgsql-%'"; + admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + if (error) { + proxy_error("Error on %s : %s\n", q, error); + return; + } + else { + GloPTH->wrlock(); + char* previous_default_charset = GloPTH->get_variable_string((char*)"default_charset"); + char* previous_default_collation_connection = GloPTH->get_variable_string((char*)"default_collation_connection"); + assert(previous_default_charset); + assert(previous_default_collation_connection); + for (std::vector::iterator it = resultset->rows.begin(); it != resultset->rows.end(); ++it) { + SQLite3_row* r = *it; + const char* value = r->fields[1]; + bool rc = GloPTH->set_variable(r->fields[0], value); + if (rc == false) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0], value); + if (replace) { + char* val = GloPTH->get_variable(r->fields[0]); + char q[1000]; + if (val) { + if (strcmp(val, value)) { + proxy_warning("Impossible to set variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0], value, val); + sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-%s\",\"%s\")", r->fields[0], val); + db->execute(q); + } + free(val); + } + else { + if (strcmp(r->fields[0], (char*)"session_debug") == 0) { + sprintf(q, "DELETE FROM disk.global_variables WHERE variable_name=\"pgsql-%s\"", r->fields[0]); + db->execute(q); + } + else { + if (strcmp(r->fields[0], (char*)"forward_autocommit") == 0) { + if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) { + proxy_error("Global variable pgsql-forward_autocommit is deprecated. See issue #3253\n"); + } + sprintf(q, "DELETE FROM disk.global_variables WHERE variable_name=\"pgsql-%s\"", r->fields[0]); + db->execute(q); + } + else { + proxy_warning("Impossible to set not existing variable %s with value \"%s\". Deleting. If the variable name is correct, this version doesn't support it\n", r->fields[0], r->fields[1]); + } + } + sprintf(q, "DELETE FROM global_variables WHERE variable_name=\"pgsql-%s\"", r->fields[0]); + db->execute(q); + } + } + } + else { + if ( + (strcmp(r->fields[0], "default_collation_connection") == 0) + || (strcmp(r->fields[0], "default_charset") == 0) + ) { + char* val = GloPTH->get_variable(r->fields[0]); + char q[1000]; + if (val) { + if (strcmp(val, value)) { + proxy_warning("Variable %s with value \"%s\" is being replaced with value \"%s\".\n", r->fields[0], value, val); + sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-%s\",\"%s\")", r->fields[0], val); + db->execute(q); + } + free(val); + } + } + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Set variable %s with value \"%s\"\n", r->fields[0], value); + if (strcmp(r->fields[0], (char*)"show_processlist_extended") == 0) { + variables.pgsql_show_processlist_extended = atoi(value); + } + } + // } + } + + char q[1000]; + char* default_charset = GloPTH->get_variable_string((char*)"default_charset"); + char* default_collation_connection = GloPTH->get_variable_string((char*)"default_collation_connection"); + assert(default_charset); + assert(default_collation_connection); + MARIADB_CHARSET_INFO* ci = NULL; + ci = proxysql_find_charset_name(default_charset); + if (ci == NULL) { + // invalid charset + proxy_error("Found an incorrect value for pgsql-default_charset: %s\n", default_charset); + // let's try to get a charset from collation connection + ci = proxysql_find_charset_collate(default_collation_connection); + if (ci == NULL) { + proxy_error("Found an incorrect value for pgsql-default_collation_connection: %s\n", default_collation_connection); + const char* p = mysql_tracked_variables[SQL_CHARACTER_SET].default_value; + ci = proxysql_find_charset_name(p); + assert(ci); + proxy_info("Resetting pgsql-default_charset to hardcoded default value: %s\n", ci->csname); + sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_charset\",\"%s\")", ci->csname); + db->execute(q); + GloPTH->set_variable((char*)"default_charset", ci->csname); + proxy_info("Resetting pgsql-default_collation_connection to hardcoded default value: %s\n", ci->name); + sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_collation_connection\",\"%s\")", ci->name); + db->execute(q); + GloPTH->set_variable((char*)"default_collation_connection", ci->name); + } + else { + proxy_info("Changing pgsql-default_charset to %s using configured pgsql-default_collation_connection %s\n", ci->csname, ci->name); + sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_charset\",\"%s\")", ci->csname); + db->execute(q); + GloPTH->set_variable((char*)"default_charset", ci->csname); + } + } + else { + MARIADB_CHARSET_INFO* cic = NULL; + cic = proxysql_find_charset_collate(default_collation_connection); + if (cic == NULL) { + proxy_error("Found an incorrect value for pgsql-default_collation_connection: %s\n", default_collation_connection); + proxy_info("Changing pgsql-default_collation_connection to %s using configured pgsql-default_charset: %s\n", ci->name, ci->csname); + sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_collation_connection\",\"%s\")", ci->name); + db->execute(q); + GloPTH->set_variable((char*)"default_collation_connection", ci->name); + } + else { + if (strcmp(cic->csname, ci->csname) == 0) { + // pgsql-default_collation_connection and pgsql-default_charset are compatible + } + else { + proxy_error("Found incompatible values for pgsql-default_charset (%s) and pgsql-default_collation_connection (%s)\n", default_charset, default_collation_connection); + bool use_collation = true; + if (strcmp(default_charset, previous_default_charset)) { // charset changed + if (strcmp(default_collation_connection, previous_default_collation_connection) == 0) { // collation didn't change + // the user has changed the charset but not the collation + // we use charset as source of truth + use_collation = false; + } + } + if (use_collation) { + proxy_info("Changing pgsql-default_charset to %s using configured pgsql-default_collation_connection %s\n", cic->csname, cic->name); + sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_charset\",\"%s\")", cic->csname); + db->execute(q); + GloPTH->set_variable((char*)"default_charset", cic->csname); + } + else { + proxy_info("Changing pgsql-default_collation_connection to %s using configured pgsql-default_charset: %s\n", ci->name, ci->csname); + sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_collation_connection\",\"%s\")", ci->name); + db->execute(q); + GloPTH->set_variable((char*)"default_collation_connection", ci->name); + } + } + } + } + free(default_charset); + free(default_collation_connection); + free(previous_default_charset); + free(previous_default_collation_connection); + GloPTH->commit(); + GloPTH->wrunlock(); + + /* Checksums are always generated - 'admin-checksum_*' deprecated + { + // NOTE: 'GloPTH->wrunlock()' should have been called before this point to avoid possible + // deadlocks. See issue #3847. + pthread_mutex_lock(&GloVars.checksum_mutex); + // generate checksum for cluster + flush_mysql_variables___runtime_to_database(admindb, false, false, false, true, true); + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = NULL; + std::string q; + q = "SELECT variable_name, variable_value FROM runtime_global_variables WHERE variable_name LIKE 'mysql-\%' AND variable_name NOT IN ('mysql-threads')"; + if (GloVars.cluster_sync_interfaces == false) { + q += " AND variable_name NOT IN " + string(CLUSTER_SYNC_INTERFACES_MYSQL); + } + q += " ORDER BY variable_name"; + admindb->execute_statement(q.c_str(), &error, &cols, &affected_rows, &resultset); + uint64_t hash1 = resultset->raw_checksum(); + uint32_t d32[2]; + char buf[20]; + memcpy(&d32, &hash1, sizeof(hash1)); + sprintf(buf, "0x%0X%0X", d32[0], d32[1]); + GloVars.checksums_values.mysql_variables.set_checksum(buf); + GloVars.checksums_values.mysql_variables.version++; + time_t t = time(NULL); + if (epoch != 0 && checksum != "" && GloVars.checksums_values.mysql_variables.checksum == checksum) { + GloVars.checksums_values.mysql_variables.epoch = epoch; + } + else { + GloVars.checksums_values.mysql_variables.epoch = t; + } + GloVars.epoch_version = t; + GloVars.generate_global_checksum(); + GloVars.checksums_values.updates_cnt++; + pthread_mutex_unlock(&GloVars.checksum_mutex); + delete resultset; + } + proxy_info( + "Computed checksum for 'LOAD MYSQL VARIABLES TO RUNTIME' was '%s', with epoch '%llu'\n", + GloVars.checksums_values.mysql_variables.checksum, GloVars.checksums_values.mysql_variables.epoch + ); + */ + } + if (resultset) delete resultset; +} + +void ProxySQL_Admin::flush_mysql_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime, bool use_lock) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing MySQL variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); + if (onlyifempty) { + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + char *q=(char *)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'mysql-%'"; + db->execute_statement(q, &error , &cols , &affected_rows , &resultset); + int matching_rows=0; + if (error) { + proxy_error("Error on %s : %s\n", q, error); + return; + } else { + for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { + SQLite3_row *r=*it; + matching_rows+=atoi(r->fields[0]); + } + } + if (resultset) delete resultset; + if (matching_rows) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has MySQL variables - skipping\n"); + return; + } + } + if (del) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting MySQL variables from global_variables\n"); + db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'mysql-%'"); + } + static char *a; + static char *b; + if (replace) { + a=(char *)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(?1, ?2)"; + } else { + a=(char *)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(?1, ?2)"; + } + int rc; + sqlite3_stmt *statement1=NULL; + sqlite3_stmt *statement2=NULL; + + rc=db->prepare_v2(a, &statement1); + ASSERT_SQLITE_OK(rc, db); + if (runtime) { + db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'mysql-%'"); + b=(char *)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(?1, ?2)"; + + rc=db->prepare_v2(b, &statement2); + ASSERT_SQLITE_OK(rc, db); + } + if (use_lock) { + GloMTH->wrlock(); + db->execute("BEGIN"); + } + char **varnames=GloMTH->get_variables_list(); + for (int i=0; varnames[i]; i++) { + char *val=GloMTH->get_variable(varnames[i]); + char *qualified_name=(char *)malloc(strlen(varnames[i])+7); + sprintf(qualified_name, "mysql-%s", varnames[i]); + rc=(*proxy_sqlite3_bind_text)(statement1, 1, qualified_name, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + rc=(*proxy_sqlite3_bind_text)(statement1, 2, (val ? val : (char *)""), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + SAFE_SQLITE3_STEP2(statement1); + rc=(*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, db); + rc=(*proxy_sqlite3_reset)(statement1); ASSERT_SQLITE_OK(rc, db); + if (runtime) { + rc=(*proxy_sqlite3_bind_text)(statement2, 1, qualified_name, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + rc=(*proxy_sqlite3_bind_text)(statement2, 2, (val ? val : (char *)""), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + SAFE_SQLITE3_STEP2(statement2); + rc=(*proxy_sqlite3_clear_bindings)(statement2); ASSERT_SQLITE_OK(rc, db); + rc=(*proxy_sqlite3_reset)(statement2); ASSERT_SQLITE_OK(rc, db); + } + if (val) + free(val); + free(qualified_name); + } + if (use_lock) { + db->execute("COMMIT"); + GloMTH->wrunlock(); + } + (*proxy_sqlite3_finalize)(statement1); + if (runtime) + (*proxy_sqlite3_finalize)(statement2); + for (int i=0; varnames[i]; i++) { + free(varnames[i]); + } + free(varnames); +} + +void ProxySQL_Admin::flush_ldap_variables___database_to_runtime(SQLite3DB *db, bool replace, const std::string& checksum, const time_t epoch) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing LDAP variables. Replace:%d\n", replace); + if (GloMyLdapAuth == NULL) { + return; + } + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + if (flush_GENERIC_variables__retrieve__database_to_runtime("ldap", error, cols, affected_rows, resultset) == true) { + GloMyLdapAuth->wrlock(); + flush_GENERIC_variables__process__database_to_runtime("admin", db, resultset, false, replace, {}, {}, {}, {}); + GloMyLdapAuth->wrunlock(); + + // Checksums are always generated - 'admin-checksum_*' deprecated + { + pthread_mutex_lock(&GloVars.checksum_mutex); + // generate checksum for cluster + flush_ldap_variables___runtime_to_database(admindb, false, false, false, true); + flush_GENERIC_variables__checksum__database_to_runtime("ldap", checksum, epoch); + pthread_mutex_unlock(&GloVars.checksum_mutex); + } + } + if (resultset) delete resultset; +} + +void ProxySQL_Admin::flush_ldap_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing LDAP variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); + if (GloMyLdapAuth == NULL) { + return; + } + if (onlyifempty) { + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + char *q=(char *)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'ldap-%'"; + db->execute_statement(q, &error , &cols , &affected_rows , &resultset); + int matching_rows=0; + if (error) { + proxy_error("Error on %s : %s\n", q, error); + return; + } else { + for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { + SQLite3_row *r=*it; + matching_rows+=atoi(r->fields[0]); + } + } + if (resultset) delete resultset; + if (matching_rows) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has LDAP variables - skipping\n"); + return; + } + } + if (del) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting LDAP variables from global_variables\n"); + db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'ldap-%'"); + } + if (runtime) { + db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'ldap-%'"); + } + char *a; + char *b=(char *)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(\"ldap-%s\",\"%s\")"; + if (replace) { + a=(char *)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(\"ldap-%s\",\"%s\")"; + } else { + a=(char *)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(\"ldap-%s\",\"%s\")"; + } + int l=strlen(a)+200; + GloMyLdapAuth->wrlock(); + char **varnames=GloMyLdapAuth->get_variables_list(); + for (int i=0; varnames[i]; i++) { + char *val=GloMyLdapAuth->get_variable(varnames[i]); + l+=( varnames[i] ? strlen(varnames[i]) : 6); + l+=( val ? strlen(val) : 6); + char *query=(char *)malloc(l); + sprintf(query, a, varnames[i], val); + if (runtime) { + db->execute(query); + sprintf(query, b, varnames[i], val); + } + db->execute(query); + if (val) + free(val); + free(query); + } + GloMyLdapAuth->wrunlock(); + for (int i=0; varnames[i]; i++) { + free(varnames[i]); + } + free(varnames); +} + +void ProxySQL_Admin::flush_admin_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ADMIN variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); + if (onlyifempty) { + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + char *q=(char *)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'admin-%'"; + db->execute_statement(q, &error , &cols , &affected_rows , &resultset); + int matching_rows=0; + if (error) { + proxy_error("Error on %s : %s\n", q, error); + return; + } else { + for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { + SQLite3_row *r=*it; + matching_rows+=atoi(r->fields[0]); + } + } + if (resultset) delete resultset; + if (matching_rows) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has ADMIN variables - skipping\n"); + return; + } + } + if (del) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting ADMIN variables from global_variables\n"); + db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'admin-%'"); + } + if (runtime) { + db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'admin-%'"); + } + char *a; + char *b=(char *)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(\"admin-%s\",\"%s\")"; + if (replace) { + a=(char *)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(\"admin-%s\",\"%s\")"; + } else { + a=(char *)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(\"admin-%s\",\"%s\")"; + } + int l=strlen(a)+200; + + char **varnames=get_variables_list(); + for (int i=0; varnames[i]; i++) { + char *val=get_variable(varnames[i]); + l+=( varnames[i] ? strlen(varnames[i]) : 6); + l+=( val ? strlen(val) : 6); + char *query=(char *)malloc(l); + sprintf(query, a, varnames[i], val); + db->execute(query); + if (runtime) { + sprintf(query, b, varnames[i], val); + db->execute(query); + } + if (val) + free(val); + free(query); + } + for (int i=0; varnames[i]; i++) { + free(varnames[i]); + } + free(varnames); + +} diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp new file mode 100644 index 000000000..b00075383 --- /dev/null +++ b/lib/Admin_Handler.cpp @@ -0,0 +1,3658 @@ +#include "../deps/json/json.hpp" +using json = nlohmann::json; +#define PROXYJSON + +#include // std::cout +#include // std::stringstream +#include +#include // std::sort +#include +#include // std::vector +#include +#include "prometheus/exposer.h" +#include "prometheus/counter.h" +#include "openssl/ssl.h" +#include "openssl/err.h" + +#include "Base_Thread.h" + +#include "MySQL_HostGroups_Manager.h" +#include "PgSQL_HostGroups_Manager.h" +#include "mysql.h" +#include "proxysql_admin.h" +#include "re2/re2.h" +#include "re2/regexp.h" +#include "proxysql.h" +#include "proxysql_config.h" +#include "proxysql_restapi.h" +#include "proxysql_utils.h" +#include "prometheus_helpers.h" +#include "cpp.h" + +#include "MySQL_Data_Stream.h" +#include "PgSQL_Data_Stream.h" +#include "query_processor.h" +#include "ProxySQL_HTTP_Server.hpp" // HTTP server +#include "MySQL_Authentication.hpp" +#include "PgSQL_Authentication.h" +#include "MySQL_LDAP_Authentication.hpp" +#include "MySQL_PreparedStatement.h" +#include "ProxySQL_Cluster.hpp" +#include "ProxySQL_Statistics.hpp" +#include "MySQL_Logger.hpp" +#include "PgSQL_Logger.hpp" +#include "SQLite3_Server.h" +#include "Web_Interface.hpp" +#include "Client_Session.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef SPOOKYV2 +#include "SpookyV2.h" +#define SPOOKYV2 +#endif + +#include +#include + +#include "platform.h" +#include "microhttpd.h" + +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__)) && defined(__linux) +// currently only support x86-32, x86-64, ARM, and MIPS on Linux +#include "coredumper/coredumper.h" +#endif + +#include + +#include "PgSQL_Protocol.h" +//#include "usual/time.h" + +using std::string; +using std::unique_ptr; + +#ifdef WITHGCOV +extern "C" void __gcov_dump(); +extern "C" void __gcov_reset(); +#endif + + +#ifdef DEBUG +//#define BENCHMARK_FASTROUTING_LOAD +#endif // DEBUG + +//#define MYSQL_THREAD_IMPLEMENTATION + +#define SELECT_VERSION_COMMENT "select @@version_comment limit 1" +#define SELECT_VERSION_COMMENT_LEN 32 +#define SELECT_DB_USER "select DATABASE(), USER() limit 1" +#define SELECT_DB_USER_LEN 33 +#define SELECT_CHARSET_VARIOUS "select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1" +#define SELECT_CHARSET_VARIOUS_LEN 115 + +#define READ_ONLY_OFF "\x01\x00\x00\x01\x02\x23\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x0d\x56\x61\x72\x69\x61\x62\x6c\x65\x5f\x6e\x61\x6d\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x1b\x00\x00\x03\x03\x64\x65\x66\x00\x00\x00\x05\x56\x61\x6c\x75\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x05\x00\x00\x04\xfe\x00\x00\x02\x00\x0e\x00\x00\x05\x09\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x03\x4f\x46\x46\x05\x00\x00\x06\xfe\x00\x00\x02\x00" +#define READ_ONLY_ON "\x01\x00\x00\x01\x02\x23\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x0d\x56\x61\x72\x69\x61\x62\x6c\x65\x5f\x6e\x61\x6d\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x1b\x00\x00\x03\x03\x64\x65\x66\x00\x00\x00\x05\x56\x61\x6c\x75\x65\x00\x0c\x21\x00\x0f\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x05\x00\x00\x04\xfe\x00\x00\x02\x00\x0d\x00\x00\x05\x09\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x02\x4f\x4e\x05\x00\x00\x06\xfe\x00\x00\x02\x00" + +#define READ_ONLY_0 "\x01\x00\x00\x01\x01\x28\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x12\x40\x40\x67\x6c\x6f\x62\x61\x6c\x2e\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x00\x0c\x3f\x00\x01\x00\x00\x00\x08\x80\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x02\x00\x00\x04\x01\x30\x05\x00\x00\x05\xfe\x00\x00\x02\x00" + +#define READ_ONLY_1 "\x01\x00\x00\x01\x01\x28\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x12\x40\x40\x67\x6c\x6f\x62\x61\x6c\x2e\x72\x65\x61\x64\x5f\x6f\x6e\x6c\x79\x00\x0c\x3f\x00\x01\x00\x00\x00\x08\x80\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x02\x00\x00\x04\x01\x31\x05\x00\x00\x05\xfe\x00\x00\x02\x00" + +extern struct MHD_Daemon *Admin_HTTP_Server; + +extern ProxySQL_Statistics *GloProxyStats; + +int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char **msg); + +extern char *ssl_key_fp; +extern char *ssl_cert_fp; +extern char *ssl_ca_fp; + +// ProxySQL_Admin shared variables +extern int admin___web_verbosity; +extern char * proxysql_version; + +#include "proxysql_find_charset.h" + +extern int admin_load_main_; +extern bool admin_nostart_; + +extern int __admin_refresh_interval; + +extern bool admin_proxysql_mysql_paused; +extern bool admin_proxysql_pgsql_paused; +extern int admin_old_wait_timeout; + + +extern Query_Cache *GloQC; +extern MySQL_Authentication *GloMyAuth; +extern PgSQL_Authentication *GloPgAuth; +extern MySQL_LDAP_Authentication *GloMyLdapAuth; +extern ProxySQL_Admin *GloAdmin; +extern Query_Processor *GloQPro; +extern MySQL_Threads_Handler *GloMTH; +extern MySQL_Logger *GloMyLogger; +extern PgSQL_Logger* GloPgSQL_Logger; +extern MySQL_STMT_Manager_v14 *GloMyStmt; +extern MySQL_Monitor *GloMyMon; +extern PgSQL_Threads_Handler* GloPTH; + +extern void (*flush_logs_function)(); + +extern Web_Interface *GloWebInterface; + +extern ProxySQL_Cluster *GloProxyCluster; +#ifdef PROXYSQLCLICKHOUSE +extern ClickHouse_Authentication *GloClickHouseAuth; +extern ClickHouse_Server *GloClickHouseServer; +#endif /* PROXYSQLCLICKHOUSE */ + +extern SQLite3_Server *GloSQLite3Server; + +extern char * binary_sha1; + +extern int ProxySQL_create_or_load_TLS(bool bootstrap, std::string& msg); + + +#define PANIC(msg) { perror(msg); exit(EXIT_FAILURE); } + +extern pthread_mutex_t users_mutex; + +extern ProxySQL_Admin *SPA; + +const std::vector LOAD_ADMIN_VARIABLES_TO_MEMORY = { + "LOAD ADMIN VARIABLES TO MEMORY" , + "LOAD ADMIN VARIABLES TO MEM" , + "LOAD ADMIN VARIABLES FROM DISK" }; + +const std::vector SAVE_ADMIN_VARIABLES_FROM_MEMORY = { + "SAVE ADMIN VARIABLES FROM MEMORY" , + "SAVE ADMIN VARIABLES FROM MEM" , + "SAVE ADMIN VARIABLES TO DISK" }; + +const std::vector LOAD_ADMIN_VARIABLES_FROM_MEMORY = { + "LOAD ADMIN VARIABLES FROM MEMORY" , + "LOAD ADMIN VARIABLES FROM MEM" , + "LOAD ADMIN VARIABLES TO RUNTIME" , + "LOAD ADMIN VARIABLES TO RUN" }; + +const std::vector SAVE_ADMIN_VARIABLES_TO_MEMORY = { + "SAVE ADMIN VARIABLES TO MEMORY" , + "SAVE ADMIN VARIABLES TO MEM" , + "SAVE ADMIN VARIABLES FROM RUNTIME" , + "SAVE ADMIN VARIABLES FROM RUN" }; + +const std::vector LOAD_MYSQL_SERVERS_FROM_MEMORY = { + "LOAD MYSQL SERVERS FROM MEMORY" , + "LOAD MYSQL SERVERS FROM MEM" , + "LOAD MYSQL SERVERS TO RUNTIME" , + "LOAD MYSQL SERVERS TO RUN" }; + +const std::vector SAVE_MYSQL_SERVERS_TO_MEMORY = { + "SAVE MYSQL SERVERS TO MEMORY" , + "SAVE MYSQL SERVERS TO MEM" , + "SAVE MYSQL SERVERS FROM RUNTIME" , + "SAVE MYSQL SERVERS FROM RUN" }; + +const std::vector LOAD_MYSQL_USERS_FROM_MEMORY = { + "LOAD MYSQL USERS FROM MEMORY" , + "LOAD MYSQL USERS FROM MEM" , + "LOAD MYSQL USERS TO RUNTIME" , + "LOAD MYSQL USERS TO RUN" }; + +const std::vector SAVE_MYSQL_USERS_TO_MEMORY = { + "SAVE MYSQL USERS TO MEMORY" , + "SAVE MYSQL USERS TO MEM" , + "SAVE MYSQL USERS FROM RUNTIME" , + "SAVE MYSQL USERS FROM RUN" }; + +const std::vector LOAD_MYSQL_VARIABLES_FROM_MEMORY = { + "LOAD MYSQL VARIABLES FROM MEMORY" , + "LOAD MYSQL VARIABLES FROM MEM" , + "LOAD MYSQL VARIABLES TO RUNTIME" , + "LOAD MYSQL VARIABLES TO RUN" }; + +const std::vector SAVE_MYSQL_VARIABLES_TO_MEMORY = { + "SAVE MYSQL VARIABLES TO MEMORY" , + "SAVE MYSQL VARIABLES TO MEM" , + "SAVE MYSQL VARIABLES FROM RUNTIME" , + "SAVE MYSQL VARIABLES FROM RUN" }; + +// PgSQL +const std::vector LOAD_PGSQL_SERVERS_FROM_MEMORY = { + "LOAD PGSQL SERVERS FROM MEMORY" , + "LOAD PGSQL SERVERS FROM MEM" , + "LOAD PGSQL SERVERS TO RUNTIME" , + "LOAD PGSQL SERVERS TO RUN" }; + +const std::vector SAVE_PGSQL_SERVERS_TO_MEMORY = { + "SAVE PGSQL SERVERS TO MEMORY" , + "SAVE PGSQL SERVERS TO MEM" , + "SAVE PGSQL SERVERS FROM RUNTIME" , + "SAVE PGSQL SERVERS FROM RUN" }; + +const std::vector LOAD_PGSQL_USERS_FROM_MEMORY = { + "LOAD PGSQL USERS FROM MEMORY" , + "LOAD PGSQL USERS FROM MEM" , + "LOAD PGSQL USERS TO RUNTIME" , + "LOAD PGSQL USERS TO RUN" }; + +const std::vector SAVE_PGSQL_USERS_TO_MEMORY = { + "SAVE PGSQL USERS TO MEMORY" , + "SAVE PGSQL USERS TO MEM" , + "SAVE PGSQL USERS FROM RUNTIME" , + "SAVE PGSQL USERS FROM RUN" }; + +const std::vector LOAD_PGSQL_VARIABLES_FROM_MEMORY = { + "LOAD PGSQL VARIABLES FROM MEMORY" , + "LOAD PGSQL VARIABLES FROM MEM" , + "LOAD PGSQL VARIABLES TO RUNTIME" , + "LOAD PGSQL VARIABLES TO RUN" }; + +const std::vector SAVE_PGSQL_VARIABLES_TO_MEMORY = { + "SAVE PGSQL VARIABLES TO MEMORY" , + "SAVE PGSQL VARIABLES TO MEM" , + "SAVE PGSQL VARIABLES FROM RUNTIME" , + "SAVE PGSQL VARIABLES FROM RUN" }; +// +const std::vector LOAD_COREDUMP_FROM_MEMORY = { + "LOAD COREDUMP FROM MEMORY" , + "LOAD COREDUMP FROM MEM" , + "LOAD COREDUMP TO RUNTIME" , + "LOAD COREDUMP TO RUN" }; + +extern unordered_map, vector>> load_save_disk_commands; + +bool is_admin_command_or_alias(const std::vector& cmds, char *query_no_space, int query_no_space_length) { + for (std::vector::const_iterator it=cmds.begin(); it!=cmds.end(); ++it) { + if ((unsigned int)query_no_space_length==it->length() && !strncasecmp(it->c_str(), query_no_space, query_no_space_length)) { + proxy_info("Received %s command\n", query_no_space); + return true; + } + } + return false; +} + + +template +bool FlushCommandWrapper(Client_Session sess, const std::vector& cmds, char *query_no_space, int query_no_space_length, const string& name, const string& direction) { + if ( is_admin_command_or_alias(cmds, query_no_space, query_no_space_length) ) { + ProxySQL_Admin *SPA = GloAdmin; + SPA->flush_GENERIC__from_to(name, direction); +#ifdef DEBUG + string msg = "Loaded " + name + " "; + if (direction == "memory_to_disk") + msg += "from MEMORY to DISK"; + else if (direction == "disk_to_memory") + msg += "from DISK to MEMORY"; + else + assert(0); + msg += "\n"; + proxy_debug(PROXY_DEBUG_ADMIN, 4, "%s", msg.c_str()); +#endif // DEBUG + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return true; + } + return false; +} + +template +bool FlushCommandWrapper(Client_Session sess, const string& modname, char *query_no_space, int query_no_space_length) { + assert(load_save_disk_commands.find(modname) != load_save_disk_commands.end()); + tuple, vector>& t = load_save_disk_commands[modname]; + if (FlushCommandWrapper(sess, get<1>(t), query_no_space, query_no_space_length, modname, "disk_to_memory") == true) + return true; + if (FlushCommandWrapper(sess, get<2>(t), query_no_space, query_no_space_length, modname, "memory_to_disk") == true) + return true; + return false; +} + +template +bool admin_handler_command_kill_connection(char *query_no_space, unsigned int query_no_space_length, Client_Session& sess, ProxySQL_Admin *pa) { + uint32_t id=atoi(query_no_space+16); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Trying to kill session %u\n", id); + bool rc=GloMTH->kill_session(id); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + if (rc) { + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + } else { + char buf[1024]; + sprintf(buf,"Unknown thread id: %u", id); + SPA->send_error_msg_to_client(sess, buf); + } + return false; +} + +template +bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_space_length, Client_Session& sess, ProxySQL_Admin *pa) { + +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__)) && defined(__linux) + // currently only support x86-32, x86-64, ARM, and MIPS on Linux + if (!(strncasecmp("PROXYSQL COREDUMP", query_no_space, strlen("PROXYSQL COREDUMP")))) { + string filename = "core"; + if (query_no_space_length > strlen("PROXYSQL COREDUMP")) { + if (query_no_space[strlen("PROXYSQL COREDUMP")] == ' ') { + filename = string(query_no_space+strlen("PROXYSQL COREDUMP ")); + } else { + filename = ""; + } + } + if (filename == "") { + proxy_error("Received incorrect PROXYSQL COREDUMP command: %s\n", query_no_space); + } else { + proxy_info("Received PROXYSQL COREDUMP command: %s\n", query_no_space); + // generates a core dump + WriteCoreDump(filename.c_str()); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + string msg = "Coredump: " + filename; + SPA->send_ok_msg_to_client(sess, (char *)msg.c_str(), 0, query_no_space); + return false; + } + } + if (!(strncasecmp("PROXYSQL COMPRESSEDCOREDUMP", query_no_space, strlen("PROXYSQL COMPRESSEDCOREDUMP")))) { + string filename = "core"; + if (query_no_space_length > strlen("PROXYSQL COMPRESSEDCOREDUMP")) { + if (query_no_space[strlen("PROXYSQL COMPRESSEDCOREDUMP")] == ' ') { + filename = string(query_no_space+strlen("PROXYSQL COMPRESSEDCOREDUMP ")); + } else { + filename = ""; + } + } + if (filename == "") { + proxy_error("Received incorrect PROXYSQL COMPRESSEDCOREDUMP command: %s\n", query_no_space); + } else { + proxy_info("Received PROXYSQL COMPRESSEDCOREDUMP command: %s\n", query_no_space); + // generates a compressed core dump + WriteCompressedCoreDump(filename.c_str(), SIZE_MAX, COREDUMPER_COMPRESSED, NULL); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + string msg = "Coredump: " + filename; + SPA->send_ok_msg_to_client(sess, (char *)msg.c_str(), 0, query_no_space); + return false; + } + } +#endif + + if (!(strncasecmp("PROXYSQL CLUSTER_NODE_UUID ", query_no_space, strlen("PROXYSQL CLUSTER_NODE_UUID ")))) { + int l = strlen("PROXYSQL CLUSTER_NODE_UUID "); + if (sess->client_myds->addr.port == 0) { + proxy_warning("Received PROXYSQL CLUSTER_NODE_UUID not from TCP socket. Exiting client\n"); + SPA->send_error_msg_to_client(sess, (char *)"Received PROXYSQL CLUSTER_NODE_UUID not from TCP socket"); + sess->client_myds->shut_soft(); + return false; + } + if (query_no_space_length >= (unsigned int)l+36+2) { + uuid_t uu; + char *A_uuid = NULL; + char *B_interface = NULL; + c_split_2(query_no_space+l, " ", &A_uuid, &B_interface); // we split the value + if (uuid_parse(A_uuid, uu)==0 && B_interface && strlen(B_interface)) { + proxy_info("Received PROXYSQL CLUSTER_NODE_UUID from %s:%d : %s\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); + if (sess->proxysql_node_address==NULL) { + sess->proxysql_node_address = new ProxySQL_Node_Address(sess->client_myds->addr.addr, sess->client_myds->addr.port); + sess->proxysql_node_address->uuid = strdup(A_uuid); + if (sess->proxysql_node_address->admin_mysql_ifaces) { + free(sess->proxysql_node_address->admin_mysql_ifaces); + } + sess->proxysql_node_address->admin_mysql_ifaces = strdup(B_interface); + proxy_info("Created new link with Cluster node %s:%d : %s at interface %s\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, A_uuid, B_interface); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + free(A_uuid); + free(B_interface); + return false; + } else { + if (strcmp(A_uuid, sess->proxysql_node_address->uuid)) { + proxy_error("Cluster node %s:%d is sending a new UUID : %s . Former UUID : %s . Exiting client\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, A_uuid, sess->proxysql_node_address->uuid); + SPA->send_error_msg_to_client(sess, (char *)"Received PROXYSQL CLUSTER_NODE_UUID with a new UUID not matching the previous one"); + sess->client_myds->shut_soft(); + free(A_uuid); + free(B_interface); + return false; + } else { + proxy_info("Cluster node %s:%d is sending again its UUID : %s\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, A_uuid); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + free(A_uuid); + free(B_interface); + return false; + } + } + free(A_uuid); + free(B_interface); + return false; + } else { + proxy_warning("Received PROXYSQL CLUSTER_NODE_UUID from %s:%d with invalid format: %s . Exiting client\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); + SPA->send_error_msg_to_client(sess, (char *)"Received PROXYSQL CLUSTER_NODE_UUID with invalid format"); + sess->client_myds->shut_soft(); + return false; + } + } else { + proxy_warning("Received PROXYSQL CLUSTER_NODE_UUID from %s:%d with invalid format: %s . Exiting client\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); + SPA->send_error_msg_to_client(sess, (char *)"Received PROXYSQL CLUSTER_NODE_UUID with invalid format"); + sess->client_myds->shut_soft(); + return false; + } + } + if (query_no_space_length==strlen("PROXYSQL READONLY") && !strncasecmp("PROXYSQL READONLY",query_no_space, query_no_space_length)) { + // this command enables admin_read_only , so the admin module is in read_only mode + proxy_info("Received PROXYSQL READONLY command\n"); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->set_read_only(true); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + if (query_no_space_length==strlen("PROXYSQL READWRITE") && !strncasecmp("PROXYSQL READWRITE",query_no_space, query_no_space_length)) { + // this command disables admin_read_only , so the admin module won't be in read_only mode + proxy_info("Received PROXYSQL WRITE command\n"); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->set_read_only(false); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + if (query_no_space_length==strlen("PROXYSQL START") && !strncasecmp("PROXYSQL START",query_no_space, query_no_space_length)) { + proxy_info("Received PROXYSQL START command\n"); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + bool rc=false; + if (admin_nostart_) { + rc=__sync_bool_compare_and_swap(&GloVars.global.nostart,1,0); + } + if (rc) { + // Set the status variable 'threads_initialized' to 0 because it's initialized back + // in main 'init_phase3'. After GloMTH have been initialized again. + __sync_bool_compare_and_swap(&GloMTH->status_variables.threads_initialized, 1, 0); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Starting ProxySQL following PROXYSQL START command\n"); + while(__sync_fetch_and_add(&GloMTH->status_variables.threads_initialized, 0) == 1) { + usleep(1000); + } + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + } else { + proxy_warning("ProxySQL was already started when received PROXYSQL START command\n"); + SPA->send_error_msg_to_client(sess, (char *)"ProxySQL already started"); + } + return false; + } + + if (query_no_space_length==strlen("PROXYSQL RESTART") && !strncasecmp("PROXYSQL RESTART",query_no_space, query_no_space_length)) { + proxy_info("Received PROXYSQL RESTART command\n"); + // This function was introduced into 'prometheus::Registry' for being + // able to do a complete reset of all the 'prometheus counters'. It + // shall only be used during ProxySQL shutdown phases. + GloVars.prometheus_registry->ResetCounters(); + __sync_bool_compare_and_swap(&glovars.shutdown,0,1); + glovars.reload=1; + return false; + } + + if (query_no_space_length==strlen("PROXYSQL STOP") && !strncasecmp("PROXYSQL STOP",query_no_space, query_no_space_length)) { + proxy_info("Received PROXYSQL STOP command\n"); + // to speed up this process we first change wait_timeout to 0 + // MySQL_thread will call poll() with a maximum timeout of 100ms + admin_old_wait_timeout=GloMTH->get_variable_int((char *)"wait_timeout"); + GloMTH->set_variable((char *)"wait_timeout",(char *)"0"); + GloMTH->commit(); + GloMTH->signal_all_threads(0); + GloMTH->stop_listeners(); + char buf[32]; + sprintf(buf,"%d",admin_old_wait_timeout); + GloMTH->set_variable((char *)"wait_timeout",buf); + GloMTH->commit(); + glovars.reload=2; + // This function was introduced into 'prometheus::Registry' for being + // able to do a complete reset of all the 'prometheus counters'. It + // shall only be used during ProxySQL shutdown phases. + GloVars.prometheus_registry->ResetCounters(); + __sync_bool_compare_and_swap(&glovars.shutdown,0,1); + // After setting the shutdown flag, we should wake all threads and wait for + // the shutdown phase to complete. + GloMTH->signal_all_threads(0); + while (__sync_fetch_and_add(&glovars.shutdown,0)==1) { + usleep(1000); + } + // After shutdown phase is completed, we must to send a 'OK' to the + // mysql client, otherwise, since this session might not be drop due + // to the waiting condition, the client wont disconnect and will + // keep forever waiting for acknowledgement. + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if (query_no_space_length==strlen("PROXYSQL PAUSE") && !strncasecmp("PROXYSQL PAUSE",query_no_space, query_no_space_length)) { + proxy_info("Received PROXYSQL PAUSE command\n"); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + if (admin_nostart_) { + if (__sync_fetch_and_add((uint8_t *)(&GloVars.global.nostart),0)) { + SPA->send_error_msg_to_client(sess, (char *)"ProxySQL MySQL module not running, impossible to pause"); + return false; + } + } + if (admin_proxysql_mysql_paused==false) { + // to speed up this process we first change poll_timeout to 10 + // MySQL_thread will call poll() with a maximum timeout of 10ms + admin_old_wait_timeout=GloMTH->get_variable_int((char *)"poll_timeout"); + GloMTH->set_variable((char *)"poll_timeout",(char *)"10"); + GloMTH->commit(); + GloMTH->signal_all_threads(0); + GloMTH->stop_listeners(); + admin_proxysql_mysql_paused=true; + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + // we now rollback poll_timeout + char buf[32]; + sprintf(buf,"%d",admin_old_wait_timeout); + GloMTH->set_variable((char *)"poll_timeout",buf); + GloMTH->commit(); + } else { + SPA->send_error_msg_to_client(sess, (char *)"ProxySQL MySQL module is already paused, impossible to pause"); + } + + if (admin_proxysql_pgsql_paused == false) { + // to speed up this process we first change poll_timeout to 10 + // PgSQL_thread will call poll() with a maximum timeout of 10ms + admin_old_wait_timeout = GloPTH->get_variable_int((char*)"poll_timeout"); + GloPTH->set_variable((char*)"poll_timeout", (char*)"10"); + GloPTH->commit(); + GloPTH->signal_all_threads(0); + GloPTH->stop_listeners(); + admin_proxysql_pgsql_paused = true; + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + // we now rollback poll_timeout + char buf[32]; + sprintf(buf, "%d", admin_old_wait_timeout); + GloPTH->set_variable((char*)"poll_timeout", buf); + GloPTH->commit(); + } + else { + SPA->send_error_msg_to_client(sess, (char*)"ProxySQL PgSQL module is already paused, impossible to pause"); + } + return false; + } + + if (query_no_space_length==strlen("PROXYSQL RESUME") && !strncasecmp("PROXYSQL RESUME",query_no_space, query_no_space_length)) { + proxy_info("Received PROXYSQL RESUME command\n"); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + if (admin_nostart_) { + if (__sync_fetch_and_add((uint8_t *)(&GloVars.global.nostart),0)) { + SPA->send_error_msg_to_client(sess, (char *)"ProxySQL MySQL module not running, impossible to resume"); + return false; + } + } + if (admin_proxysql_mysql_paused==true) { + // to speed up this process we first change poll_timeout to 10 + // MySQL_thread will call poll() with a maximum timeout of 10ms + admin_old_wait_timeout=GloMTH->get_variable_int((char *)"poll_timeout"); + GloMTH->set_variable((char *)"poll_timeout",(char *)"10"); + GloMTH->commit(); + GloMTH->signal_all_threads(0); + GloMTH->start_listeners(); + //char buf[32]; + //sprintf(buf,"%d",old_wait_timeout); + //GloMTH->set_variable((char *)"poll_timeout",buf); + //GloMTH->commit(); + admin_proxysql_mysql_paused=false; + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + // we now rollback poll_timeout + char buf[32]; + sprintf(buf,"%d",admin_old_wait_timeout); + GloMTH->set_variable((char *)"poll_timeout",buf); + GloMTH->commit(); + } else { + SPA->send_error_msg_to_client(sess, (char *)"ProxySQL MySQL module is not paused, impossible to resume"); + } + + if (admin_proxysql_pgsql_paused == true) { + // to speed up this process we first change poll_timeout to 10 + // MySQL_thread will call poll() with a maximum timeout of 10ms + admin_old_wait_timeout = GloPTH->get_variable_int((char*)"poll_timeout"); + GloPTH->set_variable((char*)"poll_timeout", (char*)"10"); + GloPTH->commit(); + GloPTH->signal_all_threads(0); + GloPTH->start_listeners(); + //char buf[32]; + //sprintf(buf,"%d",old_wait_timeout); + //GloPTH->set_variable((char *)"poll_timeout",buf); + //GloPTH->commit(); + admin_proxysql_pgsql_paused = false; + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + // we now rollback poll_timeout + char buf[32]; + sprintf(buf, "%d", admin_old_wait_timeout); + GloPTH->set_variable((char*)"poll_timeout", buf); + GloPTH->commit(); + } + else { + SPA->send_error_msg_to_client(sess, (char*)"ProxySQL MySQL module is not paused, impossible to resume"); + } + return false; + } + + if (query_no_space_length==strlen("PROXYSQL SHUTDOWN SLOW") && !strncasecmp("PROXYSQL SHUTDOWN SLOW",query_no_space, query_no_space_length)) { + glovars.proxy_restart_on_error=false; + glovars.reload=0; + proxy_info("Received PROXYSQL SHUTDOWN SLOW command\n"); + __sync_bool_compare_and_swap(&glovars.shutdown,0,1); + return false; + } + + if (query_no_space_length==strlen("PROXYSQL FLUSH LOGS") && !strncasecmp("PROXYSQL FLUSH LOGS",query_no_space, query_no_space_length)) { + proxy_info("Received PROXYSQL FLUSH LOGS command\n"); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->flush_logs(); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if (query_no_space_length==strlen("PROXYSQL FLUSH QUERY CACHE") && !strncasecmp("PROXYSQL FLUSH QUERY CACHE",query_no_space, query_no_space_length)) { + proxy_info("Received PROXYSQL FLUSH QUERY CACHE command\n"); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + if (GloQC) { + GloQC->flush(); + } + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if (!strcasecmp("PROXYSQL FLUSH MYSQL CLIENT HOSTS", query_no_space)) { + proxy_info("Received PROXYSQL FLUSH MYSQL CLIENT HOSTS command\n"); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + if (GloMTH) { + GloMTH->flush_client_host_cache(); + } + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("PROXYSQL FLUSH CONFIGDB") && !strncasecmp("PROXYSQL FLUSH CONFIGDB",query_no_space, query_no_space_length)) // see #923 + ) { + proxy_info("Received %s command\n", query_no_space); + proxy_warning("A misconfigured configdb will cause undefined behaviors\n"); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->flush_configdb(); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if (strcasecmp("PROXYSQL RELOAD TLS",query_no_space) == 0) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + std::string s; + int rc = ProxySQL_create_or_load_TLS(false, s); + if (rc == 0) { + SPA->send_ok_msg_to_client(sess, s.length() ? (char*)s.c_str() : NULL, 0, query_no_space); + } else { + SPA->send_error_msg_to_client(sess, s.length() ? (char *)s.c_str() : (char *)"RELOAD TLS failed"); + } + return false; + } + +#ifndef NOJEM + if (query_no_space_length==strlen("PROXYSQL MEMPROFILE START") && !strncasecmp("PROXYSQL MEMPROFILE START",query_no_space, query_no_space_length)) { + bool en=true; + mallctl("prof.active", NULL, NULL, &en, sizeof(bool)); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + if (query_no_space_length==strlen("PROXYSQL MEMPROFILE STOP") && !strncasecmp("PROXYSQL MEMPROFILE STOP",query_no_space, query_no_space_length)) { + bool en=false; + mallctl("prof.active", NULL, NULL, &en, sizeof(bool)); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } +#endif + +#ifdef WITHGCOV + if (query_no_space_length==strlen("PROXYSQL GCOV DUMP") && !strncasecmp("PROXYSQL GCOV DUMP",query_no_space, query_no_space_length)) { + proxy_info("Received %s command\n", query_no_space); + __gcov_dump(); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + if (query_no_space_length==strlen("PROXYSQL GCOV RESET") && !strncasecmp("PROXYSQL GCOV RESET",query_no_space, query_no_space_length)) { + proxy_info("Received %s command\n", query_no_space); + __gcov_reset(); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } +#endif + + if (query_no_space_length==strlen("PROXYSQL KILL") && !strncasecmp("PROXYSQL KILL",query_no_space, query_no_space_length)) { + proxy_info("Received PROXYSQL KILL command\n"); + exit(EXIT_SUCCESS); + } + + if (query_no_space_length==strlen("PROXYSQL SHUTDOWN") && !strncasecmp("PROXYSQL SHUTDOWN",query_no_space, query_no_space_length)) { + // in 2.1 , PROXYSQL SHUTDOWN behaves like PROXYSQL KILL : quick exit + // the former PROXYQL SHUTDOWN is now replaced with PROXYSQL SHUTDOWN SLOW + proxy_info("Received PROXYSQL SHUTDOWN command\n"); + exit(EXIT_SUCCESS); + } + + return true; +} + +// Returns true if the given name is either a know mysql or admin global variable. +bool is_valid_global_variable(const char *var_name) { + if (strlen(var_name) > 6 && !strncmp(var_name, "mysql-", 6) && GloMTH->has_variable(var_name + 6)) { + return true; + } else if (strlen(var_name) > 6 && !strncmp(var_name, "pgsql-", 6) && GloPTH->has_variable(var_name + 6)) { + return true; + } else if (strlen(var_name) > 6 && !strncmp(var_name, "admin-", 6) && SPA->has_variable(var_name + 6)) { + return true; + } else if (strlen(var_name) > 5 && !strncmp(var_name, "ldap-", 5) && GloMyLdapAuth && GloMyLdapAuth->has_variable(var_name + 5)) { + return true; + } else if (strlen(var_name) > 13 && !strncmp(var_name, "sqliteserver-", 13) && GloSQLite3Server && GloSQLite3Server->has_variable(var_name + 13)) { + return true; +#ifdef PROXYSQLCLICKHOUSE + } else if (strlen(var_name) > 11 && !strncmp(var_name, "clickhouse-", 11) && GloClickHouseServer && GloClickHouseServer->has_variable(var_name + 11)) { + return true; +#endif /* PROXYSQLCLICKHOUSE */ + } else { + return false; + } +} + + +// This method translates a 'SET variable=value' command into an equivalent UPDATE. It doesn't yes support setting +// multiple variables at once. +// +// It modifies the original query. +template +bool admin_handler_command_set(char *query_no_space, unsigned int query_no_space_length, Client_Session& sess, ProxySQL_Admin *pa, char **q, unsigned int *ql) { + if (!strstr(query_no_space,(char *)"password")) { // issue #599 + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command %s\n", query_no_space); + if (strncasecmp(query_no_space,(char *)"set autocommit",strlen((char *)"set autocommit"))) { + if (strncasecmp(query_no_space,(char *)"SET @@session.autocommit",strlen((char *)"SET @@session.autocommit"))) { + proxy_info("Received command %s\n", query_no_space); + } + } + } + // Get a pointer to the beginnig of var=value entry and split to get var name and value + char *set_entry = query_no_space + strlen("SET "); + char *untrimmed_var_name=NULL; + char *var_value=NULL; + c_split_2(set_entry, "=", &untrimmed_var_name, &var_value); + + // Trim spaces from var name to allow writing like 'var = value' + char *var_name = trim_spaces_in_place(untrimmed_var_name); + + if (strstr(var_name,(char *)"password") || strcmp(var_name,(char *)"mysql-default_authentication_plugin")==0) { + proxy_info("Received SET command for %s\n", var_name); + } + + bool run_query = false; + // Check if the command tries to set a non-existing variable. + if (strcmp(var_name,"mysql-init_connect")==0) { + char *err_msg_fmt = (char *) "ERROR: Global variable '%s' is not configurable using SET command. You must run UPDATE global_variables"; + size_t buff_len = strlen(err_msg_fmt) + strlen(var_name) + 1; + char *buff = (char *) malloc(buff_len); + snprintf(buff, buff_len, err_msg_fmt, var_name); + SPA->send_error_msg_to_client(sess, buff); + free(buff); + run_query = false; + } else { + if (!is_valid_global_variable(var_name)) { + char *err_msg_fmt = (char *) "ERROR: Unknown global variable: '%s'."; + size_t buff_len = strlen(err_msg_fmt) + strlen(var_name) + 1; + char *buff = (char *) malloc(buff_len); + snprintf(buff, buff_len, err_msg_fmt, var_name); + SPA->send_ok_msg_to_client(sess, buff, 0, query_no_space); + free(buff); + run_query = false; + } else { + const char *update_format = (char *)"UPDATE global_variables SET variable_value=%s WHERE variable_name='%s'"; + // Computed length is more than needed since it also counts the format modifiers (%s). + size_t query_len = strlen(update_format) + strlen(var_name) + strlen(var_value) + 1; + char *query = (char *)l_alloc(query_len); + snprintf(query, query_len, update_format, var_value, var_name); + + run_query = true; + l_free(*ql,*q); + *q = query; + *ql = strlen(*q) + 1; + } + } + free(untrimmed_var_name); + free(var_value); + return run_query; +} + +/* Note: + * This function can modify the original query + */ +template +bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query_no_space_length, Client_Session& sess, ProxySQL_Admin *pa, char **q, unsigned int *ql) { + proxy_debug(PROXY_DEBUG_ADMIN, 5, "Received command %s\n", query_no_space); + +#ifdef DEBUG + if ((query_no_space_length>11) && ( (!strncasecmp("SAVE DEBUG ", query_no_space, 11)) || (!strncasecmp("LOAD DEBUG ", query_no_space, 11))) ) { + if ( + (query_no_space_length==strlen("LOAD DEBUG TO MEMORY") && !strncasecmp("LOAD DEBUG TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD DEBUG TO MEM") && !strncasecmp("LOAD DEBUG TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD DEBUG FROM DISK") && !strncasecmp("LOAD DEBUG FROM DISK",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + // we are now copying the data from memory to disk + // tables involved are: + // * debug_levels + // * debug_filters + // We only delete from filters and not from levels because the + // levels are hardcoded and fixed in number, while filters can + // be arbitrary + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->admindb->execute("DELETE FROM main.debug_filters"); + SPA->admindb->execute("INSERT OR REPLACE INTO main.debug_levels SELECT * FROM disk.debug_levels"); + SPA->admindb->execute("INSERT INTO main.debug_filters SELECT * FROM disk.debug_filters"); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded debug levels/filters to MEMORY\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("SAVE DEBUG FROM MEMORY") && !strncasecmp("SAVE DEBUG FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE DEBUG FROM MEM") && !strncasecmp("SAVE DEBUG FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE DEBUG TO DISK") && !strncasecmp("SAVE DEBUG TO DISK",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + // we are now copying the data from disk to memory + // tables involved are: + // * debug_levels + // * debug_filters + // We only delete from filters and not from levels because the + // levels are hardcoded and fixed in number, while filters can + // be arbitrary + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->admindb->execute("DELETE FROM disk.debug_filters"); + SPA->admindb->execute("INSERT OR REPLACE INTO disk.debug_levels SELECT * FROM main.debug_levels"); + SPA->admindb->execute("INSERT INTO disk.debug_filters SELECT * FROM main.debug_filters"); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved debug levels/filters to DISK\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("LOAD DEBUG FROM MEMORY") && !strncasecmp("LOAD DEBUG FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD DEBUG FROM MEM") && !strncasecmp("LOAD DEBUG FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD DEBUG TO RUNTIME") && !strncasecmp("LOAD DEBUG TO RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD DEBUG TO RUN") && !strncasecmp("LOAD DEBUG TO RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + int rc=SPA->load_debug_to_runtime(); + if (rc) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded debug levels/filters to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 1, "Error while loading debug levels/filters to RUNTIME\n"); + SPA->send_error_msg_to_client(sess, (char *)"Error while loading debug levels/filters to RUNTIME"); + } + return false; + } + + if ( + (query_no_space_length==strlen("SAVE DEBUG TO MEMORY") && !strncasecmp("SAVE DEBUG TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE DEBUG TO MEM") && !strncasecmp("SAVE DEBUG TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE DEBUG FROM RUNTIME") && !strncasecmp("SAVE DEBUG FROM RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE DEBUG FROM RUN") && !strncasecmp("SAVE DEBUG FROM RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->save_debug_from_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved debug levels/filters from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + } +#endif /* DEBUG */ + + if ((query_no_space_length>13) && ( (!strncasecmp("SAVE RESTAPI ", query_no_space, 13)) || (!strncasecmp("LOAD RESTAPI ", query_no_space, 13))) ) { + + if (FlushCommandWrapper(sess, "restapi", query_no_space, query_no_space_length) == true) + return false; + + if ( + (query_no_space_length==strlen("LOAD RESTAPI FROM MEMORY") && !strncasecmp("LOAD RESTAPI FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD RESTAPI FROM MEM") && !strncasecmp("LOAD RESTAPI FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD RESTAPI TO RUNTIME") && !strncasecmp("LOAD RESTAPI TO RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD RESTAPI TO RUN") && !strncasecmp("LOAD RESTAPI TO RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->proxysql_restapi().load_restapi_to_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded restapito RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("LOAD RESTAPI FROM CONFIG") && !strncasecmp("LOAD RESTAPI FROM CONFIG",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + int rows=0; + rows=SPA->proxysql_config().Read_Restapi_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded restapi from CONFIG\n"); + SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_error_msg_to_client(sess, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + } + return false; + } + + if ( + (query_no_space_length==strlen("SAVE RESTAPI TO MEMORY") && !strncasecmp("SAVE RESTAPI TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE RESTAPI TO MEM") && !strncasecmp("SAVE RESTAPI TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE RESTAPI FROM RUNTIME") && !strncasecmp("SAVE RESTAPI FROM RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE RESTAPI FROM RUN") && !strncasecmp("SAVE RESTAPI FROM RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->save_scheduler_runtime_to_database(false); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved scheduler from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } + + if ((query_no_space_length>15) && ( (!strncasecmp("SAVE SCHEDULER ", query_no_space, 15)) || (!strncasecmp("LOAD SCHEDULER ", query_no_space, 15))) ) { + + if (FlushCommandWrapper(sess, "scheduler", query_no_space, query_no_space_length) == true) + return false; + + if ( + (query_no_space_length==strlen("LOAD SCHEDULER FROM MEMORY") && !strncasecmp("LOAD SCHEDULER FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD SCHEDULER FROM MEM") && !strncasecmp("LOAD SCHEDULER FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD SCHEDULER TO RUNTIME") && !strncasecmp("LOAD SCHEDULER TO RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD SCHEDULER TO RUN") && !strncasecmp("LOAD SCHEDULER TO RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->load_scheduler_to_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded scheduler to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("LOAD SCHEDULER FROM CONFIG") && !strncasecmp("LOAD SCHEDULER FROM CONFIG",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + int rows=0; + rows=SPA->proxysql_config().Read_Scheduler_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded scheduler from CONFIG\n"); + SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_error_msg_to_client(sess, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + } + return false; + } + + if ( + (query_no_space_length==strlen("SAVE SCHEDULER TO MEMORY") && !strncasecmp("SAVE SCHEDULER TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE SCHEDULER TO MEM") && !strncasecmp("SAVE SCHEDULER TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE SCHEDULER FROM RUNTIME") && !strncasecmp("SAVE SCHEDULER FROM RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE SCHEDULER FROM RUN") && !strncasecmp("SAVE SCHEDULER FROM RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->save_scheduler_runtime_to_database(false); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved scheduler from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + } + if ((query_no_space_length>16) && (!strncasecmp("LOAD MYSQL USER ", query_no_space, 16)) ) { + if (query_no_space_length>27) { + if (!strncasecmp(" TO RUNTIME", query_no_space+query_no_space_length-11, 11)) { + char *name=(char *)malloc(query_no_space_length-27+1); + strncpy(name,query_no_space+16,query_no_space_length-27); + name[query_no_space_length-27]=0; + int i=0; + int s=strlen(name); + bool legitname=true; + for (i=0; i= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + ( (c == '-') || (c == '+') || (c == '_')) + ) { + v=true; + } + if (v==false) { + legitname=false; + } + } + if (legitname) { + proxy_info("Loading user %s\n", name); + pthread_mutex_lock(&users_mutex); + + if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { + SPA->public_add_active_users(USERNAME_BACKEND, name); + SPA->public_add_active_users(USERNAME_FRONTEND, name); + } else { + SPA->public_add_active_users(USERNAME_BACKEND, name); + SPA->public_add_active_users(USERNAME_FRONTEND, name); + } + + pthread_mutex_unlock(&users_mutex); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + } else { + proxy_info("Tried to load invalid user %s\n", name); + char *s=(char *)"Invalid name %s"; + char *m=(char *)malloc(strlen(s)+strlen(name)+1); + sprintf(m,s,name); + SPA->send_error_msg_to_client(sess, m); + free(m); + } + free(name); + return false; + } + } + } +#ifdef PROXYSQLCLICKHOUSE + if ( ( GloVars.global.clickhouse_server == true ) && (query_no_space_length>22) && ( (!strncasecmp("SAVE CLICKHOUSE USERS ", query_no_space, 22)) || (!strncasecmp("LOAD CLICKHOUSE USERS ", query_no_space, 22))) ) { + if ( + (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO MEMORY") && !strncasecmp("LOAD CLICKHOUSE USERS TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO MEM") && !strncasecmp("LOAD CLICKHOUSE USERS TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD CLICKHOUSE USERS FROM DISK") && !strncasecmp("LOAD CLICKHOUSE USERS FROM DISK",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->flush_clickhouse_users__from_disk_to_memory(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading clickhouse users to MEMORY\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM MEMORY") && !strncasecmp("SAVE CLICKHOUSE USERS FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM MEM") && !strncasecmp("SAVE CLICKHOUSE USERS FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE CLICKHOUSE USERS TO DISK") && !strncasecmp("SAVE CLICKHOUSE USERS TO DISK",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->flush_clickhouse_users__from_memory_to_disk(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saving clickhouse users to DISK\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("LOAD CLICKHOUSE USERS FROM MEMORY") && !strncasecmp("LOAD CLICKHOUSE USERS FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD CLICKHOUSE USERS FROM MEM") && !strncasecmp("LOAD CLICKHOUSE USERS FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO RUNTIME") && !strncasecmp("LOAD CLICKHOUSE USERS TO RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO RUN") && !strncasecmp("LOAD CLICKHOUSE USERS TO RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->init_clickhouse_users(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded clickhouse users to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("SAVE CLICKHOUSE USERS TO MEMORY") && !strncasecmp("SAVE CLICKHOUSE USERS TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE CLICKHOUSE USERS TO MEM") && !strncasecmp("SAVE CLICKHOUSE USERS TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM RUNTIME") && !strncasecmp("SAVE CLICKHOUSE USERS FROM RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM RUN") && !strncasecmp("SAVE CLICKHOUSE USERS FROM RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->save_clickhouse_users_runtime_to_database(false); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved clickhouse users from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + } +#endif /* PROXYSQLCLICKHOUSE */ + + if ((query_no_space_length>17) && ( (!strcasecmp("SAVE MYSQL DIGEST TO DISK", query_no_space) ) )) { + proxy_info("Received %s command\n", query_no_space); + unsigned long long curtime1=monotonic_time(); + int r1 = SPA->FlushDigestTableToDisk(SPA->statsdb_disk); + unsigned long long curtime2=monotonic_time(); + curtime1 = curtime1/1000; + curtime2 = curtime2/1000; + proxy_info("Saved stats_mysql_query_digest to disk: %llums to write %u entries\n", curtime2-curtime1, r1); + SPA->send_ok_msg_to_client(sess, NULL, r1, query_no_space); + return false; + } + + if ((query_no_space_length > 17) && ((!strcasecmp("SAVE PGSQL DIGEST TO DISK", query_no_space)))) { + proxy_info("Received %s command\n", query_no_space); + unsigned long long curtime1 = monotonic_time(); + int r1 = SPA->FlushDigestTableToDisk(SPA->statsdb_disk); + unsigned long long curtime2 = monotonic_time(); + curtime1 = curtime1 / 1000; + curtime2 = curtime2 / 1000; + proxy_info("Saved stats_mysql_query_digest to disk: %llums to write %u entries\n", curtime2 - curtime1, r1); + SPA->send_ok_msg_to_client(sess, NULL, r1, query_no_space); + return false; + } + + if ((query_no_space_length>17) && + ((!strncasecmp("SAVE MYSQL USERS ", query_no_space, 17)) || (!strncasecmp("LOAD MYSQL USERS ", query_no_space, 17)) || + ((!strncasecmp("SAVE PGSQL USERS ", query_no_space, 17)) || (!strncasecmp("LOAD PGSQL USERS ", query_no_space, 17))))) { + + const bool is_pgsql = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? true : false; + const std::string modname = is_pgsql ? "pgsql_users" : "mysql_users"; + + tuple, vector>& t = load_save_disk_commands[modname]; + if ( is_admin_command_or_alias(get<1>(t), query_no_space, query_no_space_length) ) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + + if (is_pgsql) + SPA->flush_pgsql_users__from_disk_to_memory(); + else + SPA->flush_mysql_users__from_disk_to_memory(); + + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading %s to MEMORY\n", modname.c_str()); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + if ( is_admin_command_or_alias(get<2>(t), query_no_space, query_no_space_length) ) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + + if (is_pgsql) + SPA->flush_pgsql_users__from_memory_to_disk(); + else + SPA->flush_mysql_users__from_memory_to_disk(); + + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saving %s to DISK\n", modname.c_str()); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if (is_pgsql) { + if (is_admin_command_or_alias(LOAD_PGSQL_USERS_FROM_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->init_pgsql_users(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql users to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } else { + if (is_admin_command_or_alias(LOAD_MYSQL_USERS_FROM_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->init_users(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql users to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } + + if ( + (query_no_space_length==strlen("LOAD MYSQL USERS FROM CONFIG") && (!strncasecmp("LOAD MYSQL USERS FROM CONFIG",query_no_space, query_no_space_length) || + !strncasecmp("LOAD PGSQL USERS FROM CONFIG", query_no_space, query_no_space_length))) + ) { + proxy_info("Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + int rows=0; + if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { + rows=SPA->proxysql_config().Read_PgSQL_Users_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql users from CONFIG\n"); + } else { + rows=SPA->proxysql_config().Read_MySQL_Users_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql users from CONFIG\n"); + } + SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_error_msg_to_client(sess, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + } + return false; + } + + if (is_pgsql) { + if (is_admin_command_or_alias(SAVE_PGSQL_USERS_TO_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->save_pgsql_users_runtime_to_database(false); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved pgsql users from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } else { + if (is_admin_command_or_alias(SAVE_MYSQL_USERS_TO_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->save_mysql_users_runtime_to_database(false); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved mysql users from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } + } + if ((query_no_space_length>28) && ( (!strncasecmp("SAVE SQLITESERVER VARIABLES ", query_no_space, 28)) || (!strncasecmp("LOAD SQLITESERVER VARIABLES ", query_no_space, 28))) ) { + + if ( + (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO MEMORY") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO MEM") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES FROM DISK") && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM DISK",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + l_free(*ql,*q); + *q=l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'sqliteserver-%'"); + *ql=strlen(*q)+1; + return true; + } + + if ( + (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM MEMORY") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM MEM") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES TO DISK") && !strncasecmp("SAVE SQLITESERVER VARIABLES TO DISK",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + l_free(*ql,*q); + *q=l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'sqliteserver-%'"); + *ql=strlen(*q)+1; + return true; + } + + if ( + (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES FROM MEMORY") && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES FROM MEM") && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO RUNTIME") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO RUN") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->load_sqliteserver_variables_to_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded SQLiteServer variables to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES TO MEMORY") && !strncasecmp("SAVE SQLITESERVER VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES TO MEM") && !strncasecmp("SAVE SQLITESERVER VARIABLES TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM RUNTIME") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM RUN") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->save_sqliteserver_variables_from_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved SQLiteServer variables from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } +#ifdef PROXYSQLCLICKHOUSE + if ((query_no_space_length>26) && ( (!strncasecmp("SAVE CLICKHOUSE VARIABLES ", query_no_space, 26)) || (!strncasecmp("LOAD CLICKHOUSE VARIABLES ", query_no_space, 26))) ) { + + if ( + (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO MEMORY") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO MEM") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES FROM DISK") && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM DISK",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + l_free(*ql,*q); + *q=l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'clickhouse-%'"); + *ql=strlen(*q)+1; + return true; + } + + if ( + (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM MEMORY") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM MEM") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES TO DISK") && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO DISK",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + l_free(*ql,*q); + *q=l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'clickhouse-%'"); + *ql=strlen(*q)+1; + return true; + } + + if ( + (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES FROM MEMORY") && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES FROM MEM") && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO RUNTIME") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO RUN") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->load_clickhouse_variables_to_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded clickhouse variables to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES TO MEMORY") && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES TO MEM") && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM RUNTIME") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM RUN") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->save_clickhouse_variables_from_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved clickhouse variables from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } +#endif /* PROXYSQLCLICKHOUSE */ + + if (GloMyLdapAuth) { + if ((query_no_space_length>20) && ( (!strncasecmp("SAVE LDAP VARIABLES ", query_no_space, 20)) || (!strncasecmp("LOAD LDAP VARIABLES ", query_no_space, 20))) ) { + + if ( + (query_no_space_length==strlen("LOAD LDAP VARIABLES TO MEMORY") && !strncasecmp("LOAD LDAP VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD LDAP VARIABLES TO MEM") && !strncasecmp("LOAD LDAP VARIABLES TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD LDAP VARIABLES FROM DISK") && !strncasecmp("LOAD LDAP VARIABLES FROM DISK",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + l_free(*ql,*q); + *q=l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'ldap-%'"); + *ql=strlen(*q)+1; + return true; + } + + if ( + (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM MEMORY") && !strncasecmp("SAVE LDAP VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM MEM") && !strncasecmp("SAVE LDAP VARIABLES FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE LDAP VARIABLES TO DISK") && !strncasecmp("SAVE LDAP VARIABLES TO DISK",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + l_free(*ql,*q); + *q=l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'ldap-%'"); + *ql=strlen(*q)+1; + return true; + } + + if ( + (query_no_space_length==strlen("LOAD LDAP VARIABLES FROM MEMORY") && !strncasecmp("LOAD LDAP VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD LDAP VARIABLES FROM MEM") && !strncasecmp("LOAD LDAP VARIABLES FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD LDAP VARIABLES TO RUNTIME") && !strncasecmp("LOAD LDAP VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD LDAP VARIABLES TO RUN") && !strncasecmp("LOAD LDAP VARIABLES TO RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->load_ldap_variables_to_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded ldap variables to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("SAVE LDAP VARIABLES TO MEMORY") && !strncasecmp("SAVE LDAP VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE LDAP VARIABLES TO MEM") && !strncasecmp("SAVE LDAP VARIABLES TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM RUNTIME") && !strncasecmp("SAVE LDAP VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM RUN") && !strncasecmp("SAVE LDAP VARIABLES FROM RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->save_ldap_variables_from_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved ldap variables from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } + } + + if ((query_no_space_length > 21) && ((!strncasecmp("SAVE MYSQL VARIABLES ", query_no_space, 21)) || (!strncasecmp("LOAD MYSQL VARIABLES ", query_no_space, 21)) || + (!strncasecmp("SAVE PGSQL VARIABLES ", query_no_space, 21)) || (!strncasecmp("LOAD PGSQL VARIABLES ", query_no_space, 21)))) { + + const bool is_pgsql = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? true : false; + const std::string modname = is_pgsql ? "pgsql_variables" : "mysql_variables"; + + tuple, vector>& t = load_save_disk_commands[modname]; + if (is_admin_command_or_alias(get<1>(t), query_no_space, query_no_space_length)) { + l_free(*ql, *q); + if (is_pgsql) { + *q = l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'pgsql-%'"); + } + else { + *q = l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'mysql-%'"); + } + *ql = strlen(*q) + 1; + return true; + } + + if (is_admin_command_or_alias(get<2>(t), query_no_space, query_no_space_length)) { + l_free(*ql, *q); + if (is_pgsql) { + *q = l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'pgsql-%'"); + } + else { + *q = l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'mysql-%'"); + } + *ql = strlen(*q) + 1; + return true; + } + + if (is_pgsql) { + if (is_admin_command_or_alias(LOAD_PGSQL_VARIABLES_FROM_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->load_pgsql_variables_to_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql variables to RUNTIME\n"); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql variables to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } else { + if (is_admin_command_or_alias(LOAD_MYSQL_VARIABLES_FROM_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->load_mysql_variables_to_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql variables to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } + + if ( + (query_no_space_length==strlen("LOAD MYSQL VARIABLES FROM CONFIG") && (!strncasecmp("LOAD MYSQL VARIABLES FROM CONFIG",query_no_space, query_no_space_length) || + !strncasecmp("LOAD PGSQL VARIABLES FROM CONFIG", query_no_space, query_no_space_length))) + ) { + proxy_info("Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + int rows=0; + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { + rows=SPA->proxysql_config().Read_Global_Variables_from_configfile("pgsql"); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql global variables from CONFIG\n"); + } else { + rows = SPA->proxysql_config().Read_Global_Variables_from_configfile("mysql"); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql global variables from CONFIG\n"); + } + SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_error_msg_to_client(sess, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + } + return false; + } + + if (is_pgsql) { + if (is_admin_command_or_alias(SAVE_PGSQL_VARIABLES_TO_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->save_pgsql_variables_from_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved pgsql variables from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } else { + if (is_admin_command_or_alias(SAVE_MYSQL_VARIABLES_TO_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->save_mysql_variables_from_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved mysql variables from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } + } + + if ((query_no_space_length > 14) && (!strncasecmp("LOAD COREDUMP ", query_no_space, 14))) { + + if ( is_admin_command_or_alias(LOAD_COREDUMP_FROM_MEMORY, query_no_space, query_no_space_length) ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + bool rc = SPA->load_coredump_to_runtime(); + if (rc) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded coredump filters to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 1, "Error while loading coredump filters to RUNTIME\n"); + SPA->send_error_msg_to_client(sess, (char*)"Error while loading coredump filters to RUNTIME"); + } + return false; + } + } + + if ((query_no_space_length>19) && ( (!strncasecmp("SAVE MYSQL SERVERS ", query_no_space, 19)) || (!strncasecmp("LOAD MYSQL SERVERS ", query_no_space, 19)) || + (!strncasecmp("SAVE PGSQL SERVERS ", query_no_space, 19)) || (!strncasecmp("LOAD PGSQL SERVERS ", query_no_space, 19)))) { + + const bool is_pgsql = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? true : false; + const std::string modname = is_pgsql ? "pgsql_servers" : "mysql_servers"; + + if (FlushCommandWrapper(sess, modname, query_no_space, query_no_space_length) == true) + return false; + + if (is_pgsql) { + if (is_admin_command_or_alias(LOAD_PGSQL_SERVERS_FROM_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->pgsql_servers_wrlock(); + SPA->load_pgsql_servers_to_runtime(); + SPA->pgsql_servers_wrunlock(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql servers to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } else { + if (is_admin_command_or_alias(LOAD_MYSQL_SERVERS_FROM_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->mysql_servers_wrlock(); + SPA->load_mysql_servers_to_runtime(); + SPA->mysql_servers_wrunlock(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql servers to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } + + if ( + (query_no_space_length==strlen("LOAD MYSQL SERVERS FROM CONFIG") && (!strncasecmp("LOAD MYSQL SERVERS FROM CONFIG",query_no_space, query_no_space_length) || + !strncasecmp("LOAD PGSQL SERVERS FROM CONFIG", query_no_space, query_no_space_length) ))) { + + proxy_info("Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + int rows=0; + if (is_pgsql) { + rows=SPA->proxysql_config().Read_PgSQL_Servers_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql servers from CONFIG\n"); + } else { + rows=SPA->proxysql_config().Read_MySQL_Servers_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql servers from CONFIG\n"); + } + SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_error_msg_to_client(sess, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + } + return false; + } + + if (is_pgsql) { + if (is_admin_command_or_alias(SAVE_PGSQL_SERVERS_TO_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->pgsql_servers_wrlock(); + SPA->save_pgsql_servers_runtime_to_database(false); + SPA->pgsql_servers_wrunlock(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved pgsql servers from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } else { + if (is_admin_command_or_alias(SAVE_MYSQL_SERVERS_TO_MEMORY, query_no_space, query_no_space_length)) { + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + SPA->mysql_servers_wrlock(); + SPA->save_mysql_servers_runtime_to_database(false); + SPA->mysql_servers_wrunlock(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved mysql servers from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } + } + + if ((query_no_space_length>22) && ( (!strncasecmp("SAVE PROXYSQL SERVERS ", query_no_space, 22)) || (!strncasecmp("LOAD PROXYSQL SERVERS ", query_no_space, 22))) ) { + + if (FlushCommandWrapper(sess, "proxysql_servers", query_no_space, query_no_space_length) == true) + return false; +/* + string modname = "proxysql_servers"; + tuple, vector>& t = load_save_disk_commands[modname]; + if (FlushCommandWrapper(sess, get<1>(t), query_no_space, query_no_space_length, modname, "disk_to_memory") == true) + return false; + + if (FlushCommandWrapper(sess, get<2>(t), query_no_space, query_no_space_length, modname, "memory_to_disk") == true) + return false; +*/ + if ( + (query_no_space_length==strlen("LOAD PROXYSQL SERVERS FROM MEMORY") && !strncasecmp("LOAD PROXYSQL SERVERS FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD PROXYSQL SERVERS FROM MEM") && !strncasecmp("LOAD PROXYSQL SERVERS FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD PROXYSQL SERVERS TO RUNTIME") && !strncasecmp("LOAD PROXYSQL SERVERS TO RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD PROXYSQL SERVERS TO RUN") && !strncasecmp("LOAD PROXYSQL SERVERS TO RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + //SPA->mysql_servers_wrlock(); + // before calling load_proxysql_servers_to_runtime() we release + // sql_query_global_mutex to prevent a possible deadlock due to + // a race condition + // load_proxysql_servers_to_runtime() calls ProxySQL_Cluster::load_servers_list() + // that then calls ProxySQL_Cluster_Nodes::load_servers_list(), holding a mutex + pthread_mutex_unlock(&SPA->sql_query_global_mutex); + SPA->load_proxysql_servers_to_runtime(true); + // we re-acquired the mutex because it will be released by the calling function + pthread_mutex_lock(&SPA->sql_query_global_mutex); + //SPA->mysql_servers_wrunlock(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded ProxySQL servers to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + if ( + (query_no_space_length==strlen("SAVE PROXYSQL SERVERS TO MEMORY") && !strncasecmp("SAVE PROXYSQL SERVERS TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE PROXYSQL SERVERS TO MEM") && !strncasecmp("SAVE PROXYSQL SERVERS TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE PROXYSQL SERVERS FROM RUNTIME") && !strncasecmp("SAVE PROXYSQL SERVERS FROM RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE PROXYSQL SERVERS FROM RUN") && !strncasecmp("SAVE PROXYSQL SERVERS FROM RUN",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + //SPA->mysql_servers_wrlock(); + // before save_proxysql_servers_runtime_to_database() we release + // sql_query_global_mutex to prevent a possible deadlock due to + // a race condition + // save_proxysql_servers_runtime_to_database() calls ProxySQL_Cluster::dump_table_proxysql_servers() + // that then holds a mutex + pthread_mutex_unlock(&SPA->sql_query_global_mutex); + SPA->save_proxysql_servers_runtime_to_database(false); + // we re-acquired the mutex because it will be released by the calling function + pthread_mutex_lock(&SPA->sql_query_global_mutex); + //SPA->mysql_servers_wrunlock(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved ProxySQL servers from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("LOAD PROXYSQL SERVERS FROM CONFIG") && !strncasecmp("LOAD PROXYSQL SERVERS FROM CONFIG",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + int rows=0; + rows=SPA->proxysql_config().Read_ProxySQL_Servers_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded ProxySQL servers from CONFIG\n"); + SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_error_msg_to_client(sess, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + } + return false; + } + + } + + if ((query_no_space_length>20) && (( (!strncasecmp("SAVE MYSQL FIREWALL ", query_no_space, 20)) || (!strncasecmp("LOAD MYSQL FIREWALL ", query_no_space, 20))) || + (!strncasecmp("SAVE PGSQL FIREWALL ", query_no_space, 20) || (!strncasecmp("LOAD PGSQL FIREWALL ", query_no_space, 20)))) ) { + + const std::string modname = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? "pgsql_firewall" : "mysql_firewall"; + + if (FlushCommandWrapper(sess, modname, query_no_space, query_no_space_length) == true) + return false; + + if ( + (query_no_space_length==strlen("LOAD MYSQL FIREWALL FROM CONFIG") && (!strncasecmp("LOAD MYSQL FIREWALL FROM CONFIG",query_no_space, query_no_space_length) || + !strncasecmp("LOAD PGSQL FIREWALL FROM CONFIG", query_no_space, query_no_space_length))) + ) { + proxy_info("Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + int rows=0; + // FIXME: not implemented yet + if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { + // rows=SPA->proxysql_config().Read_PgSQL_Firewall_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql firewall from CONFIG\n"); + } else { + // rows=SPA->proxysql_config().Read_MySQL_Firewall_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql firewall from CONFIG\n"); + } + SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_error_msg_to_client(sess, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + } + return false; + } + + if ( + (query_no_space_length==strlen("LOAD MYSQL FIREWALL FROM MEMORY") && !strncasecmp("LOAD MYSQL FIREWALL FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD MYSQL FIREWALL FROM MEM") && !strncasecmp("LOAD MYSQL FIREWALL FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD MYSQL FIREWALL TO RUNTIME") && !strncasecmp("LOAD MYSQL FIREWALL TO RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD MYSQL FIREWALL TO RUN") && !strncasecmp("LOAD MYSQL FIREWALL TO RUN",query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("LOAD PGSQL FIREWALL FROM MEMORY") && !strncasecmp("LOAD PGSQL FIREWALL FROM MEMORY", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("LOAD PGSQL FIREWALL FROM MEM") && !strncasecmp("LOAD PGSQL FIREWALL FROM MEM", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("LOAD PGSQL FIREWALL TO RUNTIME") && !strncasecmp("LOAD PGSQL FIREWALL TO RUNTIME", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("LOAD PGSQL FIREWALL TO RUN") && !strncasecmp("LOAD PGSQL FIREWALL TO RUN", query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + const char* err = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? SPA->load_pgsql_firewall_to_runtime() : SPA->load_mysql_firewall_to_runtime(); + if (err==NULL) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql firewall to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + } else { + SPA->send_error_msg_to_client(sess, err); + } + return false; + } + + if ( + (query_no_space_length==strlen("SAVE MYSQL FIREWALL TO MEMORY") && !strncasecmp("SAVE MYSQL FIREWALL TO MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE MYSQL FIREWALL TO MEM") && !strncasecmp("SAVE MYSQL FIREWALL TO MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE MYSQL FIREWALL FROM RUNTIME") && !strncasecmp("SAVE MYSQL FIREWALL FROM RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SAVE MYSQL FIREWALL FROM RUN") && !strncasecmp("SAVE MYSQL FIREWALL FROM RUN",query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE PGSQL FIREWALL TO MEMORY") && !strncasecmp("SAVE PGSQL FIREWALL TO MEMORY", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE PGSQL FIREWALL TO MEM") && !strncasecmp("SAVE PGSQL FIREWALL TO MEM", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE PGSQL FIREWALL FROM RUNTIME") && !strncasecmp("SAVE PGSQL FIREWALL FROM RUNTIME", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE PGSQL FIREWALL FROM RUN") && !strncasecmp("SAVE PGSQL FIREWALL FROM RUN", query_no_space, query_no_space_length)) + + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { + SPA->save_pgsql_firewall_from_runtime(false); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved pgsql firewall from RUNTIME\n"); + } else { + SPA->save_mysql_firewall_from_runtime(false); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved mysql firewall from RUNTIME\n"); + } + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } + + if ((query_no_space_length>23) && ( (!strncasecmp("SAVE MYSQL QUERY RULES ", query_no_space, 23)) || (!strncasecmp("LOAD MYSQL QUERY RULES ", query_no_space, 23)) || + (!strncasecmp("SAVE PGSQL QUERY RULES ", query_no_space, 23)) || (!strncasecmp("LOAD PGSQL QUERY RULES ", query_no_space, 23)) + ) ) { + + const std::string modname = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? "pgsql_query_rules" : "mysql_query_rules"; + if (FlushCommandWrapper(sess, modname, query_no_space, query_no_space_length) == true) + return false; + + if ( + (query_no_space_length==strlen("LOAD MYSQL QUERY RULES FROM CONFIG") && (!strncasecmp("LOAD MYSQL QUERY RULES FROM CONFIG",query_no_space, query_no_space_length) || + !strncasecmp("LOAD PGSQL QUERY RULES FROM CONFIG", query_no_space, query_no_space_length))) + ) { + proxy_info("Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + int rows=0; + if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { + rows = SPA->proxysql_config().Read_PgSQL_Query_Rules_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql query rules from CONFIG\n"); + } else { + rows = SPA->proxysql_config().Read_MySQL_Query_Rules_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql query rules from CONFIG\n"); + } + SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_error_msg_to_client(sess, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + } + return false; + } + + if ( + (query_no_space_length==strlen("LOAD MYSQL QUERY RULES FROM MEMORY") && !strncasecmp("LOAD MYSQL QUERY RULES FROM MEMORY",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD MYSQL QUERY RULES FROM MEM") && !strncasecmp("LOAD MYSQL QUERY RULES FROM MEM",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD MYSQL QUERY RULES TO RUNTIME") && !strncasecmp("LOAD MYSQL QUERY RULES TO RUNTIME",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("LOAD MYSQL QUERY RULES TO RUN") && !strncasecmp("LOAD MYSQL QUERY RULES TO RUN",query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("LOAD PGSQL QUERY RULES FROM MEMORY") && !strncasecmp("LOAD PGSQL QUERY RULES FROM MEMORY", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("LOAD PGSQL QUERY RULES FROM MEM") && !strncasecmp("LOAD PGSQL QUERY RULES FROM MEM", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("LOAD PGSQL QUERY RULES TO RUNTIME") && !strncasecmp("LOAD PGSQL QUERY RULES TO RUNTIME", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("LOAD PGSQL QUERY RULES TO RUN") && !strncasecmp("LOAD PGSQL QUERY RULES TO RUN", query_no_space, query_no_space_length)) + + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + char* err = NULL; + + if (query_no_space[5] == 'P' || query_no_space[5] == 'p') + err = SPA->load_pgsql_query_rules_to_runtime(); + else + err = SPA->load_mysql_query_rules_to_runtime(); + + if (err==NULL) { + if (query_no_space[5] == 'P' || query_no_space[5] == 'p') + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql query rules to RUNTIME\n"); + else + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql query rules to RUNTIME\n"); + + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + } else { + SPA->send_error_msg_to_client(sess, err); + } + return false; + } + + if ( + (query_no_space_length == strlen("SAVE MYSQL QUERY RULES TO MEMORY") && !strncasecmp("SAVE MYSQL QUERY RULES TO MEMORY", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE MYSQL QUERY RULES TO MEM") && !strncasecmp("SAVE MYSQL QUERY RULES TO MEM", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE MYSQL QUERY RULES FROM RUNTIME") && !strncasecmp("SAVE MYSQL QUERY RULES FROM RUNTIME", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE MYSQL QUERY RULES FROM RUN") && !strncasecmp("SAVE MYSQL QUERY RULES FROM RUN", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE PGSQL QUERY RULES TO MEMORY") && !strncasecmp("SAVE PGSQL QUERY RULES TO MEMORY", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE PGSQL QUERY RULES TO MEM") && !strncasecmp("SAVE PGSQL QUERY RULES TO MEM", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE PGSQL QUERY RULES FROM RUNTIME") && !strncasecmp("SAVE PGSQL QUERY RULES FROM RUNTIME", query_no_space, query_no_space_length)) + || + (query_no_space_length == strlen("SAVE PGSQL QUERY RULES FROM RUN") && !strncasecmp("SAVE PGSQL QUERY RULES FROM RUN", query_no_space, query_no_space_length)) + + ) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; + if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { + SPA->save_pgsql_query_rules_from_runtime(false); + SPA->save_pgsql_query_rules_fast_routing_from_runtime(false); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved pgsql query rules from RUNTIME\n"); + } else { + SPA->save_mysql_query_rules_from_runtime(false); + SPA->save_mysql_query_rules_fast_routing_from_runtime(false); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved mysql query rules from RUNTIME\n"); + } + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + } + + if ((query_no_space_length>21) && ( (!strncasecmp("SAVE ADMIN VARIABLES ", query_no_space, 21)) || (!strncasecmp("LOAD ADMIN VARIABLES ", query_no_space, 21))) ) { + + if ( is_admin_command_or_alias(LOAD_ADMIN_VARIABLES_TO_MEMORY, query_no_space, query_no_space_length) ) { + l_free(*ql,*q); + *q=l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'admin-%'"); + *ql=strlen(*q)+1; + return true; + } + + if ( is_admin_command_or_alias(SAVE_ADMIN_VARIABLES_FROM_MEMORY, query_no_space, query_no_space_length) ) { + l_free(*ql,*q); + *q=l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'admin-%'"); + *ql=strlen(*q)+1; + return true; + } + + if ( is_admin_command_or_alias(LOAD_ADMIN_VARIABLES_FROM_MEMORY, query_no_space, query_no_space_length) ) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->load_admin_variables_to_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded admin variables to RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + if ( + (query_no_space_length==strlen("LOAD ADMIN VARIABLES FROM CONFIG") && !strncasecmp("LOAD ADMIN VARIABLES FROM CONFIG",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + int rows=0; + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + rows=SPA->proxysql_config().Read_Global_Variables_from_configfile("admin"); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded admin variables from CONFIG\n"); + SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_error_msg_to_client(sess, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + } + return false; + } + + if ( is_admin_command_or_alias(SAVE_ADMIN_VARIABLES_TO_MEMORY, query_no_space, query_no_space_length) ) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->save_admin_variables_from_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved admin variables from RUNTIME\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + return false; + } + + } + + if (!strncasecmp("SAVE CONFIG TO FILE", query_no_space, strlen("SAVE CONFIG TO FILE"))) { + std::string fileName = query_no_space + strlen("SAVE CONFIG TO FILE"); + + fileName.erase(0, fileName.find_first_not_of("\t\n\v\f\r ")); + fileName.erase(fileName.find_last_not_of("\t\n\v\f\r ") + 1); + if (fileName.size() == 0) { + proxy_error("ProxySQL Admin Error: empty file name\n"); + SPA->send_error_msg_to_client(sess, (char *)"ProxySQL Admin Error: empty file name"); + return false; + } + std::string data; + data.reserve(100000); + data += config_header; + int rc = pa->proxysql_config().Write_Global_Variables_to_configfile(data); + rc = pa->proxysql_config().Write_MySQL_Users_to_configfile(data); + rc = pa->proxysql_config().Write_MySQL_Query_Rules_to_configfile(data); + rc = pa->proxysql_config().Write_MySQL_Servers_to_configfile(data); + rc = pa->proxysql_config().Write_PgSQL_Users_to_configfile(data); + rc = pa->proxysql_config().Write_PgSQL_Query_Rules_to_configfile(data); + rc = pa->proxysql_config().Write_PgSQL_Servers_to_configfile(data); + rc = pa->proxysql_config().Write_Scheduler_to_configfile(data); + rc = pa->proxysql_config().Write_ProxySQL_Servers_to_configfile(data); + if (rc) { + std::stringstream ss; + proxy_error("ProxySQL Admin Error: Cannot extract configuration\n"); + SPA->send_error_msg_to_client(sess, (char *)"ProxySQL Admin Error: Cannot extract configuration"); + return false; + } else { + std::ofstream out; + out.open(fileName); + if (out.is_open()) { + out << data; + out.close(); + if (!out) { + std::stringstream ss; + ss << "ProxySQL Admin Error: Error writing file " << fileName; + proxy_error("%s\n", ss.str().c_str()); + SPA->send_error_msg_to_client(sess, (char*)ss.str().c_str()); + return false; + } else { + std::stringstream ss; + ss << "File " << fileName << " is saved."; + SPA->send_ok_msg_to_client(sess, (char*)ss.str().c_str(), 0, query_no_space); + return false; + } + } else { + std::stringstream ss; + ss << "ProxySQL Admin Error: Cannot open file " << fileName; + proxy_error("%s\n", ss.str().c_str()); + SPA->send_error_msg_to_client(sess, (char*)ss.str().c_str()); + return false; + } + } + } + + return true; +} + +/** + * @brief Helper function that converts the current timezone + * expressed in seconds into a string of the format: + * - '[-]HH:MM:00'. + * Following the same pattern as the possible values returned by the SQL query + * 'SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())' in a MySQL server. + * @return A string holding the specified representation of the + * supplied timezone. + */ +std::string timediff_timezone_offset() { + std::string time_zone_offset {}; + char result[8]; + time_t rawtime; + struct tm *info; + int offset; + + time(&rawtime); + info = localtime(&rawtime); + strftime(result, 8, "%z", info); + offset = (result[0] == '+') ? 1 : 0; + time_zone_offset = ((std::string)(result)).substr(offset, 3-offset) + ":" + ((std::string)(result)).substr(3, 2) + ":00"; + + return time_zone_offset; +} + + + +template +void admin_session_handler(Client_Session sess, void *_pa, PtrSize_t *pkt) { + + ProxySQL_Admin *pa=(ProxySQL_Admin *)_pa; + bool needs_vacuum = false; + char *error=NULL; + int cols; + int affected_rows = 0; + bool run_query=true; + SQLite3_result *resultset=NULL; + char *strA=NULL; + char *strB=NULL; + int strAl, strBl; + char *query=NULL; + unsigned int query_length = 0; + + if constexpr (std::is_same::value) { + query_length = pkt->size - sizeof(mysql_hdr); + query = (char*)l_alloc(query_length); + memcpy(query, (char*)pkt->ptr + sizeof(mysql_hdr) + 1, query_length - 1); + } + else if constexpr (std::is_same::value) { + assert(sess->client_myds); + + pgsql_hdr hdr{}; + if (sess->client_myds->myprot.get_header((unsigned char*)pkt->ptr, pkt->size, &hdr) == false) { + //error + proxy_warning("Malformed packet\n"); + SPA->send_error_msg_to_client(sess, "Malformed packet"); + return; + } + + switch (hdr.type) { + case PG_PKT_STARTUP_V2: + case PG_PKT_STARTUP: + case PG_PKT_CANCEL: + case PG_PKT_SSLREQ: + case PG_PKT_GSSENCREQ: + //error + return; + } + + query_length = hdr.data.size; + query = (char*)l_alloc(query_length); + memcpy(query, (char*)hdr.data.ptr, query_length - 1); + } + + query[query_length-1]=0; + + char *query_no_space=(char *)l_alloc(query_length); + memcpy(query_no_space,query,query_length); + + unsigned int query_no_space_length=remove_spaces(query_no_space); + //fprintf(stderr,"%s----\n",query_no_space); + + if (query_no_space_length) { + // fix bug #925 + while (query_no_space[query_no_space_length-1]==';' || query_no_space[query_no_space_length-1]==' ') { + query_no_space_length--; + query_no_space[query_no_space_length]=0; + } + } + + // add global mutex, see bug #1188 + pthread_mutex_lock(&pa->sql_query_global_mutex); + + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + if (!strncasecmp("LOGENTRY ", query_no_space, strlen("LOGENTRY "))) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command LOGENTRY: %s\n", query_no_space + strlen("LOGENTRY ")); + proxy_info("Received command LOGENTRY: %s\n", query_no_space + strlen("LOGENTRY ")); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + run_query=false; + goto __run_query; + } + } + + // handle special queries from Cluster + // for bug #1188 , ProxySQL Admin needs to know the exact query + + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + string tn = ""; + if (!strncasecmp(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS, query_no_space, strlen(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS))) { + tn = "cluster_mysql_servers"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS))) { + tn = "mysql_replication_hostgroups"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS))) { + tn = "mysql_group_replication_hostgroups"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_GALERA, query_no_space, strlen(CLUSTER_QUERY_MYSQL_GALERA))) { + tn = "mysql_galera_hostgroups"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_AWS_AURORA, query_no_space, strlen(CLUSTER_QUERY_MYSQL_AWS_AURORA))) { + tn = "mysql_aws_aurora_hostgroups"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_HOSTGROUP_ATTRIBUTES, query_no_space, strlen(CLUSTER_QUERY_MYSQL_HOSTGROUP_ATTRIBUTES))) { + tn = "mysql_hostgroup_attributes"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_SERVERS_SSL_PARAMS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_SERVERS_SSL_PARAMS))) { + tn = "mysql_servers_ssl_params"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_SERVERS_V2, query_no_space, strlen(CLUSTER_QUERY_MYSQL_SERVERS_V2))) { + tn = "mysql_servers_v2"; + } + if (tn != "") { + GloAdmin->mysql_servers_wrlock(); + resultset = MyHGM->get_current_mysql_table(tn); + GloAdmin->mysql_servers_wrunlock(); + + if (resultset == nullptr) { + // 'mysql_servers_v2' is a virtual table that represents the latest 'main.mysql_servers' + // records promoted by the user. This section shouldn't be reached, since the initial resulset + // for this table ('MySQL_HostGroups_Manager::incoming_mysql_servers') is generated during + // initialization, and it's only updated in subsequent user config promotions. In case we + // reach here, an empty resultset should be replied, as it would mean that no user + // config has ever been promoted to runtime, and thus, this virtual table should remain empty. + if (tn == "mysql_servers_v2") { + const string query_empty_resultset { + string { MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS } + " LIMIT 0" + }; + + char *error=NULL; + int cols=0; + int affected_rows=0; + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "%s\n", query); + GloAdmin->mysql_servers_wrlock(); + GloAdmin->admindb->execute_statement(query_empty_resultset.c_str(), &error, &cols, &affected_rows, &resultset); + GloAdmin->mysql_servers_wrunlock(); + } else { + resultset = MyHGM->dump_table_mysql(tn); + } + + if (resultset) { + sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); + delete resultset; + run_query=false; + goto __run_query; + } + } else { + sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); + run_query=false; + goto __run_query; + } + + } + } + + if (!strncasecmp(CLUSTER_QUERY_MYSQL_USERS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_USERS))) { + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { + pthread_mutex_lock(&users_mutex); + resultset = GloMyAuth->get_current_mysql_users(); + pthread_mutex_unlock(&users_mutex); + if (resultset != nullptr) { + sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); + run_query=false; + goto __run_query; + } + } + } + + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + if (!strncasecmp(CLUSTER_QUERY_MYSQL_QUERY_RULES, query_no_space, strlen(CLUSTER_QUERY_MYSQL_QUERY_RULES))) { + GloQPro->wrlock(); + resultset = GloQPro->get_current_query_rules_inner(); + if (resultset == NULL) { + GloQPro->wrunlock(); // unlock first + resultset = GloQPro->get_current_query_rules(); + if (resultset) { + sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); + delete resultset; + run_query=false; + goto __run_query; + } + } else { + sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); + //delete resultset; // DO NOT DELETE . This is the inner resultset of Query_Processor + GloQPro->wrunlock(); + run_query=false; + goto __run_query; + } + } + if (!strncasecmp(CLUSTER_QUERY_MYSQL_QUERY_RULES_FAST_ROUTING, query_no_space, strlen(CLUSTER_QUERY_MYSQL_QUERY_RULES_FAST_ROUTING))) { + GloQPro->wrlock(); + resultset = GloQPro->get_current_query_rules_fast_routing_inner(); + if (resultset == NULL) { + GloQPro->wrunlock(); // unlock first + resultset = GloQPro->get_current_query_rules_fast_routing(); + if (resultset) { + sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); + delete resultset; + run_query=false; + goto __run_query; + } + } else { + sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); + //delete resultset; // DO NOT DELETE . This is the inner resultset of Query_Processor + GloQPro->wrunlock(); + run_query=false; + goto __run_query; + } + } + } + + // if the client simply executes: + // SELECT COUNT(*) FROM runtime_mysql_query_rules_fast_routing + // we just return the count + if (strcmp("SELECT COUNT(*) FROM runtime_mysql_query_rules_fast_routing", query_no_space)==0) { + int cnt = GloQPro->get_current_query_rules_fast_routing_count(); + l_free(query_length,query); + char buf[256]; + sprintf(buf,"SELECT %d AS 'COUNT(*)'", cnt); + query=l_strdup(buf); + query_length=strlen(query)+1; + goto __run_query; + } + + if (!strncasecmp("TRUNCATE ", query_no_space, strlen("TRUNCATE "))) { + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + if (strstr(query_no_space,"stats_mysql_query_digest")) { + bool truncate_digest_table = false; + static char * truncate_digest_table_queries[] = { + (char *)"TRUNCATE TABLE stats.stats_mysql_query_digest", + (char *)"TRUNCATE TABLE stats.stats_mysql_query_digest_reset", + (char *)"TRUNCATE TABLE stats_mysql_query_digest", + (char *)"TRUNCATE TABLE stats_mysql_query_digest_reset", + (char *)"TRUNCATE stats.stats_mysql_query_digest", + (char *)"TRUNCATE stats.stats_mysql_query_digest_reset", + (char *)"TRUNCATE stats_mysql_query_digest", + (char *)"TRUNCATE stats_mysql_query_digest_reset" + }; + size_t l=sizeof(truncate_digest_table_queries)/sizeof(char *); + unsigned int i; + for (i=0;iadmindb->execute("DELETE FROM stats.stats_mysql_query_digest"); + SPA->admindb->execute("DELETE FROM stats.stats_mysql_query_digest_reset"); + SPA->vacuum_stats(true); + // purge the digest map, asynchronously, in single thread + char *msg = NULL; + int r1 = ProxySQL_Test___PurgeDigestTable(true, false, &msg); + SPA->send_ok_msg_to_client(sess, msg, r1, query_no_space); + free(msg); + run_query=false; + goto __run_query; + } + } + } + } +#ifdef DEBUG + /** + * @brief Handles the 'PROXYSQL_SIMULATOR' command. Performing the operation specified in the payload + * format. + * @details The 'PROXYSQL_SIMULATOR' command is specified the following format. Allowing to perform a + * certain internal state changing operation. Payload spec: + * ``` + * PROXYSQL_SIMULATOR ${operation} ${hg} ${address}:${port} ${operation_params} + * ``` + * + * Supported operations include: + * - mysql_error: Find the server specified by 'hostname:port' in the specified hostgroup and calls + * 'MySrvC::connect_error()' with the provider 'error_code'. + * + * Payload example: + * ``` + * PROXYSQL_SIMULATOR mysql_error 1 127.0.0.1 3306 1234 + * ``` + */ + if (!strncasecmp("PROXYSQL_SIMULATOR ", query_no_space, strlen("PROXYSQL_SIMULATOR "))) { + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + proxy_warning("Received PROXYSQL_SIMULATOR command: %s\n", query_no_space); + + re2::RE2::Options opts = re2::RE2::Options(RE2::Quiet); + re2::RE2 pattern("\\s*(\\w+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+):(\\d+) (\\d+)\\s*\\;*", opts); + re2::StringPiece input(query_no_space + strlen("PROXYSQL_SIMULATOR")); + + std::string command, s_hg, srv_addr, s_port, s_errcode {}; + bool c_res = re2::RE2::Consume(&input, pattern, &command, &s_hg, &srv_addr, &s_port, &s_errcode); + + long i_hg = 0; + long i_port = 0; + long i_errcode = 0; + + if (c_res == true) { + char* endptr = nullptr; + i_hg = std::strtol(s_hg.c_str(), &endptr, 10); + if (errno == ERANGE || errno == EINVAL) i_hg = LONG_MIN; + i_port = std::strtol(s_port.c_str(), &endptr, 10); + if (errno == ERANGE || errno == EINVAL) i_port = LONG_MIN; + i_errcode = std::strtol(s_errcode.c_str(), &endptr, 10); + if (errno == ERANGE || errno == EINVAL) i_errcode = LONG_MIN; + } + + if (c_res == true && i_hg != LONG_MIN && i_port != LONG_MIN && i_errcode != LONG_MIN) { + MyHGM->wrlock(); + + MySrvC* mysrvc = MyHGM->find_server_in_hg(i_hg, srv_addr, i_port); + if (mysrvc != nullptr) { + int backup_shun_on_failures; + if constexpr (std::is_same::value) { + backup_shun_on_failures = mysql_thread___shun_on_failures; + mysql_thread___shun_on_failures = 1; + // Set the error twice to surpass 'mysql_thread___shun_on_failures' value. + mysrvc->connect_error(i_errcode, false); + mysrvc->connect_error(i_errcode, false); + mysql_thread___shun_on_failures = backup_shun_on_failures; + } else if constexpr (std::is_same::value) { + backup_shun_on_failures = pgsql_thread___shun_on_failures; + pgsql_thread___shun_on_failures = 1; + // Set the error twice to surpass 'pgsql_thread___shun_on_failures' value. + mysrvc->connect_error(i_errcode, false); + mysrvc->connect_error(i_errcode, false); + pgsql_thread___shun_on_failures = backup_shun_on_failures; + } else { + assert(0); + } + + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + } else { + std::string t_err_msg { "Supplied server '%s:%d' wasn't found in hg '%d'" }; + std::string err_msg {}; + string_format(t_err_msg, err_msg, srv_addr.c_str(), i_port, i_hg); + + proxy_info("%s\n", err_msg.c_str()); + SPA->send_error_msg_to_client(sess, const_cast(err_msg.c_str())); + } + + MyHGM->wrunlock(); + } else { + SPA->send_error_msg_to_client(sess, (char*)"Invalid arguments supplied with query 'PROXYSQL_SIMULATOR'"); + } + + run_query=false; + goto __run_query; + } + } +#endif // DEBUG + if (!strncasecmp("PROXYSQLTEST ", query_no_space, strlen("PROXYSQLTEST "))) { + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->ProxySQL_Test_Handler(SPA, sess, query_no_space, run_query); + goto __run_query; + } + } + { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + needs_vacuum = SPA->GenericRefreshStatistics(query_no_space,query_no_space_length, ( sess->session_type == PROXYSQL_SESSION_ADMIN ? true : false ) ); + } + + + if (!strncasecmp("SHOW GLOBAL VARIABLES LIKE 'read_only'", query_no_space, strlen("SHOW GLOBAL VARIABLES LIKE 'read_only'"))) { + l_free(query_length,query); + char *q=(char *)"SELECT 'read_only' Variable_name, '%s' Value FROM global_variables WHERE Variable_name='admin-read_only'"; + query_length=strlen(q)+5; + query=(char *)l_alloc(query_length); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + bool ro=SPA->get_read_only(); + //sprintf(query,q,( ro ? "ON" : "OFF")); + PtrSize_t pkt_2; + if (ro) { + pkt_2.size=110; + pkt_2.ptr=l_alloc(pkt_2.size); + memcpy(pkt_2.ptr,READ_ONLY_ON,pkt_2.size); + } else { + pkt_2.size=111; + pkt_2.ptr=l_alloc(pkt_2.size); + memcpy(pkt_2.ptr,READ_ONLY_OFF,pkt_2.size); + } + sess->status=WAITING_CLIENT_DATA; + sess->client_myds->DSS=STATE_SLEEP; + sess->client_myds->PSarrayOUT->add(pkt_2.ptr,pkt_2.size); + run_query=false; + goto __run_query; + } + + if (!strncasecmp("SELECT @@global.read_only", query_no_space, strlen("SELECT @@global.read_only"))) { + l_free(query_length,query); + char *q=(char *)"SELECT 'read_only' Variable_name, '%s' Value FROM global_variables WHERE Variable_name='admin-read_only'"; + query_length=strlen(q)+5; + query=(char *)l_alloc(query_length); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + bool ro=SPA->get_read_only(); + //sprintf(query,q,( ro ? "ON" : "OFF")); + PtrSize_t pkt_2; + if (ro) { + pkt_2.size=73; + pkt_2.ptr=l_alloc(pkt_2.size); + memcpy(pkt_2.ptr,READ_ONLY_1,pkt_2.size); + } else { + pkt_2.size=73; + pkt_2.ptr=l_alloc(pkt_2.size); + memcpy(pkt_2.ptr,READ_ONLY_0,pkt_2.size); + } + sess->status=WAITING_CLIENT_DATA; + sess->client_myds->DSS=STATE_SLEEP; + sess->client_myds->PSarrayOUT->add(pkt_2.ptr,pkt_2.size); + run_query=false; + goto __run_query; + } + + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + if ((query_no_space_length>13) && (!strncasecmp("PULL VERSION ", query_no_space, 13))) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received PULL command\n"); + if ((query_no_space_length>27) && (!strncasecmp("PULL VERSION MYSQL SERVERS ", query_no_space, 27))) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received PULL VERSION MYSQL SERVERS command\n"); + unsigned int wait_mysql_servers_version = 0; + unsigned int wait_timeout = 0; + int rc = sscanf(query_no_space+27,"%u %u",&wait_mysql_servers_version, &wait_timeout); + if (rc < 2) { + SPA->send_error_msg_to_client(sess, (char *)"Invalid argument"); + run_query=false; + goto __run_query; + } else { + MyHGM->wait_servers_table_version(wait_mysql_servers_version, wait_timeout); + l_free(query_length,query); + unsigned int curver = MyHGM->get_servers_table_version(); + char buf[256]; + sprintf(buf,"SELECT %u AS 'version'", curver); + query=l_strdup(buf); + query_length=strlen(query)+1; + //SPA->send_ok_msg_to_client(sess, , NULL); + //run_query=false; + goto __run_query; + } + } + } + + + if ((query_no_space_length == strlen("SELECT GLOBAL_CHECKSUM()")) && (!strncasecmp("SELECT GLOBAL_CHECKSUM()", query_no_space, strlen("SELECT GLOBAL_CHECKSUM()")))) { + char buf[32]; + pthread_mutex_lock(&GloVars.checksum_mutex); + sprintf(buf,"%lu",GloVars.checksums_values.global_checksum); + pthread_mutex_unlock(&GloVars.checksum_mutex); + uint16_t setStatus = 0; + auto *myds=sess->client_myds; + auto *myprot=&sess->client_myds->myprot; + myds->DSS=STATE_QUERY_SENT_DS; + int sid=1; + myprot->generate_pkt_column_count(true,NULL,NULL,sid,1); sid++; + myprot->generate_pkt_field(true,NULL,NULL,sid,(char *)"",(char *)"",(char *)"",(char *)"CHECKSUM",(char *)"",63,31,MYSQL_TYPE_LONGLONG,161,0,false,0,NULL); sid++; + myds->DSS=STATE_COLUMN_DEFINITION; + myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; + char **p=(char **)malloc(sizeof(char*)*1); + unsigned long *l=(unsigned long *)malloc(sizeof(unsigned long *)*1); + l[0]=strlen(buf);; + p[0]=buf; + myprot->generate_pkt_row(true,NULL,NULL,sid,1,l,p); sid++; + myds->DSS=STATE_ROW; + myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; + myds->DSS=STATE_SLEEP; + run_query=false; + free(l); + free(p); + goto __run_query; + } + + + if ((query_no_space_length>8) && (!strncasecmp("PROXYSQL ", query_no_space, 8))) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received PROXYSQL command\n"); + //pthread_mutex_lock(&admin_mutex); + run_query=admin_handler_command_proxysql(query_no_space, query_no_space_length, sess, pa); + //pthread_mutex_unlock(&admin_mutex); + goto __run_query; + } + if ((query_no_space_length>5) && ( (!strncasecmp("SAVE ", query_no_space, 5)) || (!strncasecmp("LOAD ", query_no_space, 5))) ) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received LOAD or SAVE command\n"); + run_query=admin_handler_command_load_or_save(query_no_space, query_no_space_length, sess, pa, &query, &query_length); + goto __run_query; + } + if ((query_no_space_length>16) && ( (!strncasecmp("KILL CONNECTION ", query_no_space, 16)) || (!strncasecmp("KILL CONNECTION ", query_no_space, 16))) ) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received KILL CONNECTION command\n"); + run_query=admin_handler_command_kill_connection(query_no_space, query_no_space_length, sess, pa); + goto __run_query; + } + + + // queries generated by mysqldump + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + if ( + !strncmp("/*!40014 SET ", query_no_space, 13) || + !strncmp("/*!40101 SET ", query_no_space, 13) || + !strncmp("/*!40103 SET ", query_no_space, 13) || + !strncmp("/*!40111 SET ", query_no_space, 13) || + !strncmp("/*!80000 SET ", query_no_space, 13) || + !strncmp("/*!50503 SET ", query_no_space, 13) || + !strncmp("/*!50717 SET ", query_no_space, 13) || + !strncmp("/*!50717 SELECT ", query_no_space, strlen("/*!50717 SELECT ")) || + !strncmp("/*!50717 PREPARE ", query_no_space, strlen("/*!50717 PREPARE ")) || + !strncmp("/*!50717 EXECUTE ", query_no_space, strlen("/*!50717 EXECUTE ")) || + !strncmp("/*!50717 DEALLOCATE ", query_no_space, strlen("/*!50717 DEALLOCATE ")) || + !strncmp("/*!50112 SET ", query_no_space, strlen("/*!50112 SET ")) || + !strncmp("/*!50112 PREPARE ", query_no_space, strlen("/*!50112 PREPARE ")) || + !strncmp("/*!50112 EXECUTE ", query_no_space, strlen("/*!50112 EXECUTE ")) || + !strncmp("/*!50112 DEALLOCATE ", query_no_space, strlen("/*!50112 DEALLOCATE ")) || + !strncmp("/*!40000 ALTER TABLE", query_no_space, strlen("/*!40000 ALTER TABLE")) + || + !strncmp("/*!40100 SET @@SQL_MODE='' */", query_no_space, strlen("/*!40100 SET @@SQL_MODE='' */")) + || + !strncmp("/*!40103 SET TIME_ZONE=", query_no_space, strlen("/*!40103 SET TIME_ZONE=")) + || + !strncmp("LOCK TABLES", query_no_space, strlen("LOCK TABLES")) + || + !strncmp("UNLOCK TABLES", query_no_space, strlen("UNLOCK TABLES")) + || + !strncmp("SET SQL_QUOTE_SHOW_CREATE=1", query_no_space, strlen("SET SQL_QUOTE_SHOW_CREATE=1")) + || + !strncmp("SET SESSION character_set_results", query_no_space, strlen("SET SESSION character_set_results")) + || + !strncasecmp("USE ", query_no_space, strlen("USE ")) // this applies to all clients, not only mysqldump + ) { + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + run_query=false; + goto __run_query; + } + + if (!strncmp("SHOW STATUS LIKE 'binlog_snapshot_gtid_executed'", query_no_space, strlen("SHOW STATUS LIKE 'binlog_snapshot_gtid_executed'"))) { + l_free(query_length, query); + query = l_strdup("SELECT variable_name AS Variable_name, Variable_value AS Value FROM global_variables WHERE 1=0"); + query_length = strlen(query)+1; + goto __run_query; + } + if (!strncmp("SELECT COLUMN_NAME, JSON_EXTRACT(HISTOGRAM, '$.\"number-of-buckets-specified\"') FROM information_schema.COLUMN_STATISTICS", query_no_space, strlen("SELECT COLUMN_NAME, JSON_EXTRACT(HISTOGRAM, '$.\"number-of-buckets-specified\"') FROM information_schema.COLUMN_STATISTICS"))) { + l_free(query_length, query); + query = l_strdup("SELECT variable_name AS COLUMN_NAME, Variable_value AS 'JSON_EXTRACT(HISTOGRAM, ''$.\"number-of-buckets-specified\"'')' FROM global_variables WHERE 1=0"); + query_length = strlen(query)+1; + goto __run_query; + } + if (!strncmp("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'performance_schema' AND table_name = 'session_variables'", query_no_space, strlen("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'performance_schema' AND table_name = 'session_variables'"))) { + l_free(query_length,query); + query=l_strdup("SELECT 0 as 'COUNT(*)'"); + query_length=strlen(query)+1; + goto __run_query; + } + if (!strncmp("SHOW VARIABLES LIKE 'gtid\\_mode'", query_no_space, strlen("SHOW VARIABLES LIKE 'gtid\\_mode'"))) { + l_free(query_length,query); + query=l_strdup("SELECT variable_name Variable_name, Variable_value Value FROM global_variables WHERE Variable_name='gtid_mode'"); + query_length=strlen(query)+1; + goto __run_query; + } + if (!strncmp("select @@collation_database", query_no_space, strlen("select @@collation_database"))) { + l_free(query_length,query); + query=l_strdup("SELECT Collation '@@collation_database' FROM mysql_collations WHERE Collation='utf8_general_ci' LIMIT 1"); + query_length=strlen(query)+1; + goto __run_query; + } + if (!strncmp("SHOW VARIABLES LIKE 'ndbinfo\\_version'", query_no_space, strlen("SHOW VARIABLES LIKE 'ndbinfo\\_version'"))) { + l_free(query_length,query); + query=l_strdup("SELECT variable_name Variable_name, Variable_value Value FROM global_variables WHERE Variable_name='ndbinfo_version'"); + query_length=strlen(query)+1; + goto __run_query; + } + if (!strncasecmp("show table status like '", query_no_space, strlen("show table status like '"))) { + char *strA=query_no_space+24; + int strAl=strlen(strA); + if (strAl<2) { // error + goto __run_query; + } + char *err=NULL; + SQLite3_result *resultset=SPA->generate_show_table_status(strA, &err); + sess->SQLite3_to_MySQL(resultset, err, 0, &sess->client_myds->myprot); + if (resultset) delete resultset; + if (err) free(err); + run_query=false; + goto __run_query; + } + if (!strncasecmp("show fields from ", query_no_space, strlen("show fields from "))) { + char *strA=query_no_space+17; + int strAl=strlen(strA); + if (strAl==0) { // error + goto __run_query; + } + if (strA[0]=='`') { + strA++; + strAl--; + } + if (strAl<2) { // error + goto __run_query; + } + char *err=NULL; + SQLite3_result *resultset=SPA->generate_show_fields_from(strA, &err); + sess->SQLite3_to_MySQL(resultset, err, 0, &sess->client_myds->myprot); + if (resultset) delete resultset; + if (err) free(err); + run_query=false; + goto __run_query; + } + } + + // FIXME: this should be removed, it is just a POC for issue #253 . What is important is the call to GloMTH->signal_all_threads(); + if (!strncasecmp("SIGNAL MYSQL THREADS", query_no_space, strlen("SIGNAL MYSQL THREADS"))) { + GloMTH->signal_all_threads(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SPA->save_admin_variables_from_runtime(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Sent signal to all mysql threads\n"); + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + run_query=false; + goto __run_query; + } + + // fix bug #442 + if (!strncmp("SET SQL_SAFE_UPDATES=1", query_no_space, strlen("SET SQL_SAFE_UPDATES=1"))) { + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + run_query=false; + goto __run_query; + } + + // fix bug #1047 + if ( + (!strncasecmp("BEGIN", query_no_space, strlen("BEGIN"))) + || + (!strncasecmp("START TRANSACTION", query_no_space, strlen("START TRANSACTION"))) + || + (!strncasecmp("COMMIT", query_no_space, strlen("COMMIT"))) + || + (!strncasecmp("ROLLBACK", query_no_space, strlen("ROLLBACK"))) + || + (!strncasecmp("SET character_set_results", query_no_space, strlen("SET character_set_results"))) + || + (!strncasecmp("SET SQL_AUTO_IS_NULL", query_no_space, strlen("SET SQL_AUTO_IS_NULL"))) + || + (!strncasecmp("SET NAMES", query_no_space, strlen("SET NAMES"))) + || + (!strncasecmp("SET AUTOCOMMIT", query_no_space, strlen("SET AUTOCOMMIT"))) + ) { + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); + run_query=false; + goto __run_query; + } + + // MySQL client check command for dollars quote support, starting at version '8.1.0'. See #4300. + if (!strncasecmp("SELECT $$", query_no_space, strlen("SELECT $$"))) { + pair err_info { get_dollar_quote_error(mysql_thread___server_version) }; + SPA->send_error_msg_to_client(sess, const_cast(err_info.second), err_info.first); + run_query=false; + goto __run_query; + } + + if (query_no_space_length==SELECT_VERSION_COMMENT_LEN) { + if (!strncasecmp(SELECT_VERSION_COMMENT, query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT '(ProxySQL Admin Module)'"); + query_length=strlen(query)+1; + goto __run_query; + } + } + + if (!strncasecmp("select concat(@@version, ' ', @@version_comment)", query_no_space, strlen("select concat(@@version, ' ', @@version_comment)"))) { + l_free(query_length,query); + char *q = const_cast("SELECT '%s Admin Module'"); + query_length = strlen(q) + strlen(PROXYSQL_VERSION) + 1; + query = static_cast(l_alloc(query_length)); + sprintf(query, q, PROXYSQL_VERSION); + goto __run_query; + } + + // add support for SELECT current_user() and SELECT user() + // see https://github.com/sysown/proxysql/issues/1105#issuecomment-990940585 + if ( + (strcasecmp("SELECT current_user()", query_no_space) == 0) + || + (strcasecmp("SELECT user()", query_no_space) == 0) + ) { + bool current = false; + if (strcasestr(query_no_space, "current") != NULL) + current = true; + l_free(query_length,query); + std::string s = "SELECT '"; + s += sess->client_myds->myconn->userinfo->username ; + if (strlen(sess->client_myds->addr.addr) > 0) { + s += "@"; + s += sess->client_myds->addr.addr; + } + s += "' AS '"; + if (current == true) { + s+= "current_"; + } + s += "user()'"; + query=l_strdup(s.c_str()); + query_length=strlen(query)+1; + goto __run_query; + } + + if (!strncasecmp("select @@sql_mode", query_no_space, strlen("select @@sql_mode"))) { + l_free(query_length,query); + char *q = const_cast("SELECT \"\" as \"@@sql_mode\""); + query_length = strlen(q) + strlen(PROXYSQL_VERSION) + 1; + query = static_cast(l_alloc(query_length)); + sprintf(query, q, PROXYSQL_VERSION); + goto __run_query; + } + + // trivial implementation for 'connection_id()' to support 'mycli'. See #3247 + if (!strncasecmp("select connection_id()", query_no_space, strlen("select connection_id()"))) { + l_free(query_length,query); + // 'connection_id()' is always forced to be '0' + query=l_strdup("SELECT 0 AS 'CONNECTION_ID()'"); + query_length=strlen(query)+1; + goto __run_query; + } + + // implementation for 'SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())' in order to support'csharp' connector. See #2543 + if (!strncasecmp("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())", query_no_space, strlen("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())"))) { + l_free(query_length,query); + char *query1=(char*)"SELECT '%s' as 'TIMEDIFF(NOW(), UTC_TIMESTAMP()'"; + + // compute the timezone diff + std::string timezone_offset_str = timediff_timezone_offset(); + char *query2=(char *)malloc(strlen(query1) + strlen(timezone_offset_str.c_str()) + 1); + + // format the query + sprintf(query2, query1, timezone_offset_str.c_str()); + + // copy the resulting query + query=l_strdup(query2); + query_length=strlen(query2) + 1; + + // free the buffer used to format + free(query2); + goto __run_query; + } + + // implementation for '"select @@max_allowed_packet, @@character_set_client, @@character_set_connection, @@license, @@sql_mode, @@lower_case_table_names"' + // in order to support 'csharp' connector. See #2543 + if ( + !strncasecmp( + "select @@max_allowed_packet, @@character_set_client, @@character_set_connection, @@license, @@sql_mode, @@lower_case_table_names", + query_no_space, + strlen("select @@max_allowed_packet, @@character_set_client, @@character_set_connection, @@license, @@sql_mode, @@lower_case_table_names") + ) + ) { + l_free(query_length,query); + char *query1= + const_cast( + "select '67108864' as '@@max_allowed_packet', 'utf8' as '@@character_set_client', 'utf8' as '@@character_set_connection', '' as '@@license', '' as '@@sql_mode', '' as '@@lower_case_table_names'" + ); + query=l_strdup(query1); + query_length=strlen(query1)+1; + goto __run_query; + } + + if (query_no_space_length==SELECT_DB_USER_LEN) { + if (!strncasecmp(SELECT_DB_USER, query_no_space, query_no_space_length)) { + l_free(query_length,query); + char *query1=(char *)"SELECT \"admin\" AS 'DATABASE()', \"%s\" AS 'USER()'"; + char *query2=(char *)malloc(strlen(query1)+strlen(sess->client_myds->myconn->userinfo->username)+10); + sprintf(query2,query1,sess->client_myds->myconn->userinfo->username); + query=l_strdup(query2); + query_length=strlen(query2)+1; + free(query2); + goto __run_query; + } + } + + if (query_no_space_length==SELECT_CHARSET_VARIOUS_LEN) { + if (!strncasecmp(SELECT_CHARSET_VARIOUS, query_no_space, query_no_space_length)) { + l_free(query_length,query); + char *query1=(char *)"select 'utf8' as '@@character_set_client', 'utf8' as '@@character_set_connection', 'utf8' as '@@character_set_server', 'utf8' as '@@character_set_database' limit 1"; + query=l_strdup(query1); + query_length=strlen(query1)+1; + goto __run_query; + } + } + + if (!strncasecmp("SELECT @@version", query_no_space, strlen("SELECT @@version"))) { + l_free(query_length,query); + char *q=(char *)"SELECT '%s' AS '@@version'"; + if (GloMyLdapAuth == nullptr) { + query_length=strlen(q)+20+strlen(PROXYSQL_VERSION); + } else { + query_length=strlen(q)+20+strlen(PROXYSQL_VERSION)+strlen("-Enterprise"); + } + query=(char *)l_alloc(query_length); + if (GloMyLdapAuth == nullptr) { + sprintf(query, q, PROXYSQL_VERSION); + } else { + sprintf(query, q, PROXYSQL_VERSION"-Enterprise"); + } + goto __run_query; + } + + if (!strncasecmp("SELECT version()", query_no_space, strlen("SELECT version()"))) { + l_free(query_length,query); + char *q=(char *)"SELECT '%s' AS 'version()'"; + if (GloMyLdapAuth == nullptr) { + query_length=strlen(q)+20+strlen(PROXYSQL_VERSION); + } else { + query_length=strlen(q)+20+strlen(PROXYSQL_VERSION)+strlen("-Enterprise"); + } + query=(char *)l_alloc(query_length); + if (GloMyLdapAuth == nullptr) { + sprintf(query, q, PROXYSQL_VERSION); + } else { + sprintf(query, q, PROXYSQL_VERSION"-Enterprise"); + } + goto __run_query; + } + + if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in", query_no_space, strlen("SHOW VARIABLES WHERE Variable_name in"))) { + // Allow MariaDB ConnectorJ to connect to Admin #743 + if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','sql_mode')", query_no_space, strlen("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','sql_mode')"))) { + l_free(query_length,query); + char *q=(char *)"SELECT 'max_allowed_packet' Variable_name,'4194304' Value UNION ALL SELECT 'sql_mode', 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION' UNION ALL SELECT 'system_time_zone', 'UTC' UNION ALL SELECT 'time_zone','SYSTEM'"; + query_length=strlen(q)+20; + query=(char *)l_alloc(query_length); + sprintf(query,q,PROXYSQL_VERSION); + goto __run_query; + } + // Allow MariaDB ConnectorJ 2.4.1 to connect to Admin #2009 + if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','auto_increment_increment')", query_no_space, strlen("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','auto_increment_increment')"))) { + l_free(query_length,query); + char *q=(char *)"SELECT 'max_allowed_packet' Variable_name,'4194304' Value UNION ALL SELECT 'auto_increment_increment', '1' UNION ALL SELECT 'system_time_zone', 'UTC' UNION ALL SELECT 'time_zone','SYSTEM'"; + query_length=strlen(q)+20; + query=(char *)l_alloc(query_length); + sprintf(query,q,PROXYSQL_VERSION); + goto __run_query; + } + } + + { + bool rc; + rc=RE2::PartialMatch(query_no_space,*(RE2 *)(pa->match_regexes.re[0])); + if (rc) { + string *new_query=new std::string(query_no_space); + RE2::Replace(new_query,(char *)"^(\\w+)\\s+@@(\\w+)\\s*",(char *)"SELECT variable_value AS '@@max_allowed_packet' FROM global_variables WHERE variable_name='mysql-max_allowed_packet'"); + free(query); + query_length=new_query->length()+1; + query=(char *)malloc(query_length); + memcpy(query,new_query->c_str(),query_length-1); + query[query_length-1]='\0'; + delete new_query; + goto __run_query; + } + } + { + bool rc; + rc=RE2::PartialMatch(query_no_space,*(RE2 *)(pa->match_regexes.re[1])); + if (rc) { + string *new_query=new std::string(query_no_space); + RE2::Replace(new_query,(char *)"^(\\w+) *@@([0-9A-Za-z_-]+) *",(char *)"SELECT variable_value AS '@@\\2' FROM global_variables WHERE variable_name='\\2' COLLATE NOCASE UNION ALL SELECT variable_value AS '@@\\2' FROM stats.stats_mysql_global WHERE variable_name='\\2' COLLATE NOCASE"); + free(query); + query_length=new_query->length()+1; + query=(char *)malloc(query_length); + memcpy(query,new_query->c_str(),query_length-1); + query[query_length-1]='\0'; + GloAdmin->stats___mysql_global(); + delete new_query; + goto __run_query; + } + } + { + bool rc; + rc=RE2::PartialMatch(query_no_space,*(RE2 *)(pa->match_regexes.re[2])); + if (rc) { + string *new_query=new std::string(query_no_space); + RE2::Replace(new_query,(char *)"([Ss][Hh][Oo][Ww]\\s+[Vv][Aa][Rr][Ii][Aa][Bb][Ll][Ee][Ss]\\s+[Ww][Hh][Ee][Rr][Ee])",(char *)"SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE"); + free(query); + query_length=new_query->length()+1; + query=(char *)malloc(query_length); + memcpy(query,new_query->c_str(),query_length-1); + query[query_length-1]='\0'; + delete new_query; + goto __run_query; + } + } + { + bool rc; + rc=RE2::PartialMatch(query_no_space,*(RE2 *)(pa->match_regexes.re[3])); + if (rc) { + string *new_query=new std::string(query_no_space); + RE2::Replace(new_query,(char *)"([Ss][Hh][Oo][Ww]\\s+[Vv][Aa][Rr][Ii][Aa][Bb][Ll][Ee][Ss]\\s+[Ll][Ii][Kk][Ee])",(char *)"SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE"); + free(query); + query_length=new_query->length()+1; + query=(char *)malloc(query_length); + memcpy(query,new_query->c_str(),query_length-1); + query[query_length-1]='\0'; + delete new_query; + goto __run_query; + } + } + + if (!strncasecmp("SET ", query_no_space, 4)) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received SET\n"); + run_query = admin_handler_command_set(query_no_space, query_no_space_length, sess, pa, &query, &query_length); + goto __run_query; + } + + if(!strncasecmp("CHECKSUM ", query_no_space, 9)){ + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received CHECKSUM command\n"); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + SQLite3_result *resultset=NULL; + char *tablename=NULL; + char *error=NULL; + int affected_rows=0; + int cols=0; + if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL SERVERS") && !strncasecmp("CHECKSUM DISK MYSQL SERVERS", query_no_space, strlen(query_no_space))){ + char *q=(char *)"SELECT * FROM mysql_servers ORDER BY hostgroup_id, hostname, port"; + tablename=(char *)"MYSQL SERVERS"; + SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL USERS") && !strncasecmp("CHECKSUM DISK MYSQL USERS", query_no_space, strlen(query_no_space))){ + char *q=(char *)"SELECT * FROM mysql_users ORDER BY username"; + tablename=(char *)"MYSQL USERS"; + SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL QUERY RULES") && !strncasecmp("CHECKSUM DISK MYSQL QUERY RULES", query_no_space, strlen(query_no_space))){ + char *q=(char *)"SELECT * FROM mysql_query_rules ORDER BY rule_id"; + tablename=(char *)"MYSQL QUERY RULES"; + SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL VARIABLES") && !strncasecmp("CHECKSUM DISK MYSQL VARIABLES", query_no_space, strlen(query_no_space))){ + char *q=(char *)"SELECT * FROM global_variables WHERE variable_name LIKE 'mysql-%' ORDER BY variable_name"; + tablename=(char *)"MYSQL VARIABLES"; + SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM DISK MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))){ + char *q=(char *)"SELECT * FROM mysql_replication_hostgroups ORDER BY writer_hostgroup"; + tablename=(char *)"MYSQL REPLICATION HOSTGROUPS"; + SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL SERVERS") && !strncasecmp("CHECKSUM MEMORY MYSQL SERVERS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL SERVERS") && !strncasecmp("CHECKSUM MEM MYSQL SERVERS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MYSQL SERVERS") && !strncasecmp("CHECKSUM MYSQL SERVERS", query_no_space, strlen(query_no_space)))){ + char *q=(char *)"SELECT * FROM mysql_servers ORDER BY hostgroup_id, hostname, port"; + tablename=(char *)"MYSQL SERVERS"; + SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL USERS") && !strncasecmp("CHECKSUM MEMORY MYSQL USERS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL USERS") && !strncasecmp("CHECKSUM MEM MYSQL USERS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MYSQL USERS") && !strncasecmp("CHECKSUM MYSQL USERS", query_no_space, strlen(query_no_space)))){ + char *q=(char *)"SELECT * FROM mysql_users ORDER BY username"; + tablename=(char *)"MYSQL USERS"; + SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL QUERY RULES") && !strncasecmp("CHECKSUM MEMORY MYSQL QUERY RULES", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL QUERY RULES") && !strncasecmp("CHECKSUM MEM MYSQL QUERY RULES", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MYSQL QUERY RULES") && !strncasecmp("CHECKSUM MYSQL QUERY RULES", query_no_space, strlen(query_no_space)))){ + char *q=(char *)"SELECT * FROM mysql_query_rules ORDER BY rule_id"; + tablename=(char *)"MYSQL QUERY RULES"; + SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL VARIABLES") && !strncasecmp("CHECKSUM MEMORY MYSQL VARIABLES", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL VARIABLES") && !strncasecmp("CHECKSUM MEM MYSQL VARIABLES", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MYSQL VARIABLES") && !strncasecmp("CHECKSUM MYSQL VARIABLES", query_no_space, strlen(query_no_space)))){ + char *q=(char *)"SELECT * FROM global_variables WHERE variable_name LIKE 'mysql-%' ORDER BY variable_name"; + tablename=(char *)"MYSQL VARIABLES"; + SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space)))){ + char *q=(char *)"SELECT * FROM mysql_replication_hostgroups ORDER BY writer_hostgroup"; + tablename=(char *)"MYSQL REPLICATION HOSTGROUPS"; + SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL GROUP REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL GROUP REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MYSQL GROUP REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space)))){ + char *q=(char *)"SELECT * FROM mysql_group_replication_hostgroups ORDER BY writer_hostgroup"; + tablename=(char *)"MYSQL GROUP REPLICATION HOSTGROUPS"; + SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL GALERA HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL GALERA HOSTGROUPS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL GALERA HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL GALERA HOSTGROUPS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MYSQL GALERA HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL GALERA HOSTGROUPS", query_no_space, strlen(query_no_space)))){ + char *q=(char *)"SELECT * FROM mysql_galera_hostgroups ORDER BY writer_hostgroup"; + tablename=(char *)"MYSQL GALERA HOSTGROUPS"; + SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL AURORA HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL AURORA HOSTGROUPS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL AURORA HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL AURORA HOSTGROUPS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MYSQL AURORA HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL AURORA HOSTGROUPS", query_no_space, strlen(query_no_space)))){ + char *q=(char *)"SELECT * FROM mysql_aws_aurora_hostgroups ORDER BY writer_hostgroup"; + tablename=(char *)"MYSQL AURORA HOSTGROUPS"; + SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL HOSTGROUP ATTRIBUTES") && !strncasecmp("CHECKSUM MEMORY MYSQL HOSTGROUP ATTRIBUTES", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL HOSTGROUP ATTRIBUTES") && !strncasecmp("CHECKSUM MEM MYSQL HOSTGROUP ATTRIBUTES", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MYSQL HOSTGROUP ATTRIBUTES") && !strncasecmp("CHECKSUM MYSQL HOSTGROUP ATTRIBUTES", query_no_space, strlen(query_no_space)))){ + char *q=(char *)"SELECT * FROM mysql_hostgroup_attributes ORDER BY hostgroup_id"; + tablename=(char *)"MYSQL HOSTGROUP ATTRIBUTES"; + SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL SERVERS SSL PARAMS") && !strncasecmp("CHECKSUM MEMORY MYSQL SERVERS SSL PARAMS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL SERVERS SSL PARAMS") && !strncasecmp("CHECKSUM MEM MYSQL SERVERS SSL PARAMS", query_no_space, strlen(query_no_space))) + || + (strlen(query_no_space)==strlen("CHECKSUM MYSQL SERVERS SSL PARAMS") && !strncasecmp("CHECKSUM MYSQL SERVERS SSL PARAMS", query_no_space, strlen(query_no_space)))){ + char *q=(char *)"SELECT * FROM mysql_servers_ssl_params ORDER BY hostname, port, username"; + tablename=(char *)"MYSQL HOSTGROUP ATTRIBUTES"; + SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + } + + if (error) { + proxy_error("Error: %s\n", error); + char buf[1024]; + sprintf(buf,"%s", error); + SPA->send_error_msg_to_client(sess, buf); + run_query=false; + } else if (resultset) { + l_free(query_length,query); + char *q=(char *)"SELECT '%s' AS 'table', '%s' AS 'checksum'"; + char *checksum=(char *)resultset->checksum(); + query=(char *)malloc(strlen(q)+strlen(tablename)+strlen(checksum)+1); + sprintf(query,q,tablename,checksum); + query_length = strlen(query); + free(checksum); + delete resultset; + } + goto __run_query; + } + + if (!strncasecmp("SELECT CONFIG INTO OUTFILE", query_no_space, strlen("SELECT CONFIG INTO OUTFILE"))) { + std::string fileName = query_no_space + strlen("SELECT CONFIG INTO OUTFILE"); + fileName.erase(0, fileName.find_first_not_of("\t\n\v\f\r ")); + fileName.erase(fileName.find_last_not_of("\t\n\v\f\r ") + 1); + if (fileName.size() == 0) { + std::stringstream ss; + ss << "ProxySQL Admin Error: empty file name"; + sess->SQLite3_to_MySQL(resultset, (char*)ss.str().c_str(), affected_rows, &sess->client_myds->myprot); + } + std::string data; + data.reserve(100000); + data += config_header; + int rc = pa->proxysql_config().Write_Global_Variables_to_configfile(data); + rc = pa->proxysql_config().Write_MySQL_Users_to_configfile(data); + rc = pa->proxysql_config().Write_MySQL_Query_Rules_to_configfile(data); + rc = pa->proxysql_config().Write_MySQL_Servers_to_configfile(data); + rc = pa->proxysql_config().Write_PgSQL_Users_to_configfile(data); + rc = pa->proxysql_config().Write_PgSQL_Query_Rules_to_configfile(data); + rc = pa->proxysql_config().Write_PgSQL_Servers_to_configfile(data); + rc = pa->proxysql_config().Write_Scheduler_to_configfile(data); + rc = pa->proxysql_config().Write_Restapi_to_configfile(data); + rc = pa->proxysql_config().Write_ProxySQL_Servers_to_configfile(data); + if (rc) { + std::stringstream ss; + ss << "ProxySQL Admin Error: Cannot extract configuration"; + sess->SQLite3_to_MySQL(resultset, (char*)ss.str().c_str(), affected_rows, &sess->client_myds->myprot); + } else { + std::ofstream out; + out.open(fileName); + if (out.is_open()) { + out << data; + out.close(); + if (!out) { + std::stringstream ss; + ss << "ProxySQL Admin Error: Error writing file " << fileName; + sess->SQLite3_to_MySQL(resultset, (char*)ss.str().c_str(), affected_rows, &sess->client_myds->myprot); + } else { + std::stringstream ss; + ss << "File " << fileName << " is saved."; + SPA->send_ok_msg_to_client(sess, (char*)ss.str().c_str(), data.size(), query_no_space); + } + } else { + std::stringstream ss; + ss << "ProxySQL Admin Error: Cannot open file " << fileName; + sess->SQLite3_to_MySQL(resultset, (char*)ss.str().c_str(), affected_rows, &sess->client_myds->myprot); + } + } + run_query = false; + goto __run_query; + } + + if (query_no_space_length==strlen("SELECT CONFIG FILE") && !strncasecmp("SELECT CONFIG FILE", query_no_space, query_no_space_length)) { + std::string data; + data.reserve(100000); + data += config_header; + int rc = pa->proxysql_config().Write_Global_Variables_to_configfile(data); + rc = pa->proxysql_config().Write_MySQL_Users_to_configfile(data); + rc = pa->proxysql_config().Write_MySQL_Query_Rules_to_configfile(data); + rc = pa->proxysql_config().Write_MySQL_Servers_to_configfile(data); + rc = pa->proxysql_config().Write_PgSQL_Users_to_configfile(data); + rc = pa->proxysql_config().Write_PgSQL_Query_Rules_to_configfile(data); + rc = pa->proxysql_config().Write_PgSQL_Servers_to_configfile(data); + rc = pa->proxysql_config().Write_Scheduler_to_configfile(data); + rc = pa->proxysql_config().Write_Restapi_to_configfile(data); + rc = pa->proxysql_config().Write_ProxySQL_Servers_to_configfile(data); + if (rc) { + std::stringstream ss; + ss << "ProxySQL Admin Error: Cannot write proxysql.cnf"; + sess->SQLite3_to_MySQL(resultset, (char*)ss.str().c_str(), affected_rows, &sess->client_myds->myprot); + } else { + char *pta[1]; + pta[0]=NULL; + pta[0]=(char*)data.c_str(); + SQLite3_result* resultset = new SQLite3_result(1); + resultset->add_column_definition(SQLITE_TEXT,"Data"); + resultset->add_row(pta); + sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); + delete resultset; + } + run_query = false; + goto __run_query; + } + + if (strncasecmp("SHOW ", query_no_space, 5)) { + goto __end_show_commands; // in the next block there are only SHOW commands + } + + if (!strncasecmp("SHOW PROMETHEUS METRICS", query_no_space, strlen("SHOW PROMETHEUS METRICS"))) { + char* pta[1]; + pta[0] = NULL; + SQLite3_result* resultset = new SQLite3_result(1); + resultset->add_column_definition(SQLITE_TEXT,"Data"); + + if (__sync_fetch_and_add(&GloMTH->status_variables.threads_initialized, 0) == 1) { + auto result = pa->serial_exposer({}); + pta[0] = (char*)result.second.c_str(); + resultset->add_row(pta); + } else { + resultset->add_row(pta); + } + + sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); + delete resultset; + run_query = false; + + goto __run_query; + } + + if (!strncasecmp("SHOW GLOBAL VARIABLES LIKE 'version'", query_no_space, strlen("SHOW GLOBAL VARIABLES LIKE 'version'"))) { + l_free(query_length,query); + char *q=(char *)"SELECT 'version' Variable_name, '%s' Value FROM global_variables WHERE Variable_name='admin-version'"; + query_length=strlen(q)+20+strlen(PROXYSQL_VERSION); + query=(char *)l_alloc(query_length); + sprintf(query,q,PROXYSQL_VERSION); + goto __run_query; + } + + + if (query_no_space_length==strlen("SHOW TABLES") && !strncasecmp("SHOW TABLES",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT name AS tables FROM sqlite_master WHERE type='table' AND name NOT IN ('sqlite_sequence') ORDER BY name"); + query_length=strlen(query)+1; + goto __run_query; + } + + if (query_no_space_length==strlen("SHOW CHARSET") && !strncasecmp("SHOW CHARSET",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT Charset, Collation AS 'Default collation' FROM mysql_collations WHERE `Default`='Yes'"); + query_length=strlen(query)+1; + goto __run_query; + } + + if (query_no_space_length==strlen("SHOW COLLATION") && !strncasecmp("SHOW COLLATION",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT * FROM mysql_collations"); + query_length=strlen(query)+1; + goto __run_query; + } + + if ((query_no_space_length>15) && (!strncasecmp("SHOW TABLES IN ", query_no_space, 15))) { + strA=query_no_space+15; + strAl=strlen(strA); + strB=(char *)"SELECT name AS tables FROM %s.sqlite_master WHERE type='table' AND name NOT IN ('sqlite_sequence') ORDER BY name"; + strBl=strlen(strB); + int l=strBl+strAl-2; + char *b=(char *)l_alloc(l+1); + snprintf(b,l+1,strB,strA); + b[l]=0; + l_free(query_length,query); + query=b; + query_length=l+1; + goto __run_query; + } + + if ((query_no_space_length>17) && (!strncasecmp("SHOW TABLES FROM ", query_no_space, 17))) { + strA=query_no_space+17; + strAl=strlen(strA); + strB=(char *)"SELECT name AS tables FROM %s.sqlite_master WHERE type='table' AND name NOT IN ('sqlite_sequence') ORDER BY name"; + strBl=strlen(strB); + int l=strBl+strAl-2; + char *b=(char *)l_alloc(l+1); + snprintf(b,l+1,strB,strA); + b[l]=0; + l_free(query_length,query); + query=b; + query_length=l+1; + goto __run_query; + } + + if ((query_no_space_length>17) && (!strncasecmp("SHOW TABLES LIKE ", query_no_space, 17))) { + strA=query_no_space+17; + strAl=strlen(strA); + strB=(char *)"SELECT name AS tables FROM sqlite_master WHERE type='table' AND name LIKE '%s'"; + strBl=strlen(strB); + char *tn=NULL; // tablename + tn=(char *)malloc(strAl+1); + unsigned int i=0, j=0; + while (i<(unsigned int)strAl) { + if (strA[i]!='\\' && strA[i]!='`' && strA[i]!='\'') { + tn[j]=strA[i]; + j++; + } + i++; + } + tn[j]=0; + int l=strBl+strlen(tn)-2; + char *b=(char *)l_alloc(l+1); + snprintf(b,l+1,strB,tn); + b[l]=0; + free(tn); + l_free(query_length,query); + query=b; + query_length=l+1; + goto __run_query; + } + + if (query_no_space_length==strlen("SHOW MYSQL USERS") && !strncasecmp("SHOW MYSQL USERS",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT * FROM mysql_users ORDER BY username, active DESC, username ASC"); + query_length=strlen(query)+1; + goto __run_query; + } + + if (query_no_space_length==strlen("SHOW MYSQL SERVERS") && !strncasecmp("SHOW MYSQL SERVERS",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT * FROM mysql_servers ORDER BY hostgroup_id, hostname, port"); + query_length=strlen(query)+1; + goto __run_query; + } + + if ( + (query_no_space_length==strlen("SHOW GLOBAL VARIABLES") && !strncasecmp("SHOW GLOBAL VARIABLES",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SHOW ALL VARIABLES") && !strncasecmp("SHOW ALL VARIABLES",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SHOW VARIABLES") && !strncasecmp("SHOW VARIABLES",query_no_space, query_no_space_length)) + ) { + l_free(query_length,query); + query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables ORDER BY variable_name"); + query_length=strlen(query)+1; + goto __run_query; + } + + if (GloMyLdapAuth) { + if (query_no_space_length==strlen("SHOW LDAP VARIABLES") && !strncasecmp("SHOW LDAP VARIABLES",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'ldap-\%' ORDER BY variable_name"); + query_length=strlen(query)+1; + goto __run_query; + } + } + + if (query_no_space_length==strlen("SHOW ADMIN VARIABLES") && !strncasecmp("SHOW ADMIN VARIABLES",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'admin-\%' ORDER BY variable_name"); + query_length=strlen(query)+1; + goto __run_query; + } + + if (query_no_space_length==strlen("SHOW MYSQL VARIABLES") && !strncasecmp("SHOW MYSQL VARIABLES",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'mysql-\%' ORDER BY variable_name"); + query_length=strlen(query)+1; + goto __run_query; + } + + if (query_no_space_length==strlen("SHOW MYSQL STATUS") && !strncasecmp("SHOW MYSQL STATUS",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT Variable_Name AS Variable_name, Variable_Value AS Value FROM stats_mysql_global ORDER BY variable_name"); + query_length=strlen(query)+1; + GloAdmin->stats___mysql_global(); + goto __run_query; + } + + strA=(char *)"SHOW CREATE TABLE "; + strB=(char *)"SELECT name AS 'table' , REPLACE(REPLACE(sql,' , ', X'2C0A20202020'),'CREATE TABLE %s (','CREATE TABLE %s ('||X'0A20202020') AS 'Create Table' FROM %s.sqlite_master WHERE type='table' AND name='%s'"; + strAl=strlen(strA); + if (strncasecmp("SHOW CREATE TABLE ", query_no_space, strAl)==0) { + strBl=strlen(strB); + char *dbh=NULL; + char *tbh=NULL; + c_split_2(query_no_space+strAl,".",&dbh,&tbh); + + if (strlen(tbh)==0) { + free(tbh); + tbh=dbh; + dbh=strdup("main"); + } + if (strlen(tbh)>=3 && tbh[0]=='`' && tbh[strlen(tbh)-1]=='`') { // tablename is quoted + char *tbh_tmp=(char *)malloc(strlen(tbh)-1); + strncpy(tbh_tmp,tbh+1,strlen(tbh)-2); + tbh_tmp[strlen(tbh)-2]=0; + free(tbh); + tbh=tbh_tmp; + } + int l=strBl+strlen(tbh)*3+strlen(dbh)-8; + char *buff=(char *)l_alloc(l+1); + snprintf(buff,l+1,strB,tbh,tbh,dbh,tbh); + buff[l]=0; + free(tbh); + free(dbh); + l_free(query_length,query); + query=buff; + query_length=l+1; + goto __run_query; + } + + if ( + (query_no_space_length==strlen("SHOW DATABASES") && !strncasecmp("SHOW DATABASES",query_no_space, query_no_space_length)) + || + (query_no_space_length==strlen("SHOW SCHEMAS") && !strncasecmp("SHOW SCHEMAS",query_no_space, query_no_space_length)) + ) { + l_free(query_length,query); + query=l_strdup("PRAGMA DATABASE_LIST"); + query_length=strlen(query)+1; + goto __run_query; + } + + if (query_no_space_length==strlen("SHOW FULL PROCESSLIST") && !strncasecmp("SHOW FULL PROCESSLIST",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT * FROM stats_mysql_processlist"); + query_length=strlen(query)+1; + goto __run_query; + } + + if (query_no_space_length==strlen("SHOW PROCESSLIST") && !strncasecmp("SHOW PROCESSLIST",query_no_space, query_no_space_length)) { + l_free(query_length,query); + query=l_strdup("SELECT SessionID, user, db, hostgroup, command, time_ms, SUBSTR(info,0,100) info FROM stats_mysql_processlist"); + query_length=strlen(query)+1; + goto __run_query; + } + +__end_show_commands: + + if (query_no_space_length==strlen("SELECT DATABASE()") && !strncasecmp("SELECT DATABASE()",query_no_space, query_no_space_length)) { + l_free(query_length,query); + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + query=l_strdup("SELECT \"admin\" AS 'DATABASE()'"); + } else { + query=l_strdup("SELECT \"stats\" AS 'DATABASE()'"); + } + query_length=strlen(query)+1; + goto __run_query; + } + + // see issue #1022 + if (query_no_space_length==strlen("SELECT DATABASE() AS name") && !strncasecmp("SELECT DATABASE() AS name",query_no_space, query_no_space_length)) { + l_free(query_length,query); + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + query=l_strdup("SELECT \"admin\" AS 'name'"); + } else { + query=l_strdup("SELECT \"stats\" AS 'name'"); + } + query_length=strlen(query)+1; + goto __run_query; + } + + if (sess->session_type == PROXYSQL_SESSION_STATS) { // no admin + if ( + (strncasecmp("PRAGMA",query_no_space,6)==0) + || + (strncasecmp("ATTACH",query_no_space,6)==0) + ) { + proxy_error("[WARNING]: Commands executed from stats interface in Admin Module: \"%s\"\n", query_no_space); + SPA->send_error_msg_to_client(sess, (char *)"Command not allowed"); + run_query=false; + } + } + +__run_query: + if (sess->proxysql_node_address && (__sync_fetch_and_add(&glovars.shutdown,0)==0)) { + if (sess->client_myds->active) { + const string uuid { sess->proxysql_node_address->uuid }; + const string hostname { sess->proxysql_node_address->hostname }; + const string port { std::to_string(sess->proxysql_node_address->port) }; + const string mysql_ifaces { sess->proxysql_node_address->admin_mysql_ifaces }; + + time_t now = time(NULL); + string q = "INSERT OR REPLACE INTO stats_proxysql_servers_clients_status (uuid, hostname, port, admin_mysql_ifaces, last_seen_at) VALUES (\""; + q += uuid; + q += "\",\""; + q += hostname; + q += "\","; + q += port; + q += ",\""; + q += mysql_ifaces; + q += "\","; + q += std::to_string(now) + ")"; + SPA->statsdb->execute(q.c_str()); + + std::map m_labels { { "uuid", uuid }, { "hostname", hostname }, { "port", port } }; + const string m_id { uuid + ":" + hostname + ":" + port }; + + p_update_map_gauge( + SPA->metrics.p_proxysql_servers_clients_status_map, + SPA->metrics.p_dyn_gauge_array[p_admin_dyn_gauge::proxysql_servers_clients_status_last_seen_at], + m_id, m_labels, now + ); + } + } + if (run_query) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + if (SPA->get_read_only()) { // disable writes if the admin interface is in read_only mode + SPA->admindb->execute("PRAGMA query_only = ON"); + SPA->admindb->execute_statement(query, &error , &cols , &affected_rows , &resultset); + SPA->admindb->execute("PRAGMA query_only = OFF"); + } else { + SPA->admindb->execute_statement(query, &error , &cols , &affected_rows , &resultset); + } + if (needs_vacuum) { + SPA->vacuum_stats(true); + } + } else { + SPA->statsdb->execute("PRAGMA query_only = ON"); + SPA->statsdb->execute_statement(query, &error , &cols , &affected_rows , &resultset); + SPA->statsdb->execute("PRAGMA query_only = OFF"); + if (needs_vacuum) { + SPA->vacuum_stats(false); + } + } + if (error == NULL) { + + if constexpr (std::is_same::value) { + sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); + } + else if constexpr (std::is_same::value) { + SQLite3_to_Postgres(sess->client_myds->PSarrayOUT, resultset, error, affected_rows, query); + } + + } else { + char *a = (char *)"ProxySQL Admin Error: "; + char *new_msg = (char *)malloc(strlen(error)+strlen(a)+1); + sprintf(new_msg, "%s%s", a, error); + + if constexpr (std::is_same::value) { + sess->SQLite3_to_MySQL(resultset, new_msg, affected_rows, &sess->client_myds->myprot); + } else if constexpr (std::is_same::value) { + SQLite3_to_Postgres(sess->client_myds->PSarrayOUT, resultset, new_msg, affected_rows, query); + } + + free(new_msg); + free(error); + } + delete resultset; + } + if (run_query == true) { + pthread_mutex_unlock(&pa->sql_query_global_mutex); + } else { + // The admin module may have already been freed in case of "PROXYSQL STOP" + if (strcasecmp("PROXYSQL STOP",query_no_space)) + pthread_mutex_unlock(&pa->sql_query_global_mutex); + } + l_free(pkt->size-sizeof(mysql_hdr),query_no_space); // it is always freed here + l_free(query_length,query); +} + +// Explicitly instantiate the required template class and member functions +template void admin_session_handler(Client_Session sess, void *_pa, PtrSize_t *pkt); +template void admin_session_handler(Client_Session sess, void *_pa, PtrSize_t *pkt); + diff --git a/lib/Makefile b/lib/Makefile index d5be75c7a..3237c3b26 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -142,6 +142,7 @@ _OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo GTID_Server_Data.oo MyHGC.oo MySrvConnList.oo MySrvList.oo MySrvC.oo \ MySQL_encode.oo MySQL_ResultSet.oo \ ProxySQL_Admin_Tests.oo ProxySQL_Admin_Tests2.oo ProxySQL_Admin_Scheduler.oo ProxySQL_Admin_Disk_Upgrade.oo ProxySQL_Admin_Stats.oo \ + Admin_Handler.oo Admin_FlushVariables.oo Admin_Bootstrap.oo \ Base_Session.oo Base_Thread.oo \ proxysql_find_charset.oo ProxySQL_Poll.oo sha256crypt.oo PgSQL_Protocol.oo PgSQL_Thread.oo Client_Session.oo PgSQL_Data_Stream.oo PgSQL_Session.oo PgSQL_Variables.oo PgSQL_HostGroups_Manager.oo PgSQL_Connection.oo PgSQL_Backend.oo PgSQL_Logger.oo PgSQL_Authentication.oo PgSQL_Error_Helper.oo diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index ebf3adc23..61de0b506 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -293,14 +293,14 @@ char *s_strdup(char *s) { return ret; } -static volatile int load_main_=0; -static volatile bool nostart_=false; +int admin_load_main_=0; +bool admin_nostart_=false; -static int __admin_refresh_interval=0; +int __admin_refresh_interval=0; -bool proxysql_mysql_paused = false; -bool proxysql_pgsql_paused = false; -static int old_wait_timeout; +bool admin_proxysql_mysql_paused = false; +bool admin_proxysql_pgsql_paused = false; +int admin_old_wait_timeout; extern Query_Cache *GloQC; extern MySQL_Authentication *GloMyAuth; @@ -682,9 +682,9 @@ admin_metrics_map = std::make_tuple( } ); -static ProxySQL_Admin *SPA=NULL; +ProxySQL_Admin *SPA=NULL; -static void * (*child_func[3]) (void *arg); +void * (*child_func[3]) (void *arg); const std::vector LOAD_ADMIN_VARIABLES_TO_MEMORY = { @@ -788,7 +788,7 @@ const std::vector LOAD_COREDUMP_FROM_MEMORY = { "LOAD COREDUMP TO RUNTIME" , "LOAD COREDUMP TO RUN" }; -static unordered_map, vector>> load_save_disk_commands; +unordered_map, vector>> load_save_disk_commands; static void generate_load_save_disk_commands(std::vector& vec1, std::vector& vec2, const string& name) { string s; @@ -813,48 +813,7 @@ static void generate_load_save_disk_commands(const string& name, const string& c } -bool is_admin_command_or_alias(const std::vector& cmds, char *query_no_space, int query_no_space_length) { - for (std::vector::const_iterator it=cmds.begin(); it!=cmds.end(); ++it) { - if ((unsigned int)query_no_space_length==it->length() && !strncasecmp(it->c_str(), query_no_space, query_no_space_length)) { - proxy_info("Received %s command\n", query_no_space); - return true; - } - } - return false; -} - -template -bool FlushCommandWrapper(Client_Session sess, const std::vector& cmds, char *query_no_space, int query_no_space_length, const string& name, const string& direction) { - if ( is_admin_command_or_alias(cmds, query_no_space, query_no_space_length) ) { - ProxySQL_Admin *SPA = GloAdmin; - SPA->flush_GENERIC__from_to(name, direction); -#ifdef DEBUG - string msg = "Loaded " + name + " "; - if (direction == "memory_to_disk") - msg += "from MEMORY to DISK"; - else if (direction == "disk_to_memory") - msg += "from DISK to MEMORY"; - else - assert(0); - msg += "\n"; - proxy_debug(PROXY_DEBUG_ADMIN, 4, "%s", msg.c_str()); -#endif // DEBUG - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return true; - } - return false; -} - -template -bool FlushCommandWrapper(Client_Session sess, const string& modname, char *query_no_space, int query_no_space_length) { - assert(load_save_disk_commands.find(modname) != load_save_disk_commands.end()); - tuple, vector>& t = load_save_disk_commands[modname]; - if (FlushCommandWrapper(sess, get<1>(t), query_no_space, query_no_space_length, modname, "disk_to_memory") == true) - return true; - if (FlushCommandWrapper(sess, get<2>(t), query_no_space, query_no_space_length, modname, "memory_to_disk") == true) - return true; - return false; -} +bool is_admin_command_or_alias(const std::vector& cmds, char *query_no_space, int query_no_space_length); incoming_servers_t::incoming_servers_t() {} @@ -914,15 +873,6 @@ incoming_pgsql_servers_t::incoming_pgsql_servers_t( {} -bootstrap_info_t::~bootstrap_info_t() { - if (servers != nullptr) { - mysql_free_result(servers); - } - if (users != nullptr) { - mysql_free_result(users); - } -} - peer_runtime_mysql_servers_t::peer_runtime_mysql_servers_t() : resultset(nullptr), checksum() {} peer_runtime_mysql_servers_t::peer_runtime_mysql_servers_t( @@ -1111,154 +1061,10 @@ int ProxySQL_Admin::FlushDigestTableToDisk(SQLite3DB *_db) { return r; } -typedef struct _main_args { - int nfds; - struct pollfd *fds; - int *callback_func; - volatile int *shutdown; -} main_args; - -typedef struct _ifaces_desc_t { - char **mysql_ifaces; - char **pgsql_ifaces; - char **telnet_admin_ifaces; - char **telnet_stats_ifaces; -} ifaces_desc_t; - -#define MAX_IFACES 8 -#define MAX_ADMIN_LISTENERS 16 - -class ifaces_desc { - public: - PtrArray *ifaces; - ifaces_desc() { - ifaces=new PtrArray(); - } - bool add(const char *iface) { - for (unsigned int i=0; ilen; i++) { - if (strcmp((const char *)ifaces->index(i),iface)==0) { - return false; - } - } - ifaces->add(strdup(iface)); - return true; - } - ~ifaces_desc() { - while(ifaces->len) { - char *d=(char *)ifaces->remove_index_fast(0); - free(d); - } - delete ifaces; - } -}; - -class admin_main_loop_listeners { - private: - int version; -#ifdef PA_PTHREAD_MUTEX - pthread_rwlock_t rwlock; -#else - rwlock_t rwlock; -#endif - - char ** reset_ifaces(char **ifaces) { - int i; - if (ifaces) { - for (i=0; iadd(token); - i++; - } - free_tokenizer( &tok ); - version++; - wrunlock(); - } - - - bool update_ifaces(char *list, char ***_ifaces) { - wrlock(); - int i; - char **ifaces=*_ifaces; - tokenizer_t tok; - tokenizer( &tok, list, ";", TOKENIZER_NO_EMPTIES ); - const char* token; - ifaces=reset_ifaces(ifaces); - i=0; - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - ifaces[i]=(char *)malloc(strlen(token)+1); - strcpy(ifaces[i],token); - i++; - } - free_tokenizer( &tok ); - version++; - wrunlock(); - return true; - } -}; +admin_main_loop_listeners S_amll; -static admin_main_loop_listeners S_amll; template bool admin_handler_command_kill_connection(char *query_no_space, unsigned int query_no_space_length, Client_Session& sess, ProxySQL_Admin *pa) { @@ -1276,7 +1082,7 @@ bool admin_handler_command_kill_connection(char *query_no_space, unsigned int qu return false; } -static void flush_logs_handler() { +void flush_logs_handler() { GloAdmin->flush_logs(); } @@ -1299,7101 +1105,1997 @@ void ProxySQL_Admin::flush_logs() { } } -/* - * returns false if the command is a valid one and is processed - * return true if the command is not a valid one and needs to be executed by SQLite (that will return an error) - */ -template -bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_space_length, Client_Session& sess, ProxySQL_Admin *pa) { - -#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__)) && defined(__linux) - // currently only support x86-32, x86-64, ARM, and MIPS on Linux - if (!(strncasecmp("PROXYSQL COREDUMP", query_no_space, strlen("PROXYSQL COREDUMP")))) { - string filename = "core"; - if (query_no_space_length > strlen("PROXYSQL COREDUMP")) { - if (query_no_space[strlen("PROXYSQL COREDUMP")] == ' ') { - filename = string(query_no_space+strlen("PROXYSQL COREDUMP ")); - } else { - filename = ""; - } - } - if (filename == "") { - proxy_error("Received incorrect PROXYSQL COREDUMP command: %s\n", query_no_space); - } else { - proxy_info("Received PROXYSQL COREDUMP command: %s\n", query_no_space); - // generates a core dump - WriteCoreDump(filename.c_str()); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - string msg = "Coredump: " + filename; - SPA->send_ok_msg_to_client(sess, (char *)msg.c_str(), 0, query_no_space); - return false; - } - } - if (!(strncasecmp("PROXYSQL COMPRESSEDCOREDUMP", query_no_space, strlen("PROXYSQL COMPRESSEDCOREDUMP")))) { - string filename = "core"; - if (query_no_space_length > strlen("PROXYSQL COMPRESSEDCOREDUMP")) { - if (query_no_space[strlen("PROXYSQL COMPRESSEDCOREDUMP")] == ' ') { - filename = string(query_no_space+strlen("PROXYSQL COMPRESSEDCOREDUMP ")); - } else { - filename = ""; - } - } - if (filename == "") { - proxy_error("Received incorrect PROXYSQL COMPRESSEDCOREDUMP command: %s\n", query_no_space); - } else { - proxy_info("Received PROXYSQL COMPRESSEDCOREDUMP command: %s\n", query_no_space); - // generates a compressed core dump - WriteCompressedCoreDump(filename.c_str(), SIZE_MAX, COREDUMPER_COMPRESSED, NULL); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - string msg = "Coredump: " + filename; - SPA->send_ok_msg_to_client(sess, (char *)msg.c_str(), 0, query_no_space); - return false; - } - } -#endif - - if (!(strncasecmp("PROXYSQL CLUSTER_NODE_UUID ", query_no_space, strlen("PROXYSQL CLUSTER_NODE_UUID ")))) { - int l = strlen("PROXYSQL CLUSTER_NODE_UUID "); - if (sess->client_myds->addr.port == 0) { - proxy_warning("Received PROXYSQL CLUSTER_NODE_UUID not from TCP socket. Exiting client\n"); - SPA->send_error_msg_to_client(sess, (char *)"Received PROXYSQL CLUSTER_NODE_UUID not from TCP socket"); - sess->client_myds->shut_soft(); - return false; - } - if (query_no_space_length >= (unsigned int)l+36+2) { - uuid_t uu; - char *A_uuid = NULL; - char *B_interface = NULL; - c_split_2(query_no_space+l, " ", &A_uuid, &B_interface); // we split the value - if (uuid_parse(A_uuid, uu)==0 && B_interface && strlen(B_interface)) { - proxy_info("Received PROXYSQL CLUSTER_NODE_UUID from %s:%d : %s\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); - if (sess->proxysql_node_address==NULL) { - sess->proxysql_node_address = new ProxySQL_Node_Address(sess->client_myds->addr.addr, sess->client_myds->addr.port); - sess->proxysql_node_address->uuid = strdup(A_uuid); - if (sess->proxysql_node_address->admin_mysql_ifaces) { - free(sess->proxysql_node_address->admin_mysql_ifaces); - } - sess->proxysql_node_address->admin_mysql_ifaces = strdup(B_interface); - proxy_info("Created new link with Cluster node %s:%d : %s at interface %s\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, A_uuid, B_interface); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - free(A_uuid); - free(B_interface); - return false; - } else { - if (strcmp(A_uuid, sess->proxysql_node_address->uuid)) { - proxy_error("Cluster node %s:%d is sending a new UUID : %s . Former UUID : %s . Exiting client\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, A_uuid, sess->proxysql_node_address->uuid); - SPA->send_error_msg_to_client(sess, (char *)"Received PROXYSQL CLUSTER_NODE_UUID with a new UUID not matching the previous one"); - sess->client_myds->shut_soft(); - free(A_uuid); - free(B_interface); - return false; - } else { - proxy_info("Cluster node %s:%d is sending again its UUID : %s\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, A_uuid); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - free(A_uuid); - free(B_interface); - return false; - } - } - free(A_uuid); - free(B_interface); - return false; - } else { - proxy_warning("Received PROXYSQL CLUSTER_NODE_UUID from %s:%d with invalid format: %s . Exiting client\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); - SPA->send_error_msg_to_client(sess, (char *)"Received PROXYSQL CLUSTER_NODE_UUID with invalid format"); - sess->client_myds->shut_soft(); - return false; - } - } else { - proxy_warning("Received PROXYSQL CLUSTER_NODE_UUID from %s:%d with invalid format: %s . Exiting client\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); - SPA->send_error_msg_to_client(sess, (char *)"Received PROXYSQL CLUSTER_NODE_UUID with invalid format"); - sess->client_myds->shut_soft(); - return false; - } - } - if (query_no_space_length==strlen("PROXYSQL READONLY") && !strncasecmp("PROXYSQL READONLY",query_no_space, query_no_space_length)) { - // this command enables admin_read_only , so the admin module is in read_only mode - proxy_info("Received PROXYSQL READONLY command\n"); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->set_read_only(true); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - if (query_no_space_length==strlen("PROXYSQL READWRITE") && !strncasecmp("PROXYSQL READWRITE",query_no_space, query_no_space_length)) { - // this command disables admin_read_only , so the admin module won't be in read_only mode - proxy_info("Received PROXYSQL WRITE command\n"); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->set_read_only(false); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - if (query_no_space_length==strlen("PROXYSQL START") && !strncasecmp("PROXYSQL START",query_no_space, query_no_space_length)) { - proxy_info("Received PROXYSQL START command\n"); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - bool rc=false; - if (nostart_) { - rc=__sync_bool_compare_and_swap(&GloVars.global.nostart,1,0); - } - if (rc) { - // Set the status variable 'threads_initialized' to 0 because it's initialized back - // in main 'init_phase3'. After GloMTH have been initialized again. - __sync_bool_compare_and_swap(&GloMTH->status_variables.threads_initialized, 1, 0); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Starting ProxySQL following PROXYSQL START command\n"); - while(__sync_fetch_and_add(&GloMTH->status_variables.threads_initialized, 0) == 1) { - usleep(1000); - } - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - } else { - proxy_warning("ProxySQL was already started when received PROXYSQL START command\n"); - SPA->send_error_msg_to_client(sess, (char *)"ProxySQL already started"); - } - return false; - } - - if (query_no_space_length==strlen("PROXYSQL RESTART") && !strncasecmp("PROXYSQL RESTART",query_no_space, query_no_space_length)) { - proxy_info("Received PROXYSQL RESTART command\n"); - // This function was introduced into 'prometheus::Registry' for being - // able to do a complete reset of all the 'prometheus counters'. It - // shall only be used during ProxySQL shutdown phases. - GloVars.prometheus_registry->ResetCounters(); - __sync_bool_compare_and_swap(&glovars.shutdown,0,1); - glovars.reload=1; - return false; - } - - if (query_no_space_length==strlen("PROXYSQL STOP") && !strncasecmp("PROXYSQL STOP",query_no_space, query_no_space_length)) { - proxy_info("Received PROXYSQL STOP command\n"); - // to speed up this process we first change wait_timeout to 0 - // MySQL_thread will call poll() with a maximum timeout of 100ms - old_wait_timeout=GloMTH->get_variable_int((char *)"wait_timeout"); - GloMTH->set_variable((char *)"wait_timeout",(char *)"0"); - GloMTH->commit(); - GloMTH->signal_all_threads(0); - GloMTH->stop_listeners(); - char buf[32]; - sprintf(buf,"%d",old_wait_timeout); - GloMTH->set_variable((char *)"wait_timeout",buf); - GloMTH->commit(); - glovars.reload=2; - // This function was introduced into 'prometheus::Registry' for being - // able to do a complete reset of all the 'prometheus counters'. It - // shall only be used during ProxySQL shutdown phases. - GloVars.prometheus_registry->ResetCounters(); - __sync_bool_compare_and_swap(&glovars.shutdown,0,1); - // After setting the shutdown flag, we should wake all threads and wait for - // the shutdown phase to complete. - GloMTH->signal_all_threads(0); - while (__sync_fetch_and_add(&glovars.shutdown,0)==1) { - usleep(1000); - } - // After shutdown phase is completed, we must to send a 'OK' to the - // mysql client, otherwise, since this session might not be drop due - // to the waiting condition, the client wont disconnect and will - // keep forever waiting for acknowledgement. - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - if (query_no_space_length==strlen("PROXYSQL PAUSE") && !strncasecmp("PROXYSQL PAUSE",query_no_space, query_no_space_length)) { - proxy_info("Received PROXYSQL PAUSE command\n"); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - if (nostart_) { - if (__sync_fetch_and_add((uint8_t *)(&GloVars.global.nostart),0)) { - SPA->send_error_msg_to_client(sess, (char *)"ProxySQL MySQL module not running, impossible to pause"); - return false; - } - } - if (proxysql_mysql_paused==false) { - // to speed up this process we first change poll_timeout to 10 - // MySQL_thread will call poll() with a maximum timeout of 10ms - old_wait_timeout=GloMTH->get_variable_int((char *)"poll_timeout"); - GloMTH->set_variable((char *)"poll_timeout",(char *)"10"); - GloMTH->commit(); - GloMTH->signal_all_threads(0); - GloMTH->stop_listeners(); - proxysql_mysql_paused=true; - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - // we now rollback poll_timeout - char buf[32]; - sprintf(buf,"%d",old_wait_timeout); - GloMTH->set_variable((char *)"poll_timeout",buf); - GloMTH->commit(); - } else { - SPA->send_error_msg_to_client(sess, (char *)"ProxySQL MySQL module is already paused, impossible to pause"); - } - - if (proxysql_pgsql_paused == false) { - // to speed up this process we first change poll_timeout to 10 - // PgSQL_thread will call poll() with a maximum timeout of 10ms - old_wait_timeout = GloPTH->get_variable_int((char*)"poll_timeout"); - GloPTH->set_variable((char*)"poll_timeout", (char*)"10"); - GloPTH->commit(); - GloPTH->signal_all_threads(0); - GloPTH->stop_listeners(); - proxysql_pgsql_paused = true; - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - // we now rollback poll_timeout - char buf[32]; - sprintf(buf, "%d", old_wait_timeout); - GloPTH->set_variable((char*)"poll_timeout", buf); - GloPTH->commit(); - } - else { - SPA->send_error_msg_to_client(sess, (char*)"ProxySQL PgSQL module is already paused, impossible to pause"); - } - return false; - } +// Explicitly instantiate the required template class and member functions +template void ProxySQL_Admin::send_ok_msg_to_client(Client_Session&, char const*, int, char const*); +template void ProxySQL_Admin::send_ok_msg_to_client(Client_Session&, char const*, int, char const*); +template void ProxySQL_Admin::send_error_msg_to_client(Client_Session&, char const*, unsigned short); +template void ProxySQL_Admin::send_error_msg_to_client(Client_Session&, char const*, unsigned short); +template int ProxySQL_Admin::FlushDigestTableToDisk<(SERVER_TYPE)0>(SQLite3DB*); +template int ProxySQL_Admin::FlushDigestTableToDisk<(SERVER_TYPE)1>(SQLite3DB*); - if (query_no_space_length==strlen("PROXYSQL RESUME") && !strncasecmp("PROXYSQL RESUME",query_no_space, query_no_space_length)) { - proxy_info("Received PROXYSQL RESUME command\n"); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - if (nostart_) { - if (__sync_fetch_and_add((uint8_t *)(&GloVars.global.nostart),0)) { - SPA->send_error_msg_to_client(sess, (char *)"ProxySQL MySQL module not running, impossible to resume"); - return false; - } - } - if (proxysql_mysql_paused==true) { - // to speed up this process we first change poll_timeout to 10 - // MySQL_thread will call poll() with a maximum timeout of 10ms - old_wait_timeout=GloMTH->get_variable_int((char *)"poll_timeout"); - GloMTH->set_variable((char *)"poll_timeout",(char *)"10"); - GloMTH->commit(); - GloMTH->signal_all_threads(0); - GloMTH->start_listeners(); - //char buf[32]; - //sprintf(buf,"%d",old_wait_timeout); - //GloMTH->set_variable((char *)"poll_timeout",buf); - //GloMTH->commit(); - proxysql_mysql_paused=false; - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - // we now rollback poll_timeout - char buf[32]; - sprintf(buf,"%d",old_wait_timeout); - GloMTH->set_variable((char *)"poll_timeout",buf); - GloMTH->commit(); - } else { - SPA->send_error_msg_to_client(sess, (char *)"ProxySQL MySQL module is not paused, impossible to resume"); - } - - if (proxysql_pgsql_paused == true) { - // to speed up this process we first change poll_timeout to 10 - // MySQL_thread will call poll() with a maximum timeout of 10ms - old_wait_timeout = GloPTH->get_variable_int((char*)"poll_timeout"); - GloPTH->set_variable((char*)"poll_timeout", (char*)"10"); - GloPTH->commit(); - GloPTH->signal_all_threads(0); - GloPTH->start_listeners(); - //char buf[32]; - //sprintf(buf,"%d",old_wait_timeout); - //GloPTH->set_variable((char *)"poll_timeout",buf); - //GloPTH->commit(); - proxysql_pgsql_paused = false; - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - // we now rollback poll_timeout - char buf[32]; - sprintf(buf, "%d", old_wait_timeout); - GloPTH->set_variable((char*)"poll_timeout", buf); - GloPTH->commit(); - } - else { - SPA->send_error_msg_to_client(sess, (char*)"ProxySQL MySQL module is not paused, impossible to resume"); - } - return false; - } +void ProxySQL_Admin::flush_configdb() { // see #923 + wrlock(); + admindb->execute((char *)"DETACH DATABASE disk"); + delete configdb; + configdb=new SQLite3DB(); + configdb->open((char *)GloVars.admindb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); + __attach_db(admindb, configdb, (char *)"disk"); + // Fully synchronous is not required. See to #1055 + // https://sqlite.org/pragma.html#pragma_synchronous + configdb->execute("PRAGMA synchronous=0"); + wrunlock(); +} - if (query_no_space_length==strlen("PROXYSQL SHUTDOWN SLOW") && !strncasecmp("PROXYSQL SHUTDOWN SLOW",query_no_space, query_no_space_length)) { - glovars.proxy_restart_on_error=false; - glovars.reload=0; - proxy_info("Received PROXYSQL SHUTDOWN SLOW command\n"); - __sync_bool_compare_and_swap(&glovars.shutdown,0,1); - return false; - } +bool ProxySQL_Admin::GenericRefreshStatistics(const char *query_no_space, unsigned int query_no_space_length, bool admin) { + bool ret=false; + bool refresh=false; + bool stats_mysql_processlist=false; + bool stats_mysql_free_connections=false; + bool stats_mysql_connection_pool=false; + bool stats_mysql_connection_pool_reset=false; + bool stats_mysql_query_digest=false; + bool stats_mysql_query_digest_reset=false; + bool stats_mysql_errors=false; + bool stats_mysql_errors_reset=false; + bool stats_mysql_global=false; + bool stats_memory_metrics=false; + bool stats_mysql_commands_counters=false; + bool stats_mysql_query_rules=false; + bool stats_mysql_users=false; + bool stats_mysql_gtid_executed=false; + bool stats_mysql_client_host_cache=false; + bool stats_mysql_client_host_cache_reset=false; + bool dump_global_variables=false; - if (query_no_space_length==strlen("PROXYSQL FLUSH LOGS") && !strncasecmp("PROXYSQL FLUSH LOGS",query_no_space, query_no_space_length)) { - proxy_info("Received PROXYSQL FLUSH LOGS command\n"); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->flush_logs(); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } + bool runtime_scheduler=false; + bool runtime_restapi_routes=false; + bool runtime_mysql_users=false; + bool runtime_mysql_firewall=false; + bool runtime_mysql_ldap_mapping=false; + bool runtime_mysql_servers=false; + bool runtime_mysql_query_rules=false; + bool runtime_mysql_query_rules_fast_routing=false; - if (query_no_space_length==strlen("PROXYSQL FLUSH QUERY CACHE") && !strncasecmp("PROXYSQL FLUSH QUERY CACHE",query_no_space, query_no_space_length)) { - proxy_info("Received PROXYSQL FLUSH QUERY CACHE command\n"); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - if (GloQC) { - GloQC->flush(); - } - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } + bool runtime_pgsql_users = false; + bool runtime_pgsql_firewall = false; + bool runtime_pgsql_ldap_mapping = false; + bool runtime_pgsql_servers = false; + bool runtime_pgsql_query_rules = false; + bool runtime_pgsql_query_rules_fast_routing = false; - if (!strcasecmp("PROXYSQL FLUSH MYSQL CLIENT HOSTS", query_no_space)) { - proxy_info("Received PROXYSQL FLUSH MYSQL CLIENT HOSTS command\n"); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - if (GloMTH) { - GloMTH->flush_client_host_cache(); - } - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } + bool runtime_proxysql_servers=false; + bool runtime_checksums_values=false; - if ( - (query_no_space_length==strlen("PROXYSQL FLUSH CONFIGDB") && !strncasecmp("PROXYSQL FLUSH CONFIGDB",query_no_space, query_no_space_length)) // see #923 - ) { - proxy_info("Received %s command\n", query_no_space); - proxy_warning("A misconfigured configdb will cause undefined behaviors\n"); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->flush_configdb(); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } + bool runtime_coredump_filters=false; - if (strcasecmp("PROXYSQL RELOAD TLS",query_no_space) == 0) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - std::string s; - int rc = ProxySQL_create_or_load_TLS(false, s); - if (rc == 0) { - SPA->send_ok_msg_to_client(sess, s.length() ? (char*)s.c_str() : NULL, 0, query_no_space); - } else { - SPA->send_error_msg_to_client(sess, s.length() ? (char *)s.c_str() : (char *)"RELOAD TLS failed"); - } - return false; - } + bool stats_mysql_prepared_statements_info = false; -#ifndef NOJEM - if (query_no_space_length==strlen("PROXYSQL MEMPROFILE START") && !strncasecmp("PROXYSQL MEMPROFILE START",query_no_space, query_no_space_length)) { - bool en=true; - mallctl("prof.active", NULL, NULL, &en, sizeof(bool)); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - if (query_no_space_length==strlen("PROXYSQL MEMPROFILE STOP") && !strncasecmp("PROXYSQL MEMPROFILE STOP",query_no_space, query_no_space_length)) { - bool en=false; - mallctl("prof.active", NULL, NULL, &en, sizeof(bool)); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } -#endif +#ifdef PROXYSQLCLICKHOUSE + bool runtime_clickhouse_users = false; +#endif /* PROXYSQLCLICKHOUSE */ -#ifdef WITHGCOV - if (query_no_space_length==strlen("PROXYSQL GCOV DUMP") && !strncasecmp("PROXYSQL GCOV DUMP",query_no_space, query_no_space_length)) { - proxy_info("Received %s command\n", query_no_space); - __gcov_dump(); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - if (query_no_space_length==strlen("PROXYSQL GCOV RESET") && !strncasecmp("PROXYSQL GCOV RESET",query_no_space, query_no_space_length)) { - proxy_info("Received %s command\n", query_no_space); - __gcov_reset(); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } -#endif + bool monitor_mysql_server_group_replication_log=false; - if (query_no_space_length==strlen("PROXYSQL KILL") && !strncasecmp("PROXYSQL KILL",query_no_space, query_no_space_length)) { - proxy_info("Received PROXYSQL KILL command\n"); - exit(EXIT_SUCCESS); - } + bool monitor_mysql_server_galera_log=false; - if (query_no_space_length==strlen("PROXYSQL SHUTDOWN") && !strncasecmp("PROXYSQL SHUTDOWN",query_no_space, query_no_space_length)) { - // in 2.1 , PROXYSQL SHUTDOWN behaves like PROXYSQL KILL : quick exit - // the former PROXYQL SHUTDOWN is now replaced with PROXYSQL SHUTDOWN SLOW - proxy_info("Received PROXYSQL SHUTDOWN command\n"); - exit(EXIT_SUCCESS); - } + bool monitor_mysql_server_aws_aurora_log=false; + bool monitor_mysql_server_aws_aurora_check_status=false; - return true; -} + bool stats_proxysql_servers_checksums = false; + bool stats_proxysql_servers_metrics = false; + bool stats_proxysql_message_metrics = false; + bool stats_proxysql_message_metrics_reset = false; -// Returns true if the given name is either a know mysql or admin global variable. -bool is_valid_global_variable(const char *var_name) { - if (strlen(var_name) > 6 && !strncmp(var_name, "mysql-", 6) && GloMTH->has_variable(var_name + 6)) { - return true; - } else if (strlen(var_name) > 6 && !strncmp(var_name, "pgsql-", 6) && GloPTH->has_variable(var_name + 6)) { - return true; - } else if (strlen(var_name) > 6 && !strncmp(var_name, "admin-", 6) && SPA->has_variable(var_name + 6)) { - return true; - } else if (strlen(var_name) > 5 && !strncmp(var_name, "ldap-", 5) && GloMyLdapAuth && GloMyLdapAuth->has_variable(var_name + 5)) { - return true; - } else if (strlen(var_name) > 13 && !strncmp(var_name, "sqliteserver-", 13) && GloSQLite3Server && GloSQLite3Server->has_variable(var_name + 13)) { - return true; -#ifdef PROXYSQLCLICKHOUSE - } else if (strlen(var_name) > 11 && !strncmp(var_name, "clickhouse-", 11) && GloClickHouseServer && GloClickHouseServer->has_variable(var_name + 11)) { - return true; -#endif /* PROXYSQLCLICKHOUSE */ - } else { - return false; - } -} + //bool stats_proxysql_servers_status = false; // temporary disabled because not implemented -// This method translates a 'SET variable=value' command into an equivalent UPDATE. It doesn't yes support setting -// multiple variables at once. -// -// It modifies the original query. -template -bool admin_handler_command_set(char *query_no_space, unsigned int query_no_space_length, Client_Session& sess, ProxySQL_Admin *pa, char **q, unsigned int *ql) { - if (!strstr(query_no_space,(char *)"password")) { // issue #599 - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command %s\n", query_no_space); - if (strncasecmp(query_no_space,(char *)"set autocommit",strlen((char *)"set autocommit"))) { - if (strncasecmp(query_no_space,(char *)"SET @@session.autocommit",strlen((char *)"SET @@session.autocommit"))) { - proxy_info("Received command %s\n", query_no_space); - } - } - } - // Get a pointer to the beginnig of var=value entry and split to get var name and value - char *set_entry = query_no_space + strlen("SET "); - char *untrimmed_var_name=NULL; - char *var_value=NULL; - c_split_2(set_entry, "=", &untrimmed_var_name, &var_value); - - // Trim spaces from var name to allow writing like 'var = value' - char *var_name = trim_spaces_in_place(untrimmed_var_name); - - if (strstr(var_name,(char *)"password") || strcmp(var_name,(char *)"mysql-default_authentication_plugin")==0) { - proxy_info("Received SET command for %s\n", var_name); - } - - bool run_query = false; - // Check if the command tries to set a non-existing variable. - if (strcmp(var_name,"mysql-init_connect")==0) { - char *err_msg_fmt = (char *) "ERROR: Global variable '%s' is not configurable using SET command. You must run UPDATE global_variables"; - size_t buff_len = strlen(err_msg_fmt) + strlen(var_name) + 1; - char *buff = (char *) malloc(buff_len); - snprintf(buff, buff_len, err_msg_fmt, var_name); - SPA->send_error_msg_to_client(sess, buff); - free(buff); - run_query = false; - } else { - if (!is_valid_global_variable(var_name)) { - char *err_msg_fmt = (char *) "ERROR: Unknown global variable: '%s'."; - size_t buff_len = strlen(err_msg_fmt) + strlen(var_name) + 1; - char *buff = (char *) malloc(buff_len); - snprintf(buff, buff_len, err_msg_fmt, var_name); - SPA->send_ok_msg_to_client(sess, buff, 0, query_no_space); - free(buff); - run_query = false; - } else { - const char *update_format = (char *)"UPDATE global_variables SET variable_value=%s WHERE variable_name='%s'"; - // Computed length is more than needed since it also counts the format modifiers (%s). - size_t query_len = strlen(update_format) + strlen(var_name) + strlen(var_value) + 1; - char *query = (char *)l_alloc(query_len); - snprintf(query, query_len, update_format, var_value, var_name); - - run_query = true; - l_free(*ql,*q); - *q = query; - *ql = strlen(*q) + 1; - } - } - free(untrimmed_var_name); - free(var_value); - return run_query; -} - -/* Note: - * This function can modify the original query - */ -template -bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query_no_space_length, Client_Session& sess, ProxySQL_Admin *pa, char **q, unsigned int *ql) { - proxy_debug(PROXY_DEBUG_ADMIN, 5, "Received command %s\n", query_no_space); - -#ifdef DEBUG - if ((query_no_space_length>11) && ( (!strncasecmp("SAVE DEBUG ", query_no_space, 11)) || (!strncasecmp("LOAD DEBUG ", query_no_space, 11))) ) { - if ( - (query_no_space_length==strlen("LOAD DEBUG TO MEMORY") && !strncasecmp("LOAD DEBUG TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD DEBUG TO MEM") && !strncasecmp("LOAD DEBUG TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD DEBUG FROM DISK") && !strncasecmp("LOAD DEBUG FROM DISK",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - // we are now copying the data from memory to disk - // tables involved are: - // * debug_levels - // * debug_filters - // We only delete from filters and not from levels because the - // levels are hardcoded and fixed in number, while filters can - // be arbitrary - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->admindb->execute("DELETE FROM main.debug_filters"); - SPA->admindb->execute("INSERT OR REPLACE INTO main.debug_levels SELECT * FROM disk.debug_levels"); - SPA->admindb->execute("INSERT INTO main.debug_filters SELECT * FROM disk.debug_filters"); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded debug levels/filters to MEMORY\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - - if ( - (query_no_space_length==strlen("SAVE DEBUG FROM MEMORY") && !strncasecmp("SAVE DEBUG FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE DEBUG FROM MEM") && !strncasecmp("SAVE DEBUG FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE DEBUG TO DISK") && !strncasecmp("SAVE DEBUG TO DISK",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - // we are now copying the data from disk to memory - // tables involved are: - // * debug_levels - // * debug_filters - // We only delete from filters and not from levels because the - // levels are hardcoded and fixed in number, while filters can - // be arbitrary - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->admindb->execute("DELETE FROM disk.debug_filters"); - SPA->admindb->execute("INSERT OR REPLACE INTO disk.debug_levels SELECT * FROM main.debug_levels"); - SPA->admindb->execute("INSERT INTO disk.debug_filters SELECT * FROM main.debug_filters"); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved debug levels/filters to DISK\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (strcasestr(query_no_space,"processlist")) + // This will match the following usecases: + // SHOW PROCESSLIST + // SHOW FULL PROCESSLIST + // SELECT * FROM stats_mysql_processlist + { stats_mysql_processlist=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_query_digest")) + { stats_mysql_query_digest=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_query_digest_reset")) + { stats_mysql_query_digest_reset=true; refresh=true; } + if (stats_mysql_query_digest_reset == true && stats_mysql_query_digest == true) { + int nd = 0; + int ndr= 0; + char *c = NULL; + char *_ret = NULL; + c = (char *)query_no_space; + _ret = NULL; + while ((_ret = strstr(c,"stats_mysql_query_digest_reset"))) { + ndr++; + c = _ret + strlen("stats_mysql_query_digest_reset"); } - - if ( - (query_no_space_length==strlen("LOAD DEBUG FROM MEMORY") && !strncasecmp("LOAD DEBUG FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD DEBUG FROM MEM") && !strncasecmp("LOAD DEBUG FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD DEBUG TO RUNTIME") && !strncasecmp("LOAD DEBUG TO RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD DEBUG TO RUN") && !strncasecmp("LOAD DEBUG TO RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - int rc=SPA->load_debug_to_runtime(); - if (rc) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded debug levels/filters to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 1, "Error while loading debug levels/filters to RUNTIME\n"); - SPA->send_error_msg_to_client(sess, (char *)"Error while loading debug levels/filters to RUNTIME"); - } - return false; + c = (char *)query_no_space; + _ret = NULL; + while ((_ret = strstr(c,"stats_mysql_query_digest"))) { + nd++; + c = _ret + strlen("stats_mysql_query_digest"); } - - if ( - (query_no_space_length==strlen("SAVE DEBUG TO MEMORY") && !strncasecmp("SAVE DEBUG TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE DEBUG TO MEM") && !strncasecmp("SAVE DEBUG TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE DEBUG FROM RUNTIME") && !strncasecmp("SAVE DEBUG FROM RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE DEBUG FROM RUN") && !strncasecmp("SAVE DEBUG FROM RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->save_debug_from_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved debug levels/filters from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (nd == ndr) { + stats_mysql_query_digest = false; } - } -#endif /* DEBUG */ - - if ((query_no_space_length>13) && ( (!strncasecmp("SAVE RESTAPI ", query_no_space, 13)) || (!strncasecmp("LOAD RESTAPI ", query_no_space, 13))) ) { - - if (FlushCommandWrapper(sess, "restapi", query_no_space, query_no_space_length) == true) - return false; - - if ( - (query_no_space_length==strlen("LOAD RESTAPI FROM MEMORY") && !strncasecmp("LOAD RESTAPI FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD RESTAPI FROM MEM") && !strncasecmp("LOAD RESTAPI FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD RESTAPI TO RUNTIME") && !strncasecmp("LOAD RESTAPI TO RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD RESTAPI TO RUN") && !strncasecmp("LOAD RESTAPI TO RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->proxysql_restapi().load_restapi_to_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded restapito RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (strstr(query_no_space,"stats_mysql_errors")) + { stats_mysql_errors=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_errors_reset")) + { stats_mysql_errors_reset=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_global")) + { stats_mysql_global=true; refresh=true; } + if (strstr(query_no_space,"stats_memory_metrics")) + { stats_memory_metrics=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_connection_pool_reset")) + { + stats_mysql_connection_pool_reset=true; refresh=true; + } else { + if (strstr(query_no_space,"stats_mysql_connection_pool")) + { stats_mysql_connection_pool=true; refresh=true; } } + if (strstr(query_no_space,"stats_mysql_free_connections")) + { stats_mysql_free_connections=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_commands_counters")) + { stats_mysql_commands_counters=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_query_rules")) + { stats_mysql_query_rules=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_users")) + { stats_mysql_users=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_gtid_executed")) + { stats_mysql_gtid_executed=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_client_host_cache")) + { stats_mysql_client_host_cache=true; refresh=true; } + if (strstr(query_no_space,"stats_mysql_client_host_cache_reset")) + { stats_mysql_client_host_cache_reset=true; refresh=true; } - if ( - (query_no_space_length==strlen("LOAD RESTAPI FROM CONFIG") && !strncasecmp("LOAD RESTAPI FROM CONFIG",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - if (GloVars.configfile_open) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); - if (GloVars.confFile->OpenFile(NULL)==true) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - int rows=0; - rows=SPA->proxysql_config().Read_Restapi_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded restapi from CONFIG\n"); - SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); - GloVars.confFile->CloseFile(); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); - char *s=(char *)"Unable to open or parse config file %s"; - char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); - sprintf(m,s,GloVars.config_file); - SPA->send_error_msg_to_client(sess, m); - free(m); - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); - SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); - } - return false; - } + if (strstr(query_no_space,"stats_proxysql_servers_checksums")) + { stats_proxysql_servers_checksums = true; refresh = true; } + if (strstr(query_no_space,"stats_proxysql_servers_metrics")) + { stats_proxysql_servers_metrics = true; refresh = true; } + if (strstr(query_no_space,"stats_proxysql_message_metrics")) + { stats_proxysql_message_metrics=true; refresh=true; } + if (strstr(query_no_space,"stats_proxysql_message_metrics_reset")) + { stats_proxysql_message_metrics_reset=true; refresh=true; } - if ( - (query_no_space_length==strlen("SAVE RESTAPI TO MEMORY") && !strncasecmp("SAVE RESTAPI TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE RESTAPI TO MEM") && !strncasecmp("SAVE RESTAPI TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE RESTAPI FROM RUNTIME") && !strncasecmp("SAVE RESTAPI FROM RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE RESTAPI FROM RUN") && !strncasecmp("SAVE RESTAPI FROM RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->save_scheduler_runtime_to_database(false); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved scheduler from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } + // temporary disabled because not implemented +/* + if (strstr(query_no_space,"stats_proxysql_servers_status")) + { stats_proxysql_servers_status = true; refresh = true; } +*/ + if (strstr(query_no_space,"stats_mysql_prepared_statements_info")) { + stats_mysql_prepared_statements_info=true; refresh=true; } - - if ((query_no_space_length>15) && ( (!strncasecmp("SAVE SCHEDULER ", query_no_space, 15)) || (!strncasecmp("LOAD SCHEDULER ", query_no_space, 15))) ) { - - if (FlushCommandWrapper(sess, "scheduler", query_no_space, query_no_space_length) == true) - return false; - - if ( - (query_no_space_length==strlen("LOAD SCHEDULER FROM MEMORY") && !strncasecmp("LOAD SCHEDULER FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD SCHEDULER FROM MEM") && !strncasecmp("LOAD SCHEDULER FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD SCHEDULER TO RUNTIME") && !strncasecmp("LOAD SCHEDULER TO RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD SCHEDULER TO RUN") && !strncasecmp("LOAD SCHEDULER TO RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->load_scheduler_to_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded scheduler to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - - if ( - (query_no_space_length==strlen("LOAD SCHEDULER FROM CONFIG") && !strncasecmp("LOAD SCHEDULER FROM CONFIG",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - if (GloVars.configfile_open) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); - if (GloVars.confFile->OpenFile(NULL)==true) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - int rows=0; - rows=SPA->proxysql_config().Read_Scheduler_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded scheduler from CONFIG\n"); - SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); - GloVars.confFile->CloseFile(); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); - char *s=(char *)"Unable to open or parse config file %s"; - char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); - sprintf(m,s,GloVars.config_file); - SPA->send_error_msg_to_client(sess, m); - free(m); - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); - SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + if (admin) { + if (strstr(query_no_space,"global_variables")) + { dump_global_variables=true; refresh=true; } + if (strstr(query_no_space,"runtime_")) { + if ( + strstr(query_no_space,"runtime_mysql_servers") + || + strstr(query_no_space,"runtime_mysql_replication_hostgroups") + || + strstr(query_no_space,"runtime_mysql_group_replication_hostgroups") + || + strstr(query_no_space,"runtime_mysql_galera_hostgroups") + || + strstr(query_no_space,"runtime_mysql_aws_aurora_hostgroups") + || + strstr(query_no_space,"runtime_mysql_hostgroup_attributes") + || + strstr(query_no_space,"runtime_mysql_servers_ssl_params") + ) { + runtime_mysql_servers=true; refresh=true; } - return false; - } - - if ( - (query_no_space_length==strlen("SAVE SCHEDULER TO MEMORY") && !strncasecmp("SAVE SCHEDULER TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE SCHEDULER TO MEM") && !strncasecmp("SAVE SCHEDULER TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE SCHEDULER FROM RUNTIME") && !strncasecmp("SAVE SCHEDULER FROM RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE SCHEDULER FROM RUN") && !strncasecmp("SAVE SCHEDULER FROM RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->save_scheduler_runtime_to_database(false); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved scheduler from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - - } - if ((query_no_space_length>16) && (!strncasecmp("LOAD MYSQL USER ", query_no_space, 16)) ) { - if (query_no_space_length>27) { - if (!strncasecmp(" TO RUNTIME", query_no_space+query_no_space_length-11, 11)) { - char *name=(char *)malloc(query_no_space_length-27+1); - strncpy(name,query_no_space+16,query_no_space_length-27); - name[query_no_space_length-27]=0; - int i=0; - int s=strlen(name); - bool legitname=true; - for (i=0; i= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - ( (c == '-') || (c == '+') || (c == '_')) - ) { - v=true; - } - if (v==false) { - legitname=false; - } + if ( + strstr(query_no_space, "runtime_pgsql_servers") + || + strstr(query_no_space, "runtime_pgsql_replication_hostgroups") + || + strstr(query_no_space, "runtime_pgsql_hostgroup_attributes") + ) { + runtime_pgsql_servers = true; refresh = true; + } + if ( + strstr(query_no_space,"runtime_mysql_firewall_whitelist_rules") + || + strstr(query_no_space,"runtime_mysql_firewall_whitelist_users") + || + strstr(query_no_space,"runtime_mysql_firewall_whitelist_sqli_fingerprints") + ) { + runtime_mysql_firewall=true; refresh=true; + } + if ( + strstr(query_no_space, "runtime_pgsql_firewall_whitelist_rules") + || + strstr(query_no_space, "runtime_pgsql_firewall_whitelist_users") + || + strstr(query_no_space, "runtime_pgsql_firewall_whitelist_sqli_fingerprints") + ) { + runtime_pgsql_firewall = true; refresh = true; + } + if (strstr(query_no_space,"runtime_mysql_users")) { + runtime_mysql_users=true; refresh=true; + } + if (strstr(query_no_space, "runtime_pgsql_users")) { + runtime_pgsql_users = true; refresh = true; + } + if (GloMyLdapAuth) { + if (strstr(query_no_space,"runtime_mysql_ldap_mapping")) { + runtime_mysql_ldap_mapping=true; refresh=true; } - if (legitname) { - proxy_info("Loading user %s\n", name); - pthread_mutex_lock(&users_mutex); - - if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { - SPA->public_add_active_users(USERNAME_BACKEND, name); - SPA->public_add_active_users(USERNAME_FRONTEND, name); - } else { - SPA->public_add_active_users(USERNAME_BACKEND, name); - SPA->public_add_active_users(USERNAME_FRONTEND, name); - } - - pthread_mutex_unlock(&users_mutex); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - } else { - proxy_info("Tried to load invalid user %s\n", name); - char *s=(char *)"Invalid name %s"; - char *m=(char *)malloc(strlen(s)+strlen(name)+1); - sprintf(m,s,name); - SPA->send_error_msg_to_client(sess, m); - free(m); + if (strstr(query_no_space, "runtime_pgsql_ldap_mapping")) { + runtime_mysql_ldap_mapping = true; refresh = true; } - free(name); - return false; } - } - } + if (strstr(query_no_space,"runtime_mysql_query_rules")) { + runtime_mysql_query_rules=true; refresh=true; + } + if (strstr(query_no_space, "runtime_pgsql_query_rules")) { + runtime_pgsql_query_rules = true; refresh = true; + } + if (strstr(query_no_space,"runtime_mysql_query_rules_fast_routing")) { + runtime_mysql_query_rules_fast_routing=true; refresh=true; + } + if (strstr(query_no_space, "runtime_pgsql_query_rules_fast_routing")) { + runtime_pgsql_query_rules_fast_routing = true; refresh = true; + } + if (strstr(query_no_space,"runtime_scheduler")) { + runtime_scheduler=true; refresh=true; + } + if (strstr(query_no_space,"runtime_restapi_routes")) { + runtime_restapi_routes=true; refresh=true; + } + if (strstr(query_no_space,"runtime_proxysql_servers")) { + runtime_proxysql_servers=true; refresh=true; + } + if (strstr(query_no_space,"runtime_checksums_values")) { + runtime_checksums_values=true; refresh=true; + } + if (strstr(query_no_space,"runtime_coredump_filters")) { + runtime_coredump_filters=true; refresh=true; + } #ifdef PROXYSQLCLICKHOUSE - if ( ( GloVars.global.clickhouse_server == true ) && (query_no_space_length>22) && ( (!strncasecmp("SAVE CLICKHOUSE USERS ", query_no_space, 22)) || (!strncasecmp("LOAD CLICKHOUSE USERS ", query_no_space, 22))) ) { - if ( - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO MEMORY") && !strncasecmp("LOAD CLICKHOUSE USERS TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO MEM") && !strncasecmp("LOAD CLICKHOUSE USERS TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS FROM DISK") && !strncasecmp("LOAD CLICKHOUSE USERS FROM DISK",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->flush_clickhouse_users__from_disk_to_memory(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading clickhouse users to MEMORY\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - - if ( - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM MEMORY") && !strncasecmp("SAVE CLICKHOUSE USERS FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM MEM") && !strncasecmp("SAVE CLICKHOUSE USERS FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS TO DISK") && !strncasecmp("SAVE CLICKHOUSE USERS TO DISK",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->flush_clickhouse_users__from_memory_to_disk(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saving clickhouse users to DISK\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } + if (( GloVars.global.clickhouse_server == true ) && strstr(query_no_space,"runtime_clickhouse_users")) { + runtime_clickhouse_users=true; refresh=true; + } +#endif /* PROXYSQLCLICKHOUSE */ - if ( - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS FROM MEMORY") && !strncasecmp("LOAD CLICKHOUSE USERS FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS FROM MEM") && !strncasecmp("LOAD CLICKHOUSE USERS FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO RUNTIME") && !strncasecmp("LOAD CLICKHOUSE USERS TO RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO RUN") && !strncasecmp("LOAD CLICKHOUSE USERS TO RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->init_clickhouse_users(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded clickhouse users to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; } - - if ( - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS TO MEMORY") && !strncasecmp("SAVE CLICKHOUSE USERS TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS TO MEM") && !strncasecmp("SAVE CLICKHOUSE USERS TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM RUNTIME") && !strncasecmp("SAVE CLICKHOUSE USERS FROM RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM RUN") && !strncasecmp("SAVE CLICKHOUSE USERS FROM RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->save_clickhouse_users_runtime_to_database(false); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved clickhouse users from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - } -#endif /* PROXYSQLCLICKHOUSE */ - - if ((query_no_space_length>17) && ( (!strcasecmp("SAVE MYSQL DIGEST TO DISK", query_no_space) ) )) { - proxy_info("Received %s command\n", query_no_space); - unsigned long long curtime1=monotonic_time(); - int r1 = SPA->FlushDigestTableToDisk(SPA->statsdb_disk); - unsigned long long curtime2=monotonic_time(); - curtime1 = curtime1/1000; - curtime2 = curtime2/1000; - proxy_info("Saved stats_mysql_query_digest to disk: %llums to write %u entries\n", curtime2-curtime1, r1); - SPA->send_ok_msg_to_client(sess, NULL, r1, query_no_space); - return false; + if (strstr(query_no_space,"mysql_server_group_replication_log")) { + monitor_mysql_server_group_replication_log=true; refresh=true; } - - if ((query_no_space_length > 17) && ((!strcasecmp("SAVE PGSQL DIGEST TO DISK", query_no_space)))) { - proxy_info("Received %s command\n", query_no_space); - unsigned long long curtime1 = monotonic_time(); - int r1 = SPA->FlushDigestTableToDisk(SPA->statsdb_disk); - unsigned long long curtime2 = monotonic_time(); - curtime1 = curtime1 / 1000; - curtime2 = curtime2 / 1000; - proxy_info("Saved stats_mysql_query_digest to disk: %llums to write %u entries\n", curtime2 - curtime1, r1); - SPA->send_ok_msg_to_client(sess, NULL, r1, query_no_space); - return false; + if (strstr(query_no_space,"mysql_server_galera_log")) { + monitor_mysql_server_galera_log=true; refresh=true; } - - if ((query_no_space_length>17) && - ((!strncasecmp("SAVE MYSQL USERS ", query_no_space, 17)) || (!strncasecmp("LOAD MYSQL USERS ", query_no_space, 17)) || - ((!strncasecmp("SAVE PGSQL USERS ", query_no_space, 17)) || (!strncasecmp("LOAD PGSQL USERS ", query_no_space, 17))))) { - - const bool is_pgsql = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? true : false; - const std::string modname = is_pgsql ? "pgsql_users" : "mysql_users"; - - tuple, vector>& t = load_save_disk_commands[modname]; - if ( is_admin_command_or_alias(get<1>(t), query_no_space, query_no_space_length) ) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - - if (is_pgsql) - SPA->flush_pgsql_users__from_disk_to_memory(); - else - SPA->flush_mysql_users__from_disk_to_memory(); - - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading %s to MEMORY\n", modname.c_str()); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - if ( is_admin_command_or_alias(get<2>(t), query_no_space, query_no_space_length) ) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - - if (is_pgsql) - SPA->flush_pgsql_users__from_memory_to_disk(); - else - SPA->flush_mysql_users__from_memory_to_disk(); - - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saving %s to DISK\n", modname.c_str()); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - - if (is_pgsql) { - if (is_admin_command_or_alias(LOAD_PGSQL_USERS_FROM_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->init_pgsql_users(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql users to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } + if (strstr(query_no_space,"mysql_server_aws_aurora_log")) { + monitor_mysql_server_aws_aurora_log=true; refresh=true; + } + if (strstr(query_no_space,"mysql_server_aws_aurora_check_status")) { + monitor_mysql_server_aws_aurora_check_status=true; refresh=true; + } +// if (stats_mysql_processlist || stats_mysql_connection_pool || stats_mysql_query_digest || stats_mysql_query_digest_reset) { + if (refresh==true) { + //pthread_mutex_lock(&admin_mutex); + //ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + if (stats_mysql_processlist) + stats___mysql_processlist(); + if (stats_mysql_query_digest_reset) { + stats___mysql_query_digests_v2(true, stats_mysql_query_digest, false); } else { - if (is_admin_command_or_alias(LOAD_MYSQL_USERS_FROM_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->init_users(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql users to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (stats_mysql_query_digest) { + stats___mysql_query_digests_v2(false, false, false); } } - - if ( - (query_no_space_length==strlen("LOAD MYSQL USERS FROM CONFIG") && (!strncasecmp("LOAD MYSQL USERS FROM CONFIG",query_no_space, query_no_space_length) || - !strncasecmp("LOAD PGSQL USERS FROM CONFIG", query_no_space, query_no_space_length))) - ) { - proxy_info("Received %s command\n", query_no_space); - if (GloVars.configfile_open) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); - if (GloVars.confFile->OpenFile(NULL)==true) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - int rows=0; - if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { - rows=SPA->proxysql_config().Read_PgSQL_Users_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql users from CONFIG\n"); - } else { - rows=SPA->proxysql_config().Read_MySQL_Users_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql users from CONFIG\n"); - } - SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); - GloVars.confFile->CloseFile(); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); - char *s=(char *)"Unable to open or parse config file %s"; - char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); - sprintf(m,s,GloVars.config_file); - SPA->send_error_msg_to_client(sess, m); - free(m); - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); - SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); - } - return false; + if (stats_mysql_errors) + stats___mysql_errors(false); + if (stats_mysql_errors_reset) { + stats___mysql_errors(true); } - - if (is_pgsql) { - if (is_admin_command_or_alias(SAVE_PGSQL_USERS_TO_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->save_pgsql_users_runtime_to_database(false); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved pgsql users from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } + if (stats_mysql_connection_pool_reset) { + stats___mysql_connection_pool(true); } else { - if (is_admin_command_or_alias(SAVE_MYSQL_USERS_TO_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->save_mysql_users_runtime_to_database(false); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved mysql users from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - } - } - if ((query_no_space_length>28) && ( (!strncasecmp("SAVE SQLITESERVER VARIABLES ", query_no_space, 28)) || (!strncasecmp("LOAD SQLITESERVER VARIABLES ", query_no_space, 28))) ) { - - if ( - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO MEMORY") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO MEM") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES FROM DISK") && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM DISK",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - l_free(*ql,*q); - *q=l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'sqliteserver-%'"); - *ql=strlen(*q)+1; - return true; + if (stats_mysql_connection_pool) + stats___mysql_connection_pool(false); } + if (stats_mysql_free_connections) + stats___mysql_free_connections(); + if (stats_mysql_global) + stats___mysql_global(); + if (stats_memory_metrics) + stats___memory_metrics(); + if (stats_mysql_query_rules) + stats___mysql_query_rules(); + if (stats_mysql_commands_counters) + stats___mysql_commands_counters(); + if (stats_mysql_users) + stats___mysql_users(); + if (stats_mysql_gtid_executed) + stats___mysql_gtid_executed(); - if ( - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM MEMORY") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM MEM") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES TO DISK") && !strncasecmp("SAVE SQLITESERVER VARIABLES TO DISK",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - l_free(*ql,*q); - *q=l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'sqliteserver-%'"); - *ql=strlen(*q)+1; - return true; + // cluster + if (stats_proxysql_servers_metrics) { + stats___proxysql_servers_metrics(); } - - if ( - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES FROM MEMORY") && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES FROM MEM") && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO RUNTIME") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO RUN") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->load_sqliteserver_variables_to_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded SQLiteServer variables to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (stats_proxysql_servers_checksums) { + stats___proxysql_servers_checksums(); } - - if ( - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES TO MEMORY") && !strncasecmp("SAVE SQLITESERVER VARIABLES TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES TO MEM") && !strncasecmp("SAVE SQLITESERVER VARIABLES TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM RUNTIME") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM RUN") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->save_sqliteserver_variables_from_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved SQLiteServer variables from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (stats_proxysql_message_metrics_reset) { + stats___proxysql_message_metrics(true); + } else { + if (stats_proxysql_message_metrics) { + stats___proxysql_message_metrics(false); + } } - } -#ifdef PROXYSQLCLICKHOUSE - if ((query_no_space_length>26) && ( (!strncasecmp("SAVE CLICKHOUSE VARIABLES ", query_no_space, 26)) || (!strncasecmp("LOAD CLICKHOUSE VARIABLES ", query_no_space, 26))) ) { - if ( - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO MEMORY") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO MEM") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES FROM DISK") && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM DISK",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - l_free(*ql,*q); - *q=l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'clickhouse-%'"); - *ql=strlen(*q)+1; - return true; + // temporary disabled because not implemented +// if (stats_proxysql_servers_status) { +// stats___proxysql_servers_status(); +// } + if (stats_mysql_prepared_statements_info) { + stats___mysql_prepared_statements_info(); } - if ( - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM MEMORY") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM MEM") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES TO DISK") && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO DISK",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - l_free(*ql,*q); - *q=l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'clickhouse-%'"); - *ql=strlen(*q)+1; - return true; + if (stats_mysql_client_host_cache) { + stats___mysql_client_host_cache(false); } - - if ( - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES FROM MEMORY") && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES FROM MEM") && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO RUNTIME") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO RUN") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->load_clickhouse_variables_to_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded clickhouse variables to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (stats_mysql_client_host_cache_reset) { + stats___mysql_client_host_cache(true); } - if ( - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES TO MEMORY") && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES TO MEM") && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM RUNTIME") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM RUN") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->save_clickhouse_variables_from_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved clickhouse variables from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - } + if (admin) { + if (dump_global_variables) { + pthread_mutex_lock(&GloVars.checksum_mutex); + admindb->execute("DELETE FROM runtime_global_variables"); // extra + flush_admin_variables___runtime_to_database(admindb, false, false, false, true); + flush_mysql_variables___runtime_to_database(admindb, false, false, false, true); +#ifdef PROXYSQLCLICKHOUSE + flush_clickhouse_variables___runtime_to_database(admindb, false, false, false, true); #endif /* PROXYSQLCLICKHOUSE */ - - if (GloMyLdapAuth) { - if ((query_no_space_length>20) && ( (!strncasecmp("SAVE LDAP VARIABLES ", query_no_space, 20)) || (!strncasecmp("LOAD LDAP VARIABLES ", query_no_space, 20))) ) { - - if ( - (query_no_space_length==strlen("LOAD LDAP VARIABLES TO MEMORY") && !strncasecmp("LOAD LDAP VARIABLES TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD LDAP VARIABLES TO MEM") && !strncasecmp("LOAD LDAP VARIABLES TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD LDAP VARIABLES FROM DISK") && !strncasecmp("LOAD LDAP VARIABLES FROM DISK",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - l_free(*ql,*q); - *q=l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'ldap-%'"); - *ql=strlen(*q)+1; - return true; + flush_sqliteserver_variables___runtime_to_database(admindb, false, false, false, true); + flush_ldap_variables___runtime_to_database(admindb, false, false, false, true); + flush_pgsql_variables___runtime_to_database(admindb, false, false, false, true); + pthread_mutex_unlock(&GloVars.checksum_mutex); } - - if ( - (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM MEMORY") && !strncasecmp("SAVE LDAP VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM MEM") && !strncasecmp("SAVE LDAP VARIABLES FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE LDAP VARIABLES TO DISK") && !strncasecmp("SAVE LDAP VARIABLES TO DISK",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - l_free(*ql,*q); - *q=l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'ldap-%'"); - *ql=strlen(*q)+1; - return true; + if (runtime_mysql_servers) { + int old_hostgroup_manager_verbose = mysql_thread___hostgroup_manager_verbose; + mysql_thread___hostgroup_manager_verbose = 0; + mysql_servers_wrlock(); + save_mysql_servers_runtime_to_database(true); + mysql_servers_wrunlock(); + mysql_thread___hostgroup_manager_verbose = old_hostgroup_manager_verbose; } - - if ( - (query_no_space_length==strlen("LOAD LDAP VARIABLES FROM MEMORY") && !strncasecmp("LOAD LDAP VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD LDAP VARIABLES FROM MEM") && !strncasecmp("LOAD LDAP VARIABLES FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD LDAP VARIABLES TO RUNTIME") && !strncasecmp("LOAD LDAP VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD LDAP VARIABLES TO RUN") && !strncasecmp("LOAD LDAP VARIABLES TO RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->load_ldap_variables_to_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded ldap variables to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (runtime_pgsql_servers) { + int old_hostgroup_manager_verbose = pgsql_thread___hostgroup_manager_verbose; + pgsql_thread___hostgroup_manager_verbose = 0; + pgsql_servers_wrlock(); + save_pgsql_servers_runtime_to_database(true); + pgsql_servers_wrunlock(); + pgsql_thread___hostgroup_manager_verbose = old_hostgroup_manager_verbose; } - - if ( - (query_no_space_length==strlen("SAVE LDAP VARIABLES TO MEMORY") && !strncasecmp("SAVE LDAP VARIABLES TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE LDAP VARIABLES TO MEM") && !strncasecmp("SAVE LDAP VARIABLES TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM RUNTIME") && !strncasecmp("SAVE LDAP VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM RUN") && !strncasecmp("SAVE LDAP VARIABLES FROM RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->save_ldap_variables_from_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved ldap variables from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (runtime_proxysql_servers) { + //mysql_servers_wrlock(); + // before save_proxysql_servers_runtime_to_database() we release + // sql_query_global_mutex to prevent a possible deadlock due to + // a race condition + // save_proxysql_servers_runtime_to_database() calls ProxySQL_Cluster::dump_table_proxysql_servers() + pthread_mutex_unlock(&SPA->sql_query_global_mutex); + save_proxysql_servers_runtime_to_database(true); + pthread_mutex_lock(&SPA->sql_query_global_mutex); + //mysql_servers_wrunlock(); } - } - } - - if ((query_no_space_length > 21) && ((!strncasecmp("SAVE MYSQL VARIABLES ", query_no_space, 21)) || (!strncasecmp("LOAD MYSQL VARIABLES ", query_no_space, 21)) || - (!strncasecmp("SAVE PGSQL VARIABLES ", query_no_space, 21)) || (!strncasecmp("LOAD PGSQL VARIABLES ", query_no_space, 21)))) { - - const bool is_pgsql = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? true : false; - const std::string modname = is_pgsql ? "pgsql_variables" : "mysql_variables"; - - tuple, vector>& t = load_save_disk_commands[modname]; - if (is_admin_command_or_alias(get<1>(t), query_no_space, query_no_space_length)) { - l_free(*ql, *q); - if (is_pgsql) { - *q = l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'pgsql-%'"); - } - else { - *q = l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'mysql-%'"); - } - *ql = strlen(*q) + 1; - return true; + if (runtime_mysql_users) { + save_mysql_users_runtime_to_database(true); } - - if (is_admin_command_or_alias(get<2>(t), query_no_space, query_no_space_length)) { - l_free(*ql, *q); - if (is_pgsql) { - *q = l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'pgsql-%'"); - } - else { - *q = l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'mysql-%'"); - } - *ql = strlen(*q) + 1; - return true; + if (runtime_pgsql_users) { + save_pgsql_users_runtime_to_database(true); } - - if (is_pgsql) { - if (is_admin_command_or_alias(LOAD_PGSQL_VARIABLES_FROM_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->load_pgsql_variables_to_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql variables to RUNTIME\n"); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql variables to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - } else { - if (is_admin_command_or_alias(LOAD_MYSQL_VARIABLES_FROM_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->load_mysql_variables_to_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql variables to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (runtime_mysql_firewall) { + save_mysql_firewall_from_runtime(true); } - } - - if ( - (query_no_space_length==strlen("LOAD MYSQL VARIABLES FROM CONFIG") && (!strncasecmp("LOAD MYSQL VARIABLES FROM CONFIG",query_no_space, query_no_space_length) || - !strncasecmp("LOAD PGSQL VARIABLES FROM CONFIG", query_no_space, query_no_space_length))) - ) { - proxy_info("Received %s command\n", query_no_space); - if (GloVars.configfile_open) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); - if (GloVars.confFile->OpenFile(NULL)==true) { - int rows=0; - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { - rows=SPA->proxysql_config().Read_Global_Variables_from_configfile("pgsql"); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql global variables from CONFIG\n"); - } else { - rows = SPA->proxysql_config().Read_Global_Variables_from_configfile("mysql"); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql global variables from CONFIG\n"); - } - SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); - GloVars.confFile->CloseFile(); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); - char *s=(char *)"Unable to open or parse config file %s"; - char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); - sprintf(m,s,GloVars.config_file); - SPA->send_error_msg_to_client(sess, m); - free(m); - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); - SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + if (runtime_pgsql_firewall) { + save_pgsql_firewall_from_runtime(true); } - return false; - } - - if (is_pgsql) { - if (is_admin_command_or_alias(SAVE_PGSQL_VARIABLES_TO_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->save_pgsql_variables_from_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved pgsql variables from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (runtime_mysql_ldap_mapping) { + save_mysql_ldap_mapping_runtime_to_database(true); } - } else { - if (is_admin_command_or_alias(SAVE_MYSQL_VARIABLES_TO_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->save_mysql_variables_from_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved mysql variables from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (runtime_pgsql_ldap_mapping) { + save_pgsql_ldap_mapping_runtime_to_database(true); } - } - } - - if ((query_no_space_length > 14) && (!strncasecmp("LOAD COREDUMP ", query_no_space, 14))) { - - if ( is_admin_command_or_alias(LOAD_COREDUMP_FROM_MEMORY, query_no_space, query_no_space_length) ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - bool rc = SPA->load_coredump_to_runtime(); - if (rc) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded coredump filters to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 1, "Error while loading coredump filters to RUNTIME\n"); - SPA->send_error_msg_to_client(sess, (char*)"Error while loading coredump filters to RUNTIME"); + if (runtime_mysql_query_rules) { + save_mysql_query_rules_from_runtime(true); } - return false; - } - } - - if ((query_no_space_length>19) && ( (!strncasecmp("SAVE MYSQL SERVERS ", query_no_space, 19)) || (!strncasecmp("LOAD MYSQL SERVERS ", query_no_space, 19)) || - (!strncasecmp("SAVE PGSQL SERVERS ", query_no_space, 19)) || (!strncasecmp("LOAD PGSQL SERVERS ", query_no_space, 19)))) { - - const bool is_pgsql = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? true : false; - const std::string modname = is_pgsql ? "pgsql_servers" : "mysql_servers"; - - if (FlushCommandWrapper(sess, modname, query_no_space, query_no_space_length) == true) - return false; - - if (is_pgsql) { - if (is_admin_command_or_alias(LOAD_PGSQL_SERVERS_FROM_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->pgsql_servers_wrlock(); - SPA->load_pgsql_servers_to_runtime(); - SPA->pgsql_servers_wrunlock(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql servers to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (runtime_pgsql_query_rules) { + save_pgsql_query_rules_from_runtime(true); } - } else { - if (is_admin_command_or_alias(LOAD_MYSQL_SERVERS_FROM_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->mysql_servers_wrlock(); - SPA->load_mysql_servers_to_runtime(); - SPA->mysql_servers_wrunlock(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql servers to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (runtime_mysql_query_rules_fast_routing) { + save_mysql_query_rules_fast_routing_from_runtime(true); } - } - - if ( - (query_no_space_length==strlen("LOAD MYSQL SERVERS FROM CONFIG") && (!strncasecmp("LOAD MYSQL SERVERS FROM CONFIG",query_no_space, query_no_space_length) || - !strncasecmp("LOAD PGSQL SERVERS FROM CONFIG", query_no_space, query_no_space_length) ))) { - - proxy_info("Received %s command\n", query_no_space); - if (GloVars.configfile_open) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); - if (GloVars.confFile->OpenFile(NULL)==true) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - int rows=0; - if (is_pgsql) { - rows=SPA->proxysql_config().Read_PgSQL_Servers_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql servers from CONFIG\n"); - } else { - rows=SPA->proxysql_config().Read_MySQL_Servers_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql servers from CONFIG\n"); - } - SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); - GloVars.confFile->CloseFile(); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); - char *s=(char *)"Unable to open or parse config file %s"; - char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); - sprintf(m,s,GloVars.config_file); - SPA->send_error_msg_to_client(sess, m); - free(m); - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); - SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + if (runtime_pgsql_query_rules_fast_routing) { + save_pgsql_query_rules_fast_routing_from_runtime(true); } - return false; - } - - if (is_pgsql) { - if (is_admin_command_or_alias(SAVE_PGSQL_SERVERS_TO_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->pgsql_servers_wrlock(); - SPA->save_pgsql_servers_runtime_to_database(false); - SPA->pgsql_servers_wrunlock(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved pgsql servers from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (runtime_scheduler) { + save_scheduler_runtime_to_database(true); } - } else { - if (is_admin_command_or_alias(SAVE_MYSQL_SERVERS_TO_MEMORY, query_no_space, query_no_space_length)) { - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - SPA->mysql_servers_wrlock(); - SPA->save_mysql_servers_runtime_to_database(false); - SPA->mysql_servers_wrunlock(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved mysql servers from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; + if (runtime_restapi_routes) { + proxysql_restapi().save_restapi_runtime_to_database(true); } - } - } - - if ((query_no_space_length>22) && ( (!strncasecmp("SAVE PROXYSQL SERVERS ", query_no_space, 22)) || (!strncasecmp("LOAD PROXYSQL SERVERS ", query_no_space, 22))) ) { - - if (FlushCommandWrapper(sess, "proxysql_servers", query_no_space, query_no_space_length) == true) - return false; -/* - string modname = "proxysql_servers"; - tuple, vector>& t = load_save_disk_commands[modname]; - if (FlushCommandWrapper(sess, get<1>(t), query_no_space, query_no_space_length, modname, "disk_to_memory") == true) - return false; - - if (FlushCommandWrapper(sess, get<2>(t), query_no_space, query_no_space_length, modname, "memory_to_disk") == true) - return false; -*/ - if ( - (query_no_space_length==strlen("LOAD PROXYSQL SERVERS FROM MEMORY") && !strncasecmp("LOAD PROXYSQL SERVERS FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD PROXYSQL SERVERS FROM MEM") && !strncasecmp("LOAD PROXYSQL SERVERS FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD PROXYSQL SERVERS TO RUNTIME") && !strncasecmp("LOAD PROXYSQL SERVERS TO RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD PROXYSQL SERVERS TO RUN") && !strncasecmp("LOAD PROXYSQL SERVERS TO RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - //SPA->mysql_servers_wrlock(); - // before calling load_proxysql_servers_to_runtime() we release - // sql_query_global_mutex to prevent a possible deadlock due to - // a race condition - // load_proxysql_servers_to_runtime() calls ProxySQL_Cluster::load_servers_list() - // that then calls ProxySQL_Cluster_Nodes::load_servers_list(), holding a mutex - pthread_mutex_unlock(&SPA->sql_query_global_mutex); - SPA->load_proxysql_servers_to_runtime(true); - // we re-acquired the mutex because it will be released by the calling function - pthread_mutex_lock(&SPA->sql_query_global_mutex); - //SPA->mysql_servers_wrunlock(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded ProxySQL servers to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - if ( - (query_no_space_length==strlen("SAVE PROXYSQL SERVERS TO MEMORY") && !strncasecmp("SAVE PROXYSQL SERVERS TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE PROXYSQL SERVERS TO MEM") && !strncasecmp("SAVE PROXYSQL SERVERS TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE PROXYSQL SERVERS FROM RUNTIME") && !strncasecmp("SAVE PROXYSQL SERVERS FROM RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE PROXYSQL SERVERS FROM RUN") && !strncasecmp("SAVE PROXYSQL SERVERS FROM RUN",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - //SPA->mysql_servers_wrlock(); - // before save_proxysql_servers_runtime_to_database() we release - // sql_query_global_mutex to prevent a possible deadlock due to - // a race condition - // save_proxysql_servers_runtime_to_database() calls ProxySQL_Cluster::dump_table_proxysql_servers() - // that then holds a mutex - pthread_mutex_unlock(&SPA->sql_query_global_mutex); - SPA->save_proxysql_servers_runtime_to_database(false); - // we re-acquired the mutex because it will be released by the calling function - pthread_mutex_lock(&SPA->sql_query_global_mutex); - //SPA->mysql_servers_wrunlock(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved ProxySQL servers from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - - if ( - (query_no_space_length==strlen("LOAD PROXYSQL SERVERS FROM CONFIG") && !strncasecmp("LOAD PROXYSQL SERVERS FROM CONFIG",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - if (GloVars.configfile_open) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); - if (GloVars.confFile->OpenFile(NULL)==true) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - int rows=0; - rows=SPA->proxysql_config().Read_ProxySQL_Servers_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded ProxySQL servers from CONFIG\n"); - SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); - GloVars.confFile->CloseFile(); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); - char *s=(char *)"Unable to open or parse config file %s"; - char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); - sprintf(m,s,GloVars.config_file); - SPA->send_error_msg_to_client(sess, m); - free(m); - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); - SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + if (runtime_checksums_values) { + dump_checksums_values_table(); } - return false; - } - - } - - if ((query_no_space_length>20) && (( (!strncasecmp("SAVE MYSQL FIREWALL ", query_no_space, 20)) || (!strncasecmp("LOAD MYSQL FIREWALL ", query_no_space, 20))) || - (!strncasecmp("SAVE PGSQL FIREWALL ", query_no_space, 20) || (!strncasecmp("LOAD PGSQL FIREWALL ", query_no_space, 20)))) ) { - - const std::string modname = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? "pgsql_firewall" : "mysql_firewall"; - - if (FlushCommandWrapper(sess, modname, query_no_space, query_no_space_length) == true) - return false; - - if ( - (query_no_space_length==strlen("LOAD MYSQL FIREWALL FROM CONFIG") && (!strncasecmp("LOAD MYSQL FIREWALL FROM CONFIG",query_no_space, query_no_space_length) || - !strncasecmp("LOAD PGSQL FIREWALL FROM CONFIG", query_no_space, query_no_space_length))) - ) { - proxy_info("Received %s command\n", query_no_space); - if (GloVars.configfile_open) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); - if (GloVars.confFile->OpenFile(NULL)==true) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - int rows=0; - // FIXME: not implemented yet - if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { - // rows=SPA->proxysql_config().Read_PgSQL_Firewall_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql firewall from CONFIG\n"); - } else { - // rows=SPA->proxysql_config().Read_MySQL_Firewall_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql firewall from CONFIG\n"); - } - SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); - GloVars.confFile->CloseFile(); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); - char *s=(char *)"Unable to open or parse config file %s"; - char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); - sprintf(m,s,GloVars.config_file); - SPA->send_error_msg_to_client(sess, m); - free(m); - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); - SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + if (runtime_coredump_filters) { + dump_coredump_filter_values_table(); } - return false; - } - - if ( - (query_no_space_length==strlen("LOAD MYSQL FIREWALL FROM MEMORY") && !strncasecmp("LOAD MYSQL FIREWALL FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD MYSQL FIREWALL FROM MEM") && !strncasecmp("LOAD MYSQL FIREWALL FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD MYSQL FIREWALL TO RUNTIME") && !strncasecmp("LOAD MYSQL FIREWALL TO RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD MYSQL FIREWALL TO RUN") && !strncasecmp("LOAD MYSQL FIREWALL TO RUN",query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("LOAD PGSQL FIREWALL FROM MEMORY") && !strncasecmp("LOAD PGSQL FIREWALL FROM MEMORY", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("LOAD PGSQL FIREWALL FROM MEM") && !strncasecmp("LOAD PGSQL FIREWALL FROM MEM", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("LOAD PGSQL FIREWALL TO RUNTIME") && !strncasecmp("LOAD PGSQL FIREWALL TO RUNTIME", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("LOAD PGSQL FIREWALL TO RUN") && !strncasecmp("LOAD PGSQL FIREWALL TO RUN", query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - const char* err = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? SPA->load_pgsql_firewall_to_runtime() : SPA->load_mysql_firewall_to_runtime(); - if (err==NULL) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql firewall to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - } else { - SPA->send_error_msg_to_client(sess, err); +#ifdef PROXYSQLCLICKHOUSE + if (runtime_clickhouse_users) { + save_clickhouse_users_runtime_to_database(true); } - return false; - } - - if ( - (query_no_space_length==strlen("SAVE MYSQL FIREWALL TO MEMORY") && !strncasecmp("SAVE MYSQL FIREWALL TO MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE MYSQL FIREWALL TO MEM") && !strncasecmp("SAVE MYSQL FIREWALL TO MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE MYSQL FIREWALL FROM RUNTIME") && !strncasecmp("SAVE MYSQL FIREWALL FROM RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SAVE MYSQL FIREWALL FROM RUN") && !strncasecmp("SAVE MYSQL FIREWALL FROM RUN",query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE PGSQL FIREWALL TO MEMORY") && !strncasecmp("SAVE PGSQL FIREWALL TO MEMORY", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE PGSQL FIREWALL TO MEM") && !strncasecmp("SAVE PGSQL FIREWALL TO MEM", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE PGSQL FIREWALL FROM RUNTIME") && !strncasecmp("SAVE PGSQL FIREWALL FROM RUNTIME", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE PGSQL FIREWALL FROM RUN") && !strncasecmp("SAVE PGSQL FIREWALL FROM RUN", query_no_space, query_no_space_length)) +#endif /* PROXYSQLCLICKHOUSE */ - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { - SPA->save_pgsql_firewall_from_runtime(false); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved pgsql firewall from RUNTIME\n"); - } else { - SPA->save_mysql_firewall_from_runtime(false); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved mysql firewall from RUNTIME\n"); + } + if (monitor_mysql_server_group_replication_log) { + if (GloMyMon) { + GloMyMon->populate_monitor_mysql_server_group_replication_log(); } - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; } - } - - if ((query_no_space_length>23) && ( (!strncasecmp("SAVE MYSQL QUERY RULES ", query_no_space, 23)) || (!strncasecmp("LOAD MYSQL QUERY RULES ", query_no_space, 23)) || - (!strncasecmp("SAVE PGSQL QUERY RULES ", query_no_space, 23)) || (!strncasecmp("LOAD PGSQL QUERY RULES ", query_no_space, 23)) - ) ) { - - const std::string modname = (query_no_space[5] == 'P' || query_no_space[5] == 'p') ? "pgsql_query_rules" : "mysql_query_rules"; - if (FlushCommandWrapper(sess, modname, query_no_space, query_no_space_length) == true) - return false; - - if ( - (query_no_space_length==strlen("LOAD MYSQL QUERY RULES FROM CONFIG") && (!strncasecmp("LOAD MYSQL QUERY RULES FROM CONFIG",query_no_space, query_no_space_length) || - !strncasecmp("LOAD PGSQL QUERY RULES FROM CONFIG", query_no_space, query_no_space_length))) - ) { - proxy_info("Received %s command\n", query_no_space); - if (GloVars.configfile_open) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); - if (GloVars.confFile->OpenFile(NULL)==true) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - int rows=0; - if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { - rows = SPA->proxysql_config().Read_PgSQL_Query_Rules_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql query rules from CONFIG\n"); - } else { - rows = SPA->proxysql_config().Read_MySQL_Query_Rules_from_configfile(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql query rules from CONFIG\n"); - } - SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); - GloVars.confFile->CloseFile(); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); - char *s=(char *)"Unable to open or parse config file %s"; - char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); - sprintf(m,s,GloVars.config_file); - SPA->send_error_msg_to_client(sess, m); - free(m); - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); - SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); + if (monitor_mysql_server_galera_log) { + if (GloMyMon) { + GloMyMon->populate_monitor_mysql_server_galera_log(); } - return false; } - - if ( - (query_no_space_length==strlen("LOAD MYSQL QUERY RULES FROM MEMORY") && !strncasecmp("LOAD MYSQL QUERY RULES FROM MEMORY",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD MYSQL QUERY RULES FROM MEM") && !strncasecmp("LOAD MYSQL QUERY RULES FROM MEM",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD MYSQL QUERY RULES TO RUNTIME") && !strncasecmp("LOAD MYSQL QUERY RULES TO RUNTIME",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("LOAD MYSQL QUERY RULES TO RUN") && !strncasecmp("LOAD MYSQL QUERY RULES TO RUN",query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("LOAD PGSQL QUERY RULES FROM MEMORY") && !strncasecmp("LOAD PGSQL QUERY RULES FROM MEMORY", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("LOAD PGSQL QUERY RULES FROM MEM") && !strncasecmp("LOAD PGSQL QUERY RULES FROM MEM", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("LOAD PGSQL QUERY RULES TO RUNTIME") && !strncasecmp("LOAD PGSQL QUERY RULES TO RUNTIME", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("LOAD PGSQL QUERY RULES TO RUN") && !strncasecmp("LOAD PGSQL QUERY RULES TO RUN", query_no_space, query_no_space_length)) - - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - char* err = NULL; - - if (query_no_space[5] == 'P' || query_no_space[5] == 'p') - err = SPA->load_pgsql_query_rules_to_runtime(); - else - err = SPA->load_mysql_query_rules_to_runtime(); - - if (err==NULL) { - if (query_no_space[5] == 'P' || query_no_space[5] == 'p') - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql query rules to RUNTIME\n"); - else - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql query rules to RUNTIME\n"); - - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - } else { - SPA->send_error_msg_to_client(sess, err); + if (monitor_mysql_server_aws_aurora_log) { + if (GloMyMon) { + GloMyMon->populate_monitor_mysql_server_aws_aurora_log(); } - return false; } - - if ( - (query_no_space_length == strlen("SAVE MYSQL QUERY RULES TO MEMORY") && !strncasecmp("SAVE MYSQL QUERY RULES TO MEMORY", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE MYSQL QUERY RULES TO MEM") && !strncasecmp("SAVE MYSQL QUERY RULES TO MEM", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE MYSQL QUERY RULES FROM RUNTIME") && !strncasecmp("SAVE MYSQL QUERY RULES FROM RUNTIME", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE MYSQL QUERY RULES FROM RUN") && !strncasecmp("SAVE MYSQL QUERY RULES FROM RUN", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE PGSQL QUERY RULES TO MEMORY") && !strncasecmp("SAVE PGSQL QUERY RULES TO MEMORY", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE PGSQL QUERY RULES TO MEM") && !strncasecmp("SAVE PGSQL QUERY RULES TO MEM", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE PGSQL QUERY RULES FROM RUNTIME") && !strncasecmp("SAVE PGSQL QUERY RULES FROM RUNTIME", query_no_space, query_no_space_length)) - || - (query_no_space_length == strlen("SAVE PGSQL QUERY RULES FROM RUN") && !strncasecmp("SAVE PGSQL QUERY RULES FROM RUN", query_no_space, query_no_space_length)) - - ) { - proxy_info("Received %s command\n", query_no_space); - ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; - if (query_no_space[5] == 'P' || query_no_space[5] == 'p') { - SPA->save_pgsql_query_rules_from_runtime(false); - SPA->save_pgsql_query_rules_fast_routing_from_runtime(false); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved pgsql query rules from RUNTIME\n"); - } else { - SPA->save_mysql_query_rules_from_runtime(false); - SPA->save_mysql_query_rules_fast_routing_from_runtime(false); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved mysql query rules from RUNTIME\n"); + if (monitor_mysql_server_aws_aurora_check_status) { + if (GloMyMon) { + GloMyMon->populate_monitor_mysql_server_aws_aurora_check_status(); } - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; } + //pthread_mutex_unlock(&admin_mutex); } + if ( + stats_mysql_processlist || stats_mysql_connection_pool || stats_mysql_connection_pool_reset || + stats_mysql_query_digest || stats_mysql_query_digest_reset || stats_mysql_errors || + stats_mysql_errors_reset || stats_mysql_global || stats_memory_metrics || + stats_mysql_commands_counters || stats_mysql_query_rules || stats_mysql_users || + stats_mysql_gtid_executed || stats_mysql_free_connections + ) { + ret = true; + } + + return ret; +} - if ((query_no_space_length>21) && ( (!strncasecmp("SAVE ADMIN VARIABLES ", query_no_space, 21)) || (!strncasecmp("LOAD ADMIN VARIABLES ", query_no_space, 21))) ) { - - if ( is_admin_command_or_alias(LOAD_ADMIN_VARIABLES_TO_MEMORY, query_no_space, query_no_space_length) ) { - l_free(*ql,*q); - *q=l_strdup("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables WHERE variable_name LIKE 'admin-%'"); - *ql=strlen(*q)+1; - return true; - } - - if ( is_admin_command_or_alias(SAVE_ADMIN_VARIABLES_FROM_MEMORY, query_no_space, query_no_space_length) ) { - l_free(*ql,*q); - *q=l_strdup("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables WHERE variable_name LIKE 'admin-%'"); - *ql=strlen(*q)+1; - return true; - } - - if ( is_admin_command_or_alias(LOAD_ADMIN_VARIABLES_FROM_MEMORY, query_no_space, query_no_space_length) ) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->load_admin_variables_to_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded admin variables to RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; - } - - if ( - (query_no_space_length==strlen("LOAD ADMIN VARIABLES FROM CONFIG") && !strncasecmp("LOAD ADMIN VARIABLES FROM CONFIG",query_no_space, query_no_space_length)) - ) { - proxy_info("Received %s command\n", query_no_space); - if (GloVars.configfile_open) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); - if (GloVars.confFile->OpenFile(NULL)==true) { - int rows=0; - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - rows=SPA->proxysql_config().Read_Global_Variables_from_configfile("admin"); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded admin variables from CONFIG\n"); - SPA->send_ok_msg_to_client(sess, NULL, rows, query_no_space); - GloVars.confFile->CloseFile(); - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); - char *s=(char *)"Unable to open or parse config file %s"; - char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); - sprintf(m,s,GloVars.config_file); - SPA->send_error_msg_to_client(sess, m); - free(m); - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); - SPA->send_error_msg_to_client(sess, (char *)"Config file unknown"); - } - return false; - } - if ( is_admin_command_or_alias(SAVE_ADMIN_VARIABLES_TO_MEMORY, query_no_space, query_no_space_length) ) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->save_admin_variables_from_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saved admin variables from RUNTIME\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - return false; +SQLite3_result * ProxySQL_Admin::generate_show_fields_from(const char *tablename, char **err) { + char *tn=NULL; // tablename + // note that tablename is passed with a trailing ' + tn=(char *)malloc(strlen(tablename) + 1); + unsigned int i=0, j=0; + while (iexecute_statement(q2, &error , &cols , &affected_rows , &resultset); + if (error) { + proxy_error("Error on %s : %s\n", q2, error); + free(q2); + *err=strdup(error); + free(error); + if (resultset) delete resultset; + free(tn); + return NULL; + } + if (resultset==NULL) { + free(q2); + free(tn); + return NULL; } - if (!strncasecmp("SAVE CONFIG TO FILE", query_no_space, strlen("SAVE CONFIG TO FILE"))) { - std::string fileName = query_no_space + strlen("SAVE CONFIG TO FILE"); + if (resultset->rows_count==0) { + free(q2); + free(tn); + delete resultset; + *err=strdup((char *)"Table does not exist"); + return NULL; + } - fileName.erase(0, fileName.find_first_not_of("\t\n\v\f\r ")); - fileName.erase(fileName.find_last_not_of("\t\n\v\f\r ") + 1); - if (fileName.size() == 0) { - proxy_error("ProxySQL Admin Error: empty file name\n"); - SPA->send_error_msg_to_client(sess, (char *)"ProxySQL Admin Error: empty file name"); - return false; + SQLite3_result *result=new SQLite3_result(6); + result->add_column_definition(SQLITE_TEXT,"Field"); + result->add_column_definition(SQLITE_TEXT,"Type"); + result->add_column_definition(SQLITE_TEXT,"Null"); + result->add_column_definition(SQLITE_TEXT,"Key"); + result->add_column_definition(SQLITE_TEXT,"Default"); + result->add_column_definition(SQLITE_TEXT,"Extra"); + char *pta[6]; + pta[1]=(char *)"varchar(255)"; + pta[2]=(char *)"NO"; + pta[4]=(char *)""; + pta[5]=(char *)""; + free(q2); + for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { + SQLite3_row *r=*it; + pta[0]=r->fields[1]; + pta[2]=(char *)"YES"; + if (r->fields[3]) { + if (strcmp(r->fields[3],"1")==0) { + pta[2]=(char *)"NO"; + } } - std::string data; - data.reserve(100000); - data += config_header; - int rc = pa->proxysql_config().Write_Global_Variables_to_configfile(data); - rc = pa->proxysql_config().Write_MySQL_Users_to_configfile(data); - rc = pa->proxysql_config().Write_MySQL_Query_Rules_to_configfile(data); - rc = pa->proxysql_config().Write_MySQL_Servers_to_configfile(data); - rc = pa->proxysql_config().Write_PgSQL_Users_to_configfile(data); - rc = pa->proxysql_config().Write_PgSQL_Query_Rules_to_configfile(data); - rc = pa->proxysql_config().Write_PgSQL_Servers_to_configfile(data); - rc = pa->proxysql_config().Write_Scheduler_to_configfile(data); - rc = pa->proxysql_config().Write_ProxySQL_Servers_to_configfile(data); - if (rc) { - std::stringstream ss; - proxy_error("ProxySQL Admin Error: Cannot extract configuration\n"); - SPA->send_error_msg_to_client(sess, (char *)"ProxySQL Admin Error: Cannot extract configuration"); - return false; - } else { - std::ofstream out; - out.open(fileName); - if (out.is_open()) { - out << data; - out.close(); - if (!out) { - std::stringstream ss; - ss << "ProxySQL Admin Error: Error writing file " << fileName; - proxy_error("%s\n", ss.str().c_str()); - SPA->send_error_msg_to_client(sess, (char*)ss.str().c_str()); - return false; - } else { - std::stringstream ss; - ss << "File " << fileName << " is saved."; - SPA->send_ok_msg_to_client(sess, (char*)ss.str().c_str(), 0, query_no_space); - return false; - } - } else { - std::stringstream ss; - ss << "ProxySQL Admin Error: Cannot open file " << fileName; - proxy_error("%s\n", ss.str().c_str()); - SPA->send_error_msg_to_client(sess, (char*)ss.str().c_str()); - return false; + pta[3]=(char *)""; + if (r->fields[5]) { + if (strcmp(r->fields[5],"0")) { + pta[3]=(char *)"PRI"; } } + result->add_row(pta); } - - return true; + delete resultset; + free(tn); + return result; } -// Explicitly instantiate the required template class and member functions -template void ProxySQL_Admin::send_ok_msg_to_client(Client_Session&, char const*, int, char const*); -template void ProxySQL_Admin::send_ok_msg_to_client(Client_Session&, char const*, int, char const*); -template void ProxySQL_Admin::send_error_msg_to_client(Client_Session&, char const*, unsigned short); -template void ProxySQL_Admin::send_error_msg_to_client(Client_Session&, char const*, unsigned short); -template int ProxySQL_Admin::FlushDigestTableToDisk<(SERVER_TYPE)0>(SQLite3DB*); - -void ProxySQL_Admin::flush_configdb() { // see #923 - wrlock(); - admindb->execute((char *)"DETACH DATABASE disk"); - delete configdb; - configdb=new SQLite3DB(); - configdb->open((char *)GloVars.admindb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); - __attach_db(admindb, configdb, (char *)"disk"); - // Fully synchronous is not required. See to #1055 - // https://sqlite.org/pragma.html#pragma_synchronous - configdb->execute("PRAGMA synchronous=0"); - wrunlock(); -} +SQLite3_result * ProxySQL_Admin::generate_show_table_status(const char *tablename, char **err) { + char *pta[18]; + pta[0]=NULL; + char *tn=NULL; // tablename + // note that tablename is passed with a trailing ' + tn=(char *)malloc(strlen(tablename)+1); + unsigned int i=0, j=0; + while (iexecute_statement(q2, &error , &cols , &affected_rows , &resultset); + if (error) { + proxy_error("Error on %s : %s\n", q2, error); + free(q2); + *err=strdup(error); + free(error); + if (resultset) delete resultset; + free(tn); + return NULL; + } -bool ProxySQL_Admin::GenericRefreshStatistics(const char *query_no_space, unsigned int query_no_space_length, bool admin) { - bool ret=false; - bool refresh=false; - bool stats_mysql_processlist=false; - bool stats_mysql_free_connections=false; - bool stats_mysql_connection_pool=false; - bool stats_mysql_connection_pool_reset=false; - bool stats_mysql_query_digest=false; - bool stats_mysql_query_digest_reset=false; - bool stats_mysql_errors=false; - bool stats_mysql_errors_reset=false; - bool stats_mysql_global=false; - bool stats_memory_metrics=false; - bool stats_mysql_commands_counters=false; - bool stats_mysql_query_rules=false; - bool stats_mysql_users=false; - bool stats_mysql_gtid_executed=false; - bool stats_mysql_client_host_cache=false; - bool stats_mysql_client_host_cache_reset=false; - bool dump_global_variables=false; + if (resultset==NULL) { + free(q2); + free(tn); + return NULL; + } - bool runtime_scheduler=false; - bool runtime_restapi_routes=false; - bool runtime_mysql_users=false; - bool runtime_mysql_firewall=false; - bool runtime_mysql_ldap_mapping=false; - bool runtime_mysql_servers=false; - bool runtime_mysql_query_rules=false; - bool runtime_mysql_query_rules_fast_routing=false; + if (resultset->rows_count==0) { + free(q2); + free(tn); + delete resultset; + *err=strdup((char *)"Table does not exist"); + return NULL; + } + SQLite3_result *result=new SQLite3_result(18); + result->add_column_definition(SQLITE_TEXT,"Name"); + result->add_column_definition(SQLITE_TEXT,"Engine"); + result->add_column_definition(SQLITE_TEXT,"Version"); + result->add_column_definition(SQLITE_TEXT,"Row_format"); + result->add_column_definition(SQLITE_TEXT,"Rows"); + result->add_column_definition(SQLITE_TEXT,"Avg_row_length"); + result->add_column_definition(SQLITE_TEXT,"Data_length"); + result->add_column_definition(SQLITE_TEXT,"Max_data_length"); + result->add_column_definition(SQLITE_TEXT,"Index_length"); + result->add_column_definition(SQLITE_TEXT,"Data_free"); + result->add_column_definition(SQLITE_TEXT,"Auto_increment"); + result->add_column_definition(SQLITE_TEXT,"Create_time"); + result->add_column_definition(SQLITE_TEXT,"Update_time"); + result->add_column_definition(SQLITE_TEXT,"Check_time"); + result->add_column_definition(SQLITE_TEXT,"Collation"); + result->add_column_definition(SQLITE_TEXT,"Checksum"); + result->add_column_definition(SQLITE_TEXT,"Create_options"); + result->add_column_definition(SQLITE_TEXT,"Comment"); + pta[0]=tn; + pta[1]=(char *)"SQLite"; + pta[2]=(char *)"10"; + pta[3]=(char *)"Dynamic"; + delete resultset; + sprintf(q2,"SELECT COUNT(*) FROM %s",tn); + admindb->execute_statement(q2, &error , &cols , &affected_rows , &resultset); + char buf[20]; + sprintf(buf,"%d",resultset->rows_count); + pta[4]=buf; + delete resultset; + free(q2); + pta[5]=(char *)"0"; + pta[6]=(char *)"0"; + pta[7]=(char *)"0"; + pta[8]=(char *)"0"; + pta[9]=(char *)"0"; + pta[10]=(char *)"NULL"; + pta[11]=(char *)"0000-00-00 00:00:00"; + pta[12]=(char *)"0000-00-00 00:00:00"; + pta[13]=(char *)"0000-00-00 00:00:00"; + pta[14]=(char *)"utf8_bin"; + pta[15]=(char *)"NULL"; + pta[16]=(char *)""; + pta[17]=(char *)""; + result->add_row(pta); + free(tn); + return result; +} - bool runtime_pgsql_users = false; - bool runtime_pgsql_firewall = false; - bool runtime_pgsql_ldap_mapping = false; - bool runtime_pgsql_servers = false; - bool runtime_pgsql_query_rules = false; - bool runtime_pgsql_query_rules_fast_routing = false; - bool runtime_proxysql_servers=false; - bool runtime_checksums_values=false; +template +void admin_session_handler(Client_Session sess, void *_pa, PtrSize_t *pkt); - bool runtime_coredump_filters=false; +void ProxySQL_Admin::vacuum_stats(bool is_admin) { + if (variables.vacuum_stats==false) { + return; + } + const vector tablenames = { + "stats_mysql_commands_counters", + "stats_mysql_free_connections", + "stats_mysql_connection_pool", + "stats_mysql_connection_pool_reset", + "stats_mysql_prepared_statements_info", + "stats_mysql_processlist", + "stats_mysql_query_digest", + "stats_mysql_query_digest_reset", + "stats_mysql_query_rules", + "stats_mysql_users", + "stats_proxysql_servers_checksums", + "stats_proxysql_servers_metrics", + "stats_proxysql_servers_status", + }; + string s; + SQLite3DB *tmpdb = NULL; + if (is_admin == true) { + tmpdb = admindb; + } else { + tmpdb = statsdb; + } + for (auto it = tablenames.begin(); it != tablenames.end(); it++) { + s = "DELETE FROM "; + if (is_admin == true) s+= "stats."; + s += *it; + tmpdb->execute(s.c_str()); + } + s = "VACUUM"; + if (is_admin == true) + s+= " stats"; + tmpdb->execute(s.c_str()); +} - bool stats_mysql_prepared_statements_info = false; -#ifdef PROXYSQLCLICKHOUSE - bool runtime_clickhouse_users = false; -#endif /* PROXYSQLCLICKHOUSE */ +void *child_mysql(void *arg) { + if (GloMTH == nullptr) { return NULL; } - bool monitor_mysql_server_group_replication_log=false; + pthread_attr_t thread_attr; + size_t tmp_stack_size=0; + if (!pthread_attr_init(&thread_attr)) { + if (!pthread_attr_getstacksize(&thread_attr , &tmp_stack_size )) { + __sync_fetch_and_add(&GloVars.statuses.stack_memory_admin_threads,tmp_stack_size); + } + } - bool monitor_mysql_server_galera_log=false; + arg_proxysql_adm*myarg = (arg_proxysql_adm*)arg; + int client = myarg->client_t; - bool monitor_mysql_server_aws_aurora_log=false; - bool monitor_mysql_server_aws_aurora_check_status=false; + //struct sockaddr *addr = arg->addr; + //socklen_t addr_size; - bool stats_proxysql_servers_checksums = false; - bool stats_proxysql_servers_metrics = false; - bool stats_proxysql_message_metrics = false; - bool stats_proxysql_message_metrics_reset = false; + GloMTH->wrlock(); + { + char *s=GloMTH->get_variable((char *)"server_capabilities"); + mysql_thread___server_capabilities=atoi(s); + free(s); + } + GloMTH->wrunlock(); - //bool stats_proxysql_servers_status = false; // temporary disabled because not implemented + struct pollfd fds[1]; + nfds_t nfds=1; + int rc; + pthread_mutex_unlock(&sock_mutex); + MySQL_Thread *mysql_thr=new MySQL_Thread(); + mysql_thr->curtime=monotonic_time(); + GloQPro->init_thread(); + mysql_thr->refresh_variables(); + MySQL_Session *sess=mysql_thr->create_new_session_and_client_data_stream(client); + sess->thread=mysql_thr; + sess->session_type = PROXYSQL_SESSION_ADMIN; + sess->handler_function=admin_session_handler; + MySQL_Data_Stream *myds=sess->client_myds; + sess->start_time=mysql_thr->curtime; - if (strcasestr(query_no_space,"processlist")) - // This will match the following usecases: - // SHOW PROCESSLIST - // SHOW FULL PROCESSLIST - // SELECT * FROM stats_mysql_processlist - { stats_mysql_processlist=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_query_digest")) - { stats_mysql_query_digest=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_query_digest_reset")) - { stats_mysql_query_digest_reset=true; refresh=true; } - if (stats_mysql_query_digest_reset == true && stats_mysql_query_digest == true) { - int nd = 0; - int ndr= 0; - char *c = NULL; - char *_ret = NULL; - c = (char *)query_no_space; - _ret = NULL; - while ((_ret = strstr(c,"stats_mysql_query_digest_reset"))) { - ndr++; - c = _ret + strlen("stats_mysql_query_digest_reset"); - } - c = (char *)query_no_space; - _ret = NULL; - while ((_ret = strstr(c,"stats_mysql_query_digest"))) { - nd++; - c = _ret + strlen("stats_mysql_query_digest"); + sess->client_myds->client_addrlen=myarg->addr_size; + sess->client_myds->client_addr=myarg->addr; + + switch (sess->client_myds->client_addr->sa_family) { + case AF_INET: { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)sess->client_myds->client_addr; + char buf[INET_ADDRSTRLEN]; + inet_ntop(sess->client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); + sess->client_myds->addr.addr = strdup(buf); + sess->client_myds->addr.port = htons(ipv4->sin_port); + break; } - if (nd == ndr) { - stats_mysql_query_digest = false; - } - } - if (strstr(query_no_space,"stats_mysql_errors")) - { stats_mysql_errors=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_errors_reset")) - { stats_mysql_errors_reset=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_global")) - { stats_mysql_global=true; refresh=true; } - if (strstr(query_no_space,"stats_memory_metrics")) - { stats_memory_metrics=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_connection_pool_reset")) - { - stats_mysql_connection_pool_reset=true; refresh=true; - } else { - if (strstr(query_no_space,"stats_mysql_connection_pool")) - { stats_mysql_connection_pool=true; refresh=true; } - } - if (strstr(query_no_space,"stats_mysql_free_connections")) - { stats_mysql_free_connections=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_commands_counters")) - { stats_mysql_commands_counters=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_query_rules")) - { stats_mysql_query_rules=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_users")) - { stats_mysql_users=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_gtid_executed")) - { stats_mysql_gtid_executed=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_client_host_cache")) - { stats_mysql_client_host_cache=true; refresh=true; } - if (strstr(query_no_space,"stats_mysql_client_host_cache_reset")) - { stats_mysql_client_host_cache_reset=true; refresh=true; } - - if (strstr(query_no_space,"stats_proxysql_servers_checksums")) - { stats_proxysql_servers_checksums = true; refresh = true; } - if (strstr(query_no_space,"stats_proxysql_servers_metrics")) - { stats_proxysql_servers_metrics = true; refresh = true; } - if (strstr(query_no_space,"stats_proxysql_message_metrics")) - { stats_proxysql_message_metrics=true; refresh=true; } - if (strstr(query_no_space,"stats_proxysql_message_metrics_reset")) - { stats_proxysql_message_metrics_reset=true; refresh=true; } - - // temporary disabled because not implemented -/* - if (strstr(query_no_space,"stats_proxysql_servers_status")) - { stats_proxysql_servers_status = true; refresh = true; } -*/ - if (strstr(query_no_space,"stats_mysql_prepared_statements_info")) { - stats_mysql_prepared_statements_info=true; refresh=true; - } - if (admin) { - if (strstr(query_no_space,"global_variables")) - { dump_global_variables=true; refresh=true; } - if (strstr(query_no_space,"runtime_")) { - if ( - strstr(query_no_space,"runtime_mysql_servers") - || - strstr(query_no_space,"runtime_mysql_replication_hostgroups") - || - strstr(query_no_space,"runtime_mysql_group_replication_hostgroups") - || - strstr(query_no_space,"runtime_mysql_galera_hostgroups") - || - strstr(query_no_space,"runtime_mysql_aws_aurora_hostgroups") - || - strstr(query_no_space,"runtime_mysql_hostgroup_attributes") - || - strstr(query_no_space,"runtime_mysql_servers_ssl_params") - ) { - runtime_mysql_servers=true; refresh=true; - } - if ( - strstr(query_no_space, "runtime_pgsql_servers") - || - strstr(query_no_space, "runtime_pgsql_replication_hostgroups") - || - strstr(query_no_space, "runtime_pgsql_hostgroup_attributes") - ) { - runtime_pgsql_servers = true; refresh = true; - } - if ( - strstr(query_no_space,"runtime_mysql_firewall_whitelist_rules") - || - strstr(query_no_space,"runtime_mysql_firewall_whitelist_users") - || - strstr(query_no_space,"runtime_mysql_firewall_whitelist_sqli_fingerprints") - ) { - runtime_mysql_firewall=true; refresh=true; - } - if ( - strstr(query_no_space, "runtime_pgsql_firewall_whitelist_rules") - || - strstr(query_no_space, "runtime_pgsql_firewall_whitelist_users") - || - strstr(query_no_space, "runtime_pgsql_firewall_whitelist_sqli_fingerprints") - ) { - runtime_pgsql_firewall = true; refresh = true; - } - if (strstr(query_no_space,"runtime_mysql_users")) { - runtime_mysql_users=true; refresh=true; - } - if (strstr(query_no_space, "runtime_pgsql_users")) { - runtime_pgsql_users = true; refresh = true; - } - if (GloMyLdapAuth) { - if (strstr(query_no_space,"runtime_mysql_ldap_mapping")) { - runtime_mysql_ldap_mapping=true; refresh=true; - } - if (strstr(query_no_space, "runtime_pgsql_ldap_mapping")) { - runtime_mysql_ldap_mapping = true; refresh = true; - } - } - if (strstr(query_no_space,"runtime_mysql_query_rules")) { - runtime_mysql_query_rules=true; refresh=true; - } - if (strstr(query_no_space, "runtime_pgsql_query_rules")) { - runtime_pgsql_query_rules = true; refresh = true; - } - if (strstr(query_no_space,"runtime_mysql_query_rules_fast_routing")) { - runtime_mysql_query_rules_fast_routing=true; refresh=true; - } - if (strstr(query_no_space, "runtime_pgsql_query_rules_fast_routing")) { - runtime_pgsql_query_rules_fast_routing = true; refresh = true; - } - if (strstr(query_no_space,"runtime_scheduler")) { - runtime_scheduler=true; refresh=true; - } - if (strstr(query_no_space,"runtime_restapi_routes")) { - runtime_restapi_routes=true; refresh=true; - } - if (strstr(query_no_space,"runtime_proxysql_servers")) { - runtime_proxysql_servers=true; refresh=true; - } - if (strstr(query_no_space,"runtime_checksums_values")) { - runtime_checksums_values=true; refresh=true; - } - if (strstr(query_no_space,"runtime_coredump_filters")) { - runtime_coredump_filters=true; refresh=true; - } -#ifdef PROXYSQLCLICKHOUSE - if (( GloVars.global.clickhouse_server == true ) && strstr(query_no_space,"runtime_clickhouse_users")) { - runtime_clickhouse_users=true; refresh=true; - } -#endif /* PROXYSQLCLICKHOUSE */ - - } - } - if (strstr(query_no_space,"mysql_server_group_replication_log")) { - monitor_mysql_server_group_replication_log=true; refresh=true; - } - if (strstr(query_no_space,"mysql_server_galera_log")) { - monitor_mysql_server_galera_log=true; refresh=true; - } - if (strstr(query_no_space,"mysql_server_aws_aurora_log")) { - monitor_mysql_server_aws_aurora_log=true; refresh=true; - } - if (strstr(query_no_space,"mysql_server_aws_aurora_check_status")) { - monitor_mysql_server_aws_aurora_check_status=true; refresh=true; - } -// if (stats_mysql_processlist || stats_mysql_connection_pool || stats_mysql_query_digest || stats_mysql_query_digest_reset) { - if (refresh==true) { - //pthread_mutex_lock(&admin_mutex); - //ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - if (stats_mysql_processlist) - stats___mysql_processlist(); - if (stats_mysql_query_digest_reset) { - stats___mysql_query_digests_v2(true, stats_mysql_query_digest, false); - } else { - if (stats_mysql_query_digest) { - stats___mysql_query_digests_v2(false, false, false); - } - } - if (stats_mysql_errors) - stats___mysql_errors(false); - if (stats_mysql_errors_reset) { - stats___mysql_errors(true); - } - if (stats_mysql_connection_pool_reset) { - stats___mysql_connection_pool(true); - } else { - if (stats_mysql_connection_pool) - stats___mysql_connection_pool(false); - } - if (stats_mysql_free_connections) - stats___mysql_free_connections(); - if (stats_mysql_global) - stats___mysql_global(); - if (stats_memory_metrics) - stats___memory_metrics(); - if (stats_mysql_query_rules) - stats___mysql_query_rules(); - if (stats_mysql_commands_counters) - stats___mysql_commands_counters(); - if (stats_mysql_users) - stats___mysql_users(); - if (stats_mysql_gtid_executed) - stats___mysql_gtid_executed(); - - // cluster - if (stats_proxysql_servers_metrics) { - stats___proxysql_servers_metrics(); - } - if (stats_proxysql_servers_checksums) { - stats___proxysql_servers_checksums(); - } - if (stats_proxysql_message_metrics_reset) { - stats___proxysql_message_metrics(true); - } else { - if (stats_proxysql_message_metrics) { - stats___proxysql_message_metrics(false); - } - } - - // temporary disabled because not implemented -// if (stats_proxysql_servers_status) { -// stats___proxysql_servers_status(); -// } - if (stats_mysql_prepared_statements_info) { - stats___mysql_prepared_statements_info(); - } - - if (stats_mysql_client_host_cache) { - stats___mysql_client_host_cache(false); - } - if (stats_mysql_client_host_cache_reset) { - stats___mysql_client_host_cache(true); - } - - if (admin) { - if (dump_global_variables) { - pthread_mutex_lock(&GloVars.checksum_mutex); - admindb->execute("DELETE FROM runtime_global_variables"); // extra - flush_admin_variables___runtime_to_database(admindb, false, false, false, true); - flush_mysql_variables___runtime_to_database(admindb, false, false, false, true); -#ifdef PROXYSQLCLICKHOUSE - flush_clickhouse_variables___runtime_to_database(admindb, false, false, false, true); -#endif /* PROXYSQLCLICKHOUSE */ - flush_sqliteserver_variables___runtime_to_database(admindb, false, false, false, true); - flush_ldap_variables___runtime_to_database(admindb, false, false, false, true); - flush_pgsql_variables___runtime_to_database(admindb, false, false, false, true); - pthread_mutex_unlock(&GloVars.checksum_mutex); - } - if (runtime_mysql_servers) { - int old_hostgroup_manager_verbose = mysql_thread___hostgroup_manager_verbose; - mysql_thread___hostgroup_manager_verbose = 0; - mysql_servers_wrlock(); - save_mysql_servers_runtime_to_database(true); - mysql_servers_wrunlock(); - mysql_thread___hostgroup_manager_verbose = old_hostgroup_manager_verbose; - } - if (runtime_pgsql_servers) { - int old_hostgroup_manager_verbose = pgsql_thread___hostgroup_manager_verbose; - pgsql_thread___hostgroup_manager_verbose = 0; - pgsql_servers_wrlock(); - save_pgsql_servers_runtime_to_database(true); - pgsql_servers_wrunlock(); - pgsql_thread___hostgroup_manager_verbose = old_hostgroup_manager_verbose; - } - if (runtime_proxysql_servers) { - //mysql_servers_wrlock(); - // before save_proxysql_servers_runtime_to_database() we release - // sql_query_global_mutex to prevent a possible deadlock due to - // a race condition - // save_proxysql_servers_runtime_to_database() calls ProxySQL_Cluster::dump_table_proxysql_servers() - pthread_mutex_unlock(&SPA->sql_query_global_mutex); - save_proxysql_servers_runtime_to_database(true); - pthread_mutex_lock(&SPA->sql_query_global_mutex); - //mysql_servers_wrunlock(); - } - if (runtime_mysql_users) { - save_mysql_users_runtime_to_database(true); - } - if (runtime_pgsql_users) { - save_pgsql_users_runtime_to_database(true); - } - if (runtime_mysql_firewall) { - save_mysql_firewall_from_runtime(true); - } - if (runtime_pgsql_firewall) { - save_pgsql_firewall_from_runtime(true); - } - if (runtime_mysql_ldap_mapping) { - save_mysql_ldap_mapping_runtime_to_database(true); - } - if (runtime_pgsql_ldap_mapping) { - save_pgsql_ldap_mapping_runtime_to_database(true); - } - if (runtime_mysql_query_rules) { - save_mysql_query_rules_from_runtime(true); - } - if (runtime_pgsql_query_rules) { - save_pgsql_query_rules_from_runtime(true); - } - if (runtime_mysql_query_rules_fast_routing) { - save_mysql_query_rules_fast_routing_from_runtime(true); - } - if (runtime_pgsql_query_rules_fast_routing) { - save_pgsql_query_rules_fast_routing_from_runtime(true); - } - if (runtime_scheduler) { - save_scheduler_runtime_to_database(true); - } - if (runtime_restapi_routes) { - proxysql_restapi().save_restapi_runtime_to_database(true); - } - if (runtime_checksums_values) { - dump_checksums_values_table(); - } - if (runtime_coredump_filters) { - dump_coredump_filter_values_table(); - } -#ifdef PROXYSQLCLICKHOUSE - if (runtime_clickhouse_users) { - save_clickhouse_users_runtime_to_database(true); - } -#endif /* PROXYSQLCLICKHOUSE */ - - } - if (monitor_mysql_server_group_replication_log) { - if (GloMyMon) { - GloMyMon->populate_monitor_mysql_server_group_replication_log(); - } - } - if (monitor_mysql_server_galera_log) { - if (GloMyMon) { - GloMyMon->populate_monitor_mysql_server_galera_log(); - } - } - if (monitor_mysql_server_aws_aurora_log) { - if (GloMyMon) { - GloMyMon->populate_monitor_mysql_server_aws_aurora_log(); - } - } - if (monitor_mysql_server_aws_aurora_check_status) { - if (GloMyMon) { - GloMyMon->populate_monitor_mysql_server_aws_aurora_check_status(); - } - } - //pthread_mutex_unlock(&admin_mutex); - } - if ( - stats_mysql_processlist || stats_mysql_connection_pool || stats_mysql_connection_pool_reset || - stats_mysql_query_digest || stats_mysql_query_digest_reset || stats_mysql_errors || - stats_mysql_errors_reset || stats_mysql_global || stats_memory_metrics || - stats_mysql_commands_counters || stats_mysql_query_rules || stats_mysql_users || - stats_mysql_gtid_executed || stats_mysql_free_connections - ) { - ret = true; - } - - return ret; -} - - -SQLite3_result * ProxySQL_Admin::generate_show_fields_from(const char *tablename, char **err) { - char *tn=NULL; // tablename - // note that tablename is passed with a trailing ' - tn=(char *)malloc(strlen(tablename) + 1); - unsigned int i=0, j=0; - while (iexecute_statement(q2, &error , &cols , &affected_rows , &resultset); - if (error) { - proxy_error("Error on %s : %s\n", q2, error); - free(q2); - *err=strdup(error); - free(error); - if (resultset) delete resultset; - free(tn); - return NULL; - } - - if (resultset==NULL) { - free(q2); - free(tn); - return NULL; - } - - if (resultset->rows_count==0) { - free(q2); - free(tn); - delete resultset; - *err=strdup((char *)"Table does not exist"); - return NULL; - } - - SQLite3_result *result=new SQLite3_result(6); - result->add_column_definition(SQLITE_TEXT,"Field"); - result->add_column_definition(SQLITE_TEXT,"Type"); - result->add_column_definition(SQLITE_TEXT,"Null"); - result->add_column_definition(SQLITE_TEXT,"Key"); - result->add_column_definition(SQLITE_TEXT,"Default"); - result->add_column_definition(SQLITE_TEXT,"Extra"); - char *pta[6]; - pta[1]=(char *)"varchar(255)"; - pta[2]=(char *)"NO"; - pta[4]=(char *)""; - pta[5]=(char *)""; - free(q2); - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - pta[0]=r->fields[1]; - pta[2]=(char *)"YES"; - if (r->fields[3]) { - if (strcmp(r->fields[3],"1")==0) { - pta[2]=(char *)"NO"; - } - } - pta[3]=(char *)""; - if (r->fields[5]) { - if (strcmp(r->fields[5],"0")) { - pta[3]=(char *)"PRI"; - } - } - result->add_row(pta); - } - delete resultset; - free(tn); - return result; -} - -SQLite3_result * ProxySQL_Admin::generate_show_table_status(const char *tablename, char **err) { - char *pta[18]; - pta[0]=NULL; - char *tn=NULL; // tablename - // note that tablename is passed with a trailing ' - tn=(char *)malloc(strlen(tablename)+1); - unsigned int i=0, j=0; - while (iexecute_statement(q2, &error , &cols , &affected_rows , &resultset); - if (error) { - proxy_error("Error on %s : %s\n", q2, error); - free(q2); - *err=strdup(error); - free(error); - if (resultset) delete resultset; - free(tn); - return NULL; - } - - if (resultset==NULL) { - free(q2); - free(tn); - return NULL; - } - - if (resultset->rows_count==0) { - free(q2); - free(tn); - delete resultset; - *err=strdup((char *)"Table does not exist"); - return NULL; - } - SQLite3_result *result=new SQLite3_result(18); - result->add_column_definition(SQLITE_TEXT,"Name"); - result->add_column_definition(SQLITE_TEXT,"Engine"); - result->add_column_definition(SQLITE_TEXT,"Version"); - result->add_column_definition(SQLITE_TEXT,"Row_format"); - result->add_column_definition(SQLITE_TEXT,"Rows"); - result->add_column_definition(SQLITE_TEXT,"Avg_row_length"); - result->add_column_definition(SQLITE_TEXT,"Data_length"); - result->add_column_definition(SQLITE_TEXT,"Max_data_length"); - result->add_column_definition(SQLITE_TEXT,"Index_length"); - result->add_column_definition(SQLITE_TEXT,"Data_free"); - result->add_column_definition(SQLITE_TEXT,"Auto_increment"); - result->add_column_definition(SQLITE_TEXT,"Create_time"); - result->add_column_definition(SQLITE_TEXT,"Update_time"); - result->add_column_definition(SQLITE_TEXT,"Check_time"); - result->add_column_definition(SQLITE_TEXT,"Collation"); - result->add_column_definition(SQLITE_TEXT,"Checksum"); - result->add_column_definition(SQLITE_TEXT,"Create_options"); - result->add_column_definition(SQLITE_TEXT,"Comment"); - pta[0]=tn; - pta[1]=(char *)"SQLite"; - pta[2]=(char *)"10"; - pta[3]=(char *)"Dynamic"; - delete resultset; - sprintf(q2,"SELECT COUNT(*) FROM %s",tn); - admindb->execute_statement(q2, &error , &cols , &affected_rows , &resultset); - char buf[20]; - sprintf(buf,"%d",resultset->rows_count); - pta[4]=buf; - delete resultset; - free(q2); - pta[5]=(char *)"0"; - pta[6]=(char *)"0"; - pta[7]=(char *)"0"; - pta[8]=(char *)"0"; - pta[9]=(char *)"0"; - pta[10]=(char *)"NULL"; - pta[11]=(char *)"0000-00-00 00:00:00"; - pta[12]=(char *)"0000-00-00 00:00:00"; - pta[13]=(char *)"0000-00-00 00:00:00"; - pta[14]=(char *)"utf8_bin"; - pta[15]=(char *)"NULL"; - pta[16]=(char *)""; - pta[17]=(char *)""; - result->add_row(pta); - free(tn); - return result; -} - -/** - * @brief Helper function that converts the current timezone - * expressed in seconds into a string of the format: - * - '[-]HH:MM:00'. - * Following the same pattern as the possible values returned by the SQL query - * 'SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())' in a MySQL server. - * @return A string holding the specified representation of the - * supplied timezone. - */ -std::string timediff_timezone_offset() { - std::string time_zone_offset {}; - char result[8]; - time_t rawtime; - struct tm *info; - int offset; - - time(&rawtime); - info = localtime(&rawtime); - strftime(result, 8, "%z", info); - offset = (result[0] == '+') ? 1 : 0; - time_zone_offset = ((std::string)(result)).substr(offset, 3-offset) + ":" + ((std::string)(result)).substr(3, 2) + ":00"; - - return time_zone_offset; -} - -template -void admin_session_handler(Client_Session sess, void *_pa, PtrSize_t *pkt) { - - ProxySQL_Admin *pa=(ProxySQL_Admin *)_pa; - bool needs_vacuum = false; - char *error=NULL; - int cols; - int affected_rows = 0; - bool run_query=true; - SQLite3_result *resultset=NULL; - char *strA=NULL; - char *strB=NULL; - int strAl, strBl; - char *query=NULL; - unsigned int query_length = 0; - - if constexpr (std::is_same::value) { - query_length = pkt->size - sizeof(mysql_hdr); - query = (char*)l_alloc(query_length); - memcpy(query, (char*)pkt->ptr + sizeof(mysql_hdr) + 1, query_length - 1); - } - else if constexpr (std::is_same::value) { - assert(sess->client_myds); - - pgsql_hdr hdr{}; - if (sess->client_myds->myprot.get_header((unsigned char*)pkt->ptr, pkt->size, &hdr) == false) { - //error - proxy_warning("Malformed packet\n"); - SPA->send_error_msg_to_client(sess, "Malformed packet"); - return; - } - - switch (hdr.type) { - case PG_PKT_STARTUP_V2: - case PG_PKT_STARTUP: - case PG_PKT_CANCEL: - case PG_PKT_SSLREQ: - case PG_PKT_GSSENCREQ: - //error - return; - } - - query_length = hdr.data.size; - query = (char*)l_alloc(query_length); - memcpy(query, (char*)hdr.data.ptr, query_length - 1); - } - - query[query_length-1]=0; - - char *query_no_space=(char *)l_alloc(query_length); - memcpy(query_no_space,query,query_length); - - unsigned int query_no_space_length=remove_spaces(query_no_space); - //fprintf(stderr,"%s----\n",query_no_space); - - if (query_no_space_length) { - // fix bug #925 - while (query_no_space[query_no_space_length-1]==';' || query_no_space[query_no_space_length-1]==' ') { - query_no_space_length--; - query_no_space[query_no_space_length]=0; - } - } - - // add global mutex, see bug #1188 - pthread_mutex_lock(&pa->sql_query_global_mutex); - - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - if (!strncasecmp("LOGENTRY ", query_no_space, strlen("LOGENTRY "))) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command LOGENTRY: %s\n", query_no_space + strlen("LOGENTRY ")); - proxy_info("Received command LOGENTRY: %s\n", query_no_space + strlen("LOGENTRY ")); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - run_query=false; - goto __run_query; - } - } - - // handle special queries from Cluster - // for bug #1188 , ProxySQL Admin needs to know the exact query - - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - string tn = ""; - if (!strncasecmp(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS, query_no_space, strlen(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS))) { - tn = "cluster_mysql_servers"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS))) { - tn = "mysql_replication_hostgroups"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS))) { - tn = "mysql_group_replication_hostgroups"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_GALERA, query_no_space, strlen(CLUSTER_QUERY_MYSQL_GALERA))) { - tn = "mysql_galera_hostgroups"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_AWS_AURORA, query_no_space, strlen(CLUSTER_QUERY_MYSQL_AWS_AURORA))) { - tn = "mysql_aws_aurora_hostgroups"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_HOSTGROUP_ATTRIBUTES, query_no_space, strlen(CLUSTER_QUERY_MYSQL_HOSTGROUP_ATTRIBUTES))) { - tn = "mysql_hostgroup_attributes"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_SERVERS_SSL_PARAMS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_SERVERS_SSL_PARAMS))) { - tn = "mysql_servers_ssl_params"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_SERVERS_V2, query_no_space, strlen(CLUSTER_QUERY_MYSQL_SERVERS_V2))) { - tn = "mysql_servers_v2"; - } - if (tn != "") { - GloAdmin->mysql_servers_wrlock(); - resultset = MyHGM->get_current_mysql_table(tn); - GloAdmin->mysql_servers_wrunlock(); - - if (resultset == nullptr) { - // 'mysql_servers_v2' is a virtual table that represents the latest 'main.mysql_servers' - // records promoted by the user. This section shouldn't be reached, since the initial resulset - // for this table ('MySQL_HostGroups_Manager::incoming_mysql_servers') is generated during - // initialization, and it's only updated in subsequent user config promotions. In case we - // reach here, an empty resultset should be replied, as it would mean that no user - // config has ever been promoted to runtime, and thus, this virtual table should remain empty. - if (tn == "mysql_servers_v2") { - const string query_empty_resultset { - string { MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS } + " LIMIT 0" - }; - - char *error=NULL; - int cols=0; - int affected_rows=0; - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "%s\n", query); - GloAdmin->mysql_servers_wrlock(); - GloAdmin->admindb->execute_statement(query_empty_resultset.c_str(), &error, &cols, &affected_rows, &resultset); - GloAdmin->mysql_servers_wrunlock(); - } else { - resultset = MyHGM->dump_table_mysql(tn); - } - - if (resultset) { - sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); - delete resultset; - run_query=false; - goto __run_query; - } - } else { - sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); - run_query=false; - goto __run_query; - } - - } - } - - if (!strncasecmp(CLUSTER_QUERY_MYSQL_USERS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_USERS))) { - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { - pthread_mutex_lock(&users_mutex); - resultset = GloMyAuth->get_current_mysql_users(); - pthread_mutex_unlock(&users_mutex); - if (resultset != nullptr) { - sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); - run_query=false; - goto __run_query; - } - } - } - - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - if (!strncasecmp(CLUSTER_QUERY_MYSQL_QUERY_RULES, query_no_space, strlen(CLUSTER_QUERY_MYSQL_QUERY_RULES))) { - GloQPro->wrlock(); - resultset = GloQPro->get_current_query_rules_inner(); - if (resultset == NULL) { - GloQPro->wrunlock(); // unlock first - resultset = GloQPro->get_current_query_rules(); - if (resultset) { - sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); - delete resultset; - run_query=false; - goto __run_query; - } - } else { - sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); - //delete resultset; // DO NOT DELETE . This is the inner resultset of Query_Processor - GloQPro->wrunlock(); - run_query=false; - goto __run_query; - } - } - if (!strncasecmp(CLUSTER_QUERY_MYSQL_QUERY_RULES_FAST_ROUTING, query_no_space, strlen(CLUSTER_QUERY_MYSQL_QUERY_RULES_FAST_ROUTING))) { - GloQPro->wrlock(); - resultset = GloQPro->get_current_query_rules_fast_routing_inner(); - if (resultset == NULL) { - GloQPro->wrunlock(); // unlock first - resultset = GloQPro->get_current_query_rules_fast_routing(); - if (resultset) { - sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); - delete resultset; - run_query=false; - goto __run_query; - } - } else { - sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); - //delete resultset; // DO NOT DELETE . This is the inner resultset of Query_Processor - GloQPro->wrunlock(); - run_query=false; - goto __run_query; - } - } - } - - // if the client simply executes: - // SELECT COUNT(*) FROM runtime_mysql_query_rules_fast_routing - // we just return the count - if (strcmp("SELECT COUNT(*) FROM runtime_mysql_query_rules_fast_routing", query_no_space)==0) { - int cnt = GloQPro->get_current_query_rules_fast_routing_count(); - l_free(query_length,query); - char buf[256]; - sprintf(buf,"SELECT %d AS 'COUNT(*)'", cnt); - query=l_strdup(buf); - query_length=strlen(query)+1; - goto __run_query; - } - - if (!strncasecmp("TRUNCATE ", query_no_space, strlen("TRUNCATE "))) { - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - if (strstr(query_no_space,"stats_mysql_query_digest")) { - bool truncate_digest_table = false; - static char * truncate_digest_table_queries[] = { - (char *)"TRUNCATE TABLE stats.stats_mysql_query_digest", - (char *)"TRUNCATE TABLE stats.stats_mysql_query_digest_reset", - (char *)"TRUNCATE TABLE stats_mysql_query_digest", - (char *)"TRUNCATE TABLE stats_mysql_query_digest_reset", - (char *)"TRUNCATE stats.stats_mysql_query_digest", - (char *)"TRUNCATE stats.stats_mysql_query_digest_reset", - (char *)"TRUNCATE stats_mysql_query_digest", - (char *)"TRUNCATE stats_mysql_query_digest_reset" - }; - size_t l=sizeof(truncate_digest_table_queries)/sizeof(char *); - unsigned int i; - for (i=0;iadmindb->execute("DELETE FROM stats.stats_mysql_query_digest"); - SPA->admindb->execute("DELETE FROM stats.stats_mysql_query_digest_reset"); - SPA->vacuum_stats(true); - // purge the digest map, asynchronously, in single thread - char *msg = NULL; - int r1 = ProxySQL_Test___PurgeDigestTable(true, false, &msg); - SPA->send_ok_msg_to_client(sess, msg, r1, query_no_space); - free(msg); - run_query=false; - goto __run_query; - } - } - } - } -#ifdef DEBUG - /** - * @brief Handles the 'PROXYSQL_SIMULATOR' command. Performing the operation specified in the payload - * format. - * @details The 'PROXYSQL_SIMULATOR' command is specified the following format. Allowing to perform a - * certain internal state changing operation. Payload spec: - * ``` - * PROXYSQL_SIMULATOR ${operation} ${hg} ${address}:${port} ${operation_params} - * ``` - * - * Supported operations include: - * - mysql_error: Find the server specified by 'hostname:port' in the specified hostgroup and calls - * 'MySrvC::connect_error()' with the provider 'error_code'. - * - * Payload example: - * ``` - * PROXYSQL_SIMULATOR mysql_error 1 127.0.0.1 3306 1234 - * ``` - */ - if (!strncasecmp("PROXYSQL_SIMULATOR ", query_no_space, strlen("PROXYSQL_SIMULATOR "))) { - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - proxy_warning("Received PROXYSQL_SIMULATOR command: %s\n", query_no_space); - - re2::RE2::Options opts = re2::RE2::Options(RE2::Quiet); - re2::RE2 pattern("\\s*(\\w+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+):(\\d+) (\\d+)\\s*\\;*", opts); - re2::StringPiece input(query_no_space + strlen("PROXYSQL_SIMULATOR")); - - std::string command, s_hg, srv_addr, s_port, s_errcode {}; - bool c_res = re2::RE2::Consume(&input, pattern, &command, &s_hg, &srv_addr, &s_port, &s_errcode); - - long i_hg = 0; - long i_port = 0; - long i_errcode = 0; - - if (c_res == true) { - char* endptr = nullptr; - i_hg = std::strtol(s_hg.c_str(), &endptr, 10); - if (errno == ERANGE || errno == EINVAL) i_hg = LONG_MIN; - i_port = std::strtol(s_port.c_str(), &endptr, 10); - if (errno == ERANGE || errno == EINVAL) i_port = LONG_MIN; - i_errcode = std::strtol(s_errcode.c_str(), &endptr, 10); - if (errno == ERANGE || errno == EINVAL) i_errcode = LONG_MIN; - } - - if (c_res == true && i_hg != LONG_MIN && i_port != LONG_MIN && i_errcode != LONG_MIN) { - MyHGM->wrlock(); - - MySrvC* mysrvc = MyHGM->find_server_in_hg(i_hg, srv_addr, i_port); - if (mysrvc != nullptr) { - int backup_shun_on_failures; - if constexpr (std::is_same::value) { - backup_shun_on_failures = mysql_thread___shun_on_failures; - mysql_thread___shun_on_failures = 1; - // Set the error twice to surpass 'mysql_thread___shun_on_failures' value. - mysrvc->connect_error(i_errcode, false); - mysrvc->connect_error(i_errcode, false); - mysql_thread___shun_on_failures = backup_shun_on_failures; - } else if constexpr (std::is_same::value) { - backup_shun_on_failures = pgsql_thread___shun_on_failures; - pgsql_thread___shun_on_failures = 1; - // Set the error twice to surpass 'pgsql_thread___shun_on_failures' value. - mysrvc->connect_error(i_errcode, false); - mysrvc->connect_error(i_errcode, false); - pgsql_thread___shun_on_failures = backup_shun_on_failures; - } else { - assert(0); - } - - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - } else { - std::string t_err_msg { "Supplied server '%s:%d' wasn't found in hg '%d'" }; - std::string err_msg {}; - string_format(t_err_msg, err_msg, srv_addr.c_str(), i_port, i_hg); - - proxy_info("%s\n", err_msg.c_str()); - SPA->send_error_msg_to_client(sess, const_cast(err_msg.c_str())); - } - - MyHGM->wrunlock(); - } else { - SPA->send_error_msg_to_client(sess, (char*)"Invalid arguments supplied with query 'PROXYSQL_SIMULATOR'"); - } - - run_query=false; - goto __run_query; - } - } -#endif // DEBUG - if (!strncasecmp("PROXYSQLTEST ", query_no_space, strlen("PROXYSQLTEST "))) { - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->ProxySQL_Test_Handler(SPA, sess, query_no_space, run_query); - goto __run_query; - } - } - { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - needs_vacuum = SPA->GenericRefreshStatistics(query_no_space,query_no_space_length, ( sess->session_type == PROXYSQL_SESSION_ADMIN ? true : false ) ); - } - - - if (!strncasecmp("SHOW GLOBAL VARIABLES LIKE 'read_only'", query_no_space, strlen("SHOW GLOBAL VARIABLES LIKE 'read_only'"))) { - l_free(query_length,query); - char *q=(char *)"SELECT 'read_only' Variable_name, '%s' Value FROM global_variables WHERE Variable_name='admin-read_only'"; - query_length=strlen(q)+5; - query=(char *)l_alloc(query_length); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - bool ro=SPA->get_read_only(); - //sprintf(query,q,( ro ? "ON" : "OFF")); - PtrSize_t pkt_2; - if (ro) { - pkt_2.size=110; - pkt_2.ptr=l_alloc(pkt_2.size); - memcpy(pkt_2.ptr,READ_ONLY_ON,pkt_2.size); - } else { - pkt_2.size=111; - pkt_2.ptr=l_alloc(pkt_2.size); - memcpy(pkt_2.ptr,READ_ONLY_OFF,pkt_2.size); - } - sess->status=WAITING_CLIENT_DATA; - sess->client_myds->DSS=STATE_SLEEP; - sess->client_myds->PSarrayOUT->add(pkt_2.ptr,pkt_2.size); - run_query=false; - goto __run_query; - } - - if (!strncasecmp("SELECT @@global.read_only", query_no_space, strlen("SELECT @@global.read_only"))) { - l_free(query_length,query); - char *q=(char *)"SELECT 'read_only' Variable_name, '%s' Value FROM global_variables WHERE Variable_name='admin-read_only'"; - query_length=strlen(q)+5; - query=(char *)l_alloc(query_length); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - bool ro=SPA->get_read_only(); - //sprintf(query,q,( ro ? "ON" : "OFF")); - PtrSize_t pkt_2; - if (ro) { - pkt_2.size=73; - pkt_2.ptr=l_alloc(pkt_2.size); - memcpy(pkt_2.ptr,READ_ONLY_1,pkt_2.size); - } else { - pkt_2.size=73; - pkt_2.ptr=l_alloc(pkt_2.size); - memcpy(pkt_2.ptr,READ_ONLY_0,pkt_2.size); - } - sess->status=WAITING_CLIENT_DATA; - sess->client_myds->DSS=STATE_SLEEP; - sess->client_myds->PSarrayOUT->add(pkt_2.ptr,pkt_2.size); - run_query=false; - goto __run_query; - } - - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - if ((query_no_space_length>13) && (!strncasecmp("PULL VERSION ", query_no_space, 13))) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received PULL command\n"); - if ((query_no_space_length>27) && (!strncasecmp("PULL VERSION MYSQL SERVERS ", query_no_space, 27))) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received PULL VERSION MYSQL SERVERS command\n"); - unsigned int wait_mysql_servers_version = 0; - unsigned int wait_timeout = 0; - int rc = sscanf(query_no_space+27,"%u %u",&wait_mysql_servers_version, &wait_timeout); - if (rc < 2) { - SPA->send_error_msg_to_client(sess, (char *)"Invalid argument"); - run_query=false; - goto __run_query; - } else { - MyHGM->wait_servers_table_version(wait_mysql_servers_version, wait_timeout); - l_free(query_length,query); - unsigned int curver = MyHGM->get_servers_table_version(); - char buf[256]; - sprintf(buf,"SELECT %u AS 'version'", curver); - query=l_strdup(buf); - query_length=strlen(query)+1; - //SPA->send_ok_msg_to_client(sess, , NULL); - //run_query=false; - goto __run_query; - } - } - } - - - if ((query_no_space_length == strlen("SELECT GLOBAL_CHECKSUM()")) && (!strncasecmp("SELECT GLOBAL_CHECKSUM()", query_no_space, strlen("SELECT GLOBAL_CHECKSUM()")))) { - char buf[32]; - pthread_mutex_lock(&GloVars.checksum_mutex); - sprintf(buf,"%lu",GloVars.checksums_values.global_checksum); - pthread_mutex_unlock(&GloVars.checksum_mutex); - uint16_t setStatus = 0; - auto *myds=sess->client_myds; - auto *myprot=&sess->client_myds->myprot; - myds->DSS=STATE_QUERY_SENT_DS; - int sid=1; - myprot->generate_pkt_column_count(true,NULL,NULL,sid,1); sid++; - myprot->generate_pkt_field(true,NULL,NULL,sid,(char *)"",(char *)"",(char *)"",(char *)"CHECKSUM",(char *)"",63,31,MYSQL_TYPE_LONGLONG,161,0,false,0,NULL); sid++; - myds->DSS=STATE_COLUMN_DEFINITION; - myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; - char **p=(char **)malloc(sizeof(char*)*1); - unsigned long *l=(unsigned long *)malloc(sizeof(unsigned long *)*1); - l[0]=strlen(buf);; - p[0]=buf; - myprot->generate_pkt_row(true,NULL,NULL,sid,1,l,p); sid++; - myds->DSS=STATE_ROW; - myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; - myds->DSS=STATE_SLEEP; - run_query=false; - free(l); - free(p); - goto __run_query; - } - - - if ((query_no_space_length>8) && (!strncasecmp("PROXYSQL ", query_no_space, 8))) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received PROXYSQL command\n"); - //pthread_mutex_lock(&admin_mutex); - run_query=admin_handler_command_proxysql(query_no_space, query_no_space_length, sess, pa); - //pthread_mutex_unlock(&admin_mutex); - goto __run_query; - } - if ((query_no_space_length>5) && ( (!strncasecmp("SAVE ", query_no_space, 5)) || (!strncasecmp("LOAD ", query_no_space, 5))) ) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received LOAD or SAVE command\n"); - run_query=admin_handler_command_load_or_save(query_no_space, query_no_space_length, sess, pa, &query, &query_length); - goto __run_query; - } - if ((query_no_space_length>16) && ( (!strncasecmp("KILL CONNECTION ", query_no_space, 16)) || (!strncasecmp("KILL CONNECTION ", query_no_space, 16))) ) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received KILL CONNECTION command\n"); - run_query=admin_handler_command_kill_connection(query_no_space, query_no_space_length, sess, pa); - goto __run_query; - } - - - // queries generated by mysqldump - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - if ( - !strncmp("/*!40014 SET ", query_no_space, 13) || - !strncmp("/*!40101 SET ", query_no_space, 13) || - !strncmp("/*!40103 SET ", query_no_space, 13) || - !strncmp("/*!40111 SET ", query_no_space, 13) || - !strncmp("/*!80000 SET ", query_no_space, 13) || - !strncmp("/*!50503 SET ", query_no_space, 13) || - !strncmp("/*!50717 SET ", query_no_space, 13) || - !strncmp("/*!50717 SELECT ", query_no_space, strlen("/*!50717 SELECT ")) || - !strncmp("/*!50717 PREPARE ", query_no_space, strlen("/*!50717 PREPARE ")) || - !strncmp("/*!50717 EXECUTE ", query_no_space, strlen("/*!50717 EXECUTE ")) || - !strncmp("/*!50717 DEALLOCATE ", query_no_space, strlen("/*!50717 DEALLOCATE ")) || - !strncmp("/*!50112 SET ", query_no_space, strlen("/*!50112 SET ")) || - !strncmp("/*!50112 PREPARE ", query_no_space, strlen("/*!50112 PREPARE ")) || - !strncmp("/*!50112 EXECUTE ", query_no_space, strlen("/*!50112 EXECUTE ")) || - !strncmp("/*!50112 DEALLOCATE ", query_no_space, strlen("/*!50112 DEALLOCATE ")) || - !strncmp("/*!40000 ALTER TABLE", query_no_space, strlen("/*!40000 ALTER TABLE")) - || - !strncmp("/*!40100 SET @@SQL_MODE='' */", query_no_space, strlen("/*!40100 SET @@SQL_MODE='' */")) - || - !strncmp("/*!40103 SET TIME_ZONE=", query_no_space, strlen("/*!40103 SET TIME_ZONE=")) - || - !strncmp("LOCK TABLES", query_no_space, strlen("LOCK TABLES")) - || - !strncmp("UNLOCK TABLES", query_no_space, strlen("UNLOCK TABLES")) - || - !strncmp("SET SQL_QUOTE_SHOW_CREATE=1", query_no_space, strlen("SET SQL_QUOTE_SHOW_CREATE=1")) - || - !strncmp("SET SESSION character_set_results", query_no_space, strlen("SET SESSION character_set_results")) - || - !strncasecmp("USE ", query_no_space, strlen("USE ")) // this applies to all clients, not only mysqldump - ) { - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - run_query=false; - goto __run_query; - } - - if (!strncmp("SHOW STATUS LIKE 'binlog_snapshot_gtid_executed'", query_no_space, strlen("SHOW STATUS LIKE 'binlog_snapshot_gtid_executed'"))) { - l_free(query_length, query); - query = l_strdup("SELECT variable_name AS Variable_name, Variable_value AS Value FROM global_variables WHERE 1=0"); - query_length = strlen(query)+1; - goto __run_query; - } - if (!strncmp("SELECT COLUMN_NAME, JSON_EXTRACT(HISTOGRAM, '$.\"number-of-buckets-specified\"') FROM information_schema.COLUMN_STATISTICS", query_no_space, strlen("SELECT COLUMN_NAME, JSON_EXTRACT(HISTOGRAM, '$.\"number-of-buckets-specified\"') FROM information_schema.COLUMN_STATISTICS"))) { - l_free(query_length, query); - query = l_strdup("SELECT variable_name AS COLUMN_NAME, Variable_value AS 'JSON_EXTRACT(HISTOGRAM, ''$.\"number-of-buckets-specified\"'')' FROM global_variables WHERE 1=0"); - query_length = strlen(query)+1; - goto __run_query; - } - if (!strncmp("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'performance_schema' AND table_name = 'session_variables'", query_no_space, strlen("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'performance_schema' AND table_name = 'session_variables'"))) { - l_free(query_length,query); - query=l_strdup("SELECT 0 as 'COUNT(*)'"); - query_length=strlen(query)+1; - goto __run_query; - } - if (!strncmp("SHOW VARIABLES LIKE 'gtid\\_mode'", query_no_space, strlen("SHOW VARIABLES LIKE 'gtid\\_mode'"))) { - l_free(query_length,query); - query=l_strdup("SELECT variable_name Variable_name, Variable_value Value FROM global_variables WHERE Variable_name='gtid_mode'"); - query_length=strlen(query)+1; - goto __run_query; - } - if (!strncmp("select @@collation_database", query_no_space, strlen("select @@collation_database"))) { - l_free(query_length,query); - query=l_strdup("SELECT Collation '@@collation_database' FROM mysql_collations WHERE Collation='utf8_general_ci' LIMIT 1"); - query_length=strlen(query)+1; - goto __run_query; - } - if (!strncmp("SHOW VARIABLES LIKE 'ndbinfo\\_version'", query_no_space, strlen("SHOW VARIABLES LIKE 'ndbinfo\\_version'"))) { - l_free(query_length,query); - query=l_strdup("SELECT variable_name Variable_name, Variable_value Value FROM global_variables WHERE Variable_name='ndbinfo_version'"); - query_length=strlen(query)+1; - goto __run_query; - } - if (!strncasecmp("show table status like '", query_no_space, strlen("show table status like '"))) { - char *strA=query_no_space+24; - int strAl=strlen(strA); - if (strAl<2) { // error - goto __run_query; - } - char *err=NULL; - SQLite3_result *resultset=SPA->generate_show_table_status(strA, &err); - sess->SQLite3_to_MySQL(resultset, err, 0, &sess->client_myds->myprot); - if (resultset) delete resultset; - if (err) free(err); - run_query=false; - goto __run_query; - } - if (!strncasecmp("show fields from ", query_no_space, strlen("show fields from "))) { - char *strA=query_no_space+17; - int strAl=strlen(strA); - if (strAl==0) { // error - goto __run_query; - } - if (strA[0]=='`') { - strA++; - strAl--; - } - if (strAl<2) { // error - goto __run_query; - } - char *err=NULL; - SQLite3_result *resultset=SPA->generate_show_fields_from(strA, &err); - sess->SQLite3_to_MySQL(resultset, err, 0, &sess->client_myds->myprot); - if (resultset) delete resultset; - if (err) free(err); - run_query=false; - goto __run_query; - } - } - - // FIXME: this should be removed, it is just a POC for issue #253 . What is important is the call to GloMTH->signal_all_threads(); - if (!strncasecmp("SIGNAL MYSQL THREADS", query_no_space, strlen("SIGNAL MYSQL THREADS"))) { - GloMTH->signal_all_threads(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received %s command\n", query_no_space); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SPA->save_admin_variables_from_runtime(); - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Sent signal to all mysql threads\n"); - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - run_query=false; - goto __run_query; - } - - // fix bug #442 - if (!strncmp("SET SQL_SAFE_UPDATES=1", query_no_space, strlen("SET SQL_SAFE_UPDATES=1"))) { - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - run_query=false; - goto __run_query; - } - - // fix bug #1047 - if ( - (!strncasecmp("BEGIN", query_no_space, strlen("BEGIN"))) - || - (!strncasecmp("START TRANSACTION", query_no_space, strlen("START TRANSACTION"))) - || - (!strncasecmp("COMMIT", query_no_space, strlen("COMMIT"))) - || - (!strncasecmp("ROLLBACK", query_no_space, strlen("ROLLBACK"))) - || - (!strncasecmp("SET character_set_results", query_no_space, strlen("SET character_set_results"))) - || - (!strncasecmp("SET SQL_AUTO_IS_NULL", query_no_space, strlen("SET SQL_AUTO_IS_NULL"))) - || - (!strncasecmp("SET NAMES", query_no_space, strlen("SET NAMES"))) - || - (!strncasecmp("SET AUTOCOMMIT", query_no_space, strlen("SET AUTOCOMMIT"))) - ) { - SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); - run_query=false; - goto __run_query; - } - - // MySQL client check command for dollars quote support, starting at version '8.1.0'. See #4300. - if (!strncasecmp("SELECT $$", query_no_space, strlen("SELECT $$"))) { - pair err_info { get_dollar_quote_error(mysql_thread___server_version) }; - SPA->send_error_msg_to_client(sess, const_cast(err_info.second), err_info.first); - run_query=false; - goto __run_query; - } - - if (query_no_space_length==SELECT_VERSION_COMMENT_LEN) { - if (!strncasecmp(SELECT_VERSION_COMMENT, query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT '(ProxySQL Admin Module)'"); - query_length=strlen(query)+1; - goto __run_query; - } - } - - if (!strncasecmp("select concat(@@version, ' ', @@version_comment)", query_no_space, strlen("select concat(@@version, ' ', @@version_comment)"))) { - l_free(query_length,query); - char *q = const_cast("SELECT '%s Admin Module'"); - query_length = strlen(q) + strlen(PROXYSQL_VERSION) + 1; - query = static_cast(l_alloc(query_length)); - sprintf(query, q, PROXYSQL_VERSION); - goto __run_query; - } - - // add support for SELECT current_user() and SELECT user() - // see https://github.com/sysown/proxysql/issues/1105#issuecomment-990940585 - if ( - (strcasecmp("SELECT current_user()", query_no_space) == 0) - || - (strcasecmp("SELECT user()", query_no_space) == 0) - ) { - bool current = false; - if (strcasestr(query_no_space, "current") != NULL) - current = true; - l_free(query_length,query); - std::string s = "SELECT '"; - s += sess->client_myds->myconn->userinfo->username ; - if (strlen(sess->client_myds->addr.addr) > 0) { - s += "@"; - s += sess->client_myds->addr.addr; - } - s += "' AS '"; - if (current == true) { - s+= "current_"; - } - s += "user()'"; - query=l_strdup(s.c_str()); - query_length=strlen(query)+1; - goto __run_query; - } - - if (!strncasecmp("select @@sql_mode", query_no_space, strlen("select @@sql_mode"))) { - l_free(query_length,query); - char *q = const_cast("SELECT \"\" as \"@@sql_mode\""); - query_length = strlen(q) + strlen(PROXYSQL_VERSION) + 1; - query = static_cast(l_alloc(query_length)); - sprintf(query, q, PROXYSQL_VERSION); - goto __run_query; - } - - // trivial implementation for 'connection_id()' to support 'mycli'. See #3247 - if (!strncasecmp("select connection_id()", query_no_space, strlen("select connection_id()"))) { - l_free(query_length,query); - // 'connection_id()' is always forced to be '0' - query=l_strdup("SELECT 0 AS 'CONNECTION_ID()'"); - query_length=strlen(query)+1; - goto __run_query; - } - - // implementation for 'SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())' in order to support'csharp' connector. See #2543 - if (!strncasecmp("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())", query_no_space, strlen("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())"))) { - l_free(query_length,query); - char *query1=(char*)"SELECT '%s' as 'TIMEDIFF(NOW(), UTC_TIMESTAMP()'"; - - // compute the timezone diff - std::string timezone_offset_str = timediff_timezone_offset(); - char *query2=(char *)malloc(strlen(query1) + strlen(timezone_offset_str.c_str()) + 1); - - // format the query - sprintf(query2, query1, timezone_offset_str.c_str()); - - // copy the resulting query - query=l_strdup(query2); - query_length=strlen(query2) + 1; - - // free the buffer used to format - free(query2); - goto __run_query; - } - - // implementation for '"select @@max_allowed_packet, @@character_set_client, @@character_set_connection, @@license, @@sql_mode, @@lower_case_table_names"' - // in order to support 'csharp' connector. See #2543 - if ( - !strncasecmp( - "select @@max_allowed_packet, @@character_set_client, @@character_set_connection, @@license, @@sql_mode, @@lower_case_table_names", - query_no_space, - strlen("select @@max_allowed_packet, @@character_set_client, @@character_set_connection, @@license, @@sql_mode, @@lower_case_table_names") - ) - ) { - l_free(query_length,query); - char *query1= - const_cast( - "select '67108864' as '@@max_allowed_packet', 'utf8' as '@@character_set_client', 'utf8' as '@@character_set_connection', '' as '@@license', '' as '@@sql_mode', '' as '@@lower_case_table_names'" - ); - query=l_strdup(query1); - query_length=strlen(query1)+1; - goto __run_query; - } - - if (query_no_space_length==SELECT_DB_USER_LEN) { - if (!strncasecmp(SELECT_DB_USER, query_no_space, query_no_space_length)) { - l_free(query_length,query); - char *query1=(char *)"SELECT \"admin\" AS 'DATABASE()', \"%s\" AS 'USER()'"; - char *query2=(char *)malloc(strlen(query1)+strlen(sess->client_myds->myconn->userinfo->username)+10); - sprintf(query2,query1,sess->client_myds->myconn->userinfo->username); - query=l_strdup(query2); - query_length=strlen(query2)+1; - free(query2); - goto __run_query; - } - } - - if (query_no_space_length==SELECT_CHARSET_VARIOUS_LEN) { - if (!strncasecmp(SELECT_CHARSET_VARIOUS, query_no_space, query_no_space_length)) { - l_free(query_length,query); - char *query1=(char *)"select 'utf8' as '@@character_set_client', 'utf8' as '@@character_set_connection', 'utf8' as '@@character_set_server', 'utf8' as '@@character_set_database' limit 1"; - query=l_strdup(query1); - query_length=strlen(query1)+1; - goto __run_query; - } - } - - if (!strncasecmp("SELECT @@version", query_no_space, strlen("SELECT @@version"))) { - l_free(query_length,query); - char *q=(char *)"SELECT '%s' AS '@@version'"; - if (GloMyLdapAuth == nullptr) { - query_length=strlen(q)+20+strlen(PROXYSQL_VERSION); - } else { - query_length=strlen(q)+20+strlen(PROXYSQL_VERSION)+strlen("-Enterprise"); - } - query=(char *)l_alloc(query_length); - if (GloMyLdapAuth == nullptr) { - sprintf(query, q, PROXYSQL_VERSION); - } else { - sprintf(query, q, PROXYSQL_VERSION"-Enterprise"); - } - goto __run_query; - } - - if (!strncasecmp("SELECT version()", query_no_space, strlen("SELECT version()"))) { - l_free(query_length,query); - char *q=(char *)"SELECT '%s' AS 'version()'"; - if (GloMyLdapAuth == nullptr) { - query_length=strlen(q)+20+strlen(PROXYSQL_VERSION); - } else { - query_length=strlen(q)+20+strlen(PROXYSQL_VERSION)+strlen("-Enterprise"); - } - query=(char *)l_alloc(query_length); - if (GloMyLdapAuth == nullptr) { - sprintf(query, q, PROXYSQL_VERSION); - } else { - sprintf(query, q, PROXYSQL_VERSION"-Enterprise"); - } - goto __run_query; - } - - if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in", query_no_space, strlen("SHOW VARIABLES WHERE Variable_name in"))) { - // Allow MariaDB ConnectorJ to connect to Admin #743 - if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','sql_mode')", query_no_space, strlen("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','sql_mode')"))) { - l_free(query_length,query); - char *q=(char *)"SELECT 'max_allowed_packet' Variable_name,'4194304' Value UNION ALL SELECT 'sql_mode', 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION' UNION ALL SELECT 'system_time_zone', 'UTC' UNION ALL SELECT 'time_zone','SYSTEM'"; - query_length=strlen(q)+20; - query=(char *)l_alloc(query_length); - sprintf(query,q,PROXYSQL_VERSION); - goto __run_query; - } - // Allow MariaDB ConnectorJ 2.4.1 to connect to Admin #2009 - if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','auto_increment_increment')", query_no_space, strlen("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','auto_increment_increment')"))) { - l_free(query_length,query); - char *q=(char *)"SELECT 'max_allowed_packet' Variable_name,'4194304' Value UNION ALL SELECT 'auto_increment_increment', '1' UNION ALL SELECT 'system_time_zone', 'UTC' UNION ALL SELECT 'time_zone','SYSTEM'"; - query_length=strlen(q)+20; - query=(char *)l_alloc(query_length); - sprintf(query,q,PROXYSQL_VERSION); - goto __run_query; - } - } - - { - bool rc; - rc=RE2::PartialMatch(query_no_space,*(RE2 *)(pa->match_regexes.re[0])); - if (rc) { - string *new_query=new std::string(query_no_space); - RE2::Replace(new_query,(char *)"^(\\w+)\\s+@@(\\w+)\\s*",(char *)"SELECT variable_value AS '@@max_allowed_packet' FROM global_variables WHERE variable_name='mysql-max_allowed_packet'"); - free(query); - query_length=new_query->length()+1; - query=(char *)malloc(query_length); - memcpy(query,new_query->c_str(),query_length-1); - query[query_length-1]='\0'; - delete new_query; - goto __run_query; - } - } - { - bool rc; - rc=RE2::PartialMatch(query_no_space,*(RE2 *)(pa->match_regexes.re[1])); - if (rc) { - string *new_query=new std::string(query_no_space); - RE2::Replace(new_query,(char *)"^(\\w+) *@@([0-9A-Za-z_-]+) *",(char *)"SELECT variable_value AS '@@\\2' FROM global_variables WHERE variable_name='\\2' COLLATE NOCASE UNION ALL SELECT variable_value AS '@@\\2' FROM stats.stats_mysql_global WHERE variable_name='\\2' COLLATE NOCASE"); - free(query); - query_length=new_query->length()+1; - query=(char *)malloc(query_length); - memcpy(query,new_query->c_str(),query_length-1); - query[query_length-1]='\0'; - GloAdmin->stats___mysql_global(); - delete new_query; - goto __run_query; - } - } - { - bool rc; - rc=RE2::PartialMatch(query_no_space,*(RE2 *)(pa->match_regexes.re[2])); - if (rc) { - string *new_query=new std::string(query_no_space); - RE2::Replace(new_query,(char *)"([Ss][Hh][Oo][Ww]\\s+[Vv][Aa][Rr][Ii][Aa][Bb][Ll][Ee][Ss]\\s+[Ww][Hh][Ee][Rr][Ee])",(char *)"SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE"); - free(query); - query_length=new_query->length()+1; - query=(char *)malloc(query_length); - memcpy(query,new_query->c_str(),query_length-1); - query[query_length-1]='\0'; - delete new_query; - goto __run_query; - } - } - { - bool rc; - rc=RE2::PartialMatch(query_no_space,*(RE2 *)(pa->match_regexes.re[3])); - if (rc) { - string *new_query=new std::string(query_no_space); - RE2::Replace(new_query,(char *)"([Ss][Hh][Oo][Ww]\\s+[Vv][Aa][Rr][Ii][Aa][Bb][Ll][Ee][Ss]\\s+[Ll][Ii][Kk][Ee])",(char *)"SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE"); - free(query); - query_length=new_query->length()+1; - query=(char *)malloc(query_length); - memcpy(query,new_query->c_str(),query_length-1); - query[query_length-1]='\0'; - delete new_query; - goto __run_query; - } - } - - if (!strncasecmp("SET ", query_no_space, 4)) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received SET\n"); - run_query = admin_handler_command_set(query_no_space, query_no_space_length, sess, pa, &query, &query_length); - goto __run_query; - } - - if(!strncasecmp("CHECKSUM ", query_no_space, 9)){ - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received CHECKSUM command\n"); - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - SQLite3_result *resultset=NULL; - char *tablename=NULL; - char *error=NULL; - int affected_rows=0; - int cols=0; - if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL SERVERS") && !strncasecmp("CHECKSUM DISK MYSQL SERVERS", query_no_space, strlen(query_no_space))){ - char *q=(char *)"SELECT * FROM mysql_servers ORDER BY hostgroup_id, hostname, port"; - tablename=(char *)"MYSQL SERVERS"; - SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL USERS") && !strncasecmp("CHECKSUM DISK MYSQL USERS", query_no_space, strlen(query_no_space))){ - char *q=(char *)"SELECT * FROM mysql_users ORDER BY username"; - tablename=(char *)"MYSQL USERS"; - SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL QUERY RULES") && !strncasecmp("CHECKSUM DISK MYSQL QUERY RULES", query_no_space, strlen(query_no_space))){ - char *q=(char *)"SELECT * FROM mysql_query_rules ORDER BY rule_id"; - tablename=(char *)"MYSQL QUERY RULES"; - SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL VARIABLES") && !strncasecmp("CHECKSUM DISK MYSQL VARIABLES", query_no_space, strlen(query_no_space))){ - char *q=(char *)"SELECT * FROM global_variables WHERE variable_name LIKE 'mysql-%' ORDER BY variable_name"; - tablename=(char *)"MYSQL VARIABLES"; - SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM DISK MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))){ - char *q=(char *)"SELECT * FROM mysql_replication_hostgroups ORDER BY writer_hostgroup"; - tablename=(char *)"MYSQL REPLICATION HOSTGROUPS"; - SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL SERVERS") && !strncasecmp("CHECKSUM MEMORY MYSQL SERVERS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL SERVERS") && !strncasecmp("CHECKSUM MEM MYSQL SERVERS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL SERVERS") && !strncasecmp("CHECKSUM MYSQL SERVERS", query_no_space, strlen(query_no_space)))){ - char *q=(char *)"SELECT * FROM mysql_servers ORDER BY hostgroup_id, hostname, port"; - tablename=(char *)"MYSQL SERVERS"; - SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL USERS") && !strncasecmp("CHECKSUM MEMORY MYSQL USERS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL USERS") && !strncasecmp("CHECKSUM MEM MYSQL USERS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL USERS") && !strncasecmp("CHECKSUM MYSQL USERS", query_no_space, strlen(query_no_space)))){ - char *q=(char *)"SELECT * FROM mysql_users ORDER BY username"; - tablename=(char *)"MYSQL USERS"; - SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL QUERY RULES") && !strncasecmp("CHECKSUM MEMORY MYSQL QUERY RULES", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL QUERY RULES") && !strncasecmp("CHECKSUM MEM MYSQL QUERY RULES", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL QUERY RULES") && !strncasecmp("CHECKSUM MYSQL QUERY RULES", query_no_space, strlen(query_no_space)))){ - char *q=(char *)"SELECT * FROM mysql_query_rules ORDER BY rule_id"; - tablename=(char *)"MYSQL QUERY RULES"; - SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL VARIABLES") && !strncasecmp("CHECKSUM MEMORY MYSQL VARIABLES", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL VARIABLES") && !strncasecmp("CHECKSUM MEM MYSQL VARIABLES", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL VARIABLES") && !strncasecmp("CHECKSUM MYSQL VARIABLES", query_no_space, strlen(query_no_space)))){ - char *q=(char *)"SELECT * FROM global_variables WHERE variable_name LIKE 'mysql-%' ORDER BY variable_name"; - tablename=(char *)"MYSQL VARIABLES"; - SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space)))){ - char *q=(char *)"SELECT * FROM mysql_replication_hostgroups ORDER BY writer_hostgroup"; - tablename=(char *)"MYSQL REPLICATION HOSTGROUPS"; - SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL GROUP REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL GROUP REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL GROUP REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space)))){ - char *q=(char *)"SELECT * FROM mysql_group_replication_hostgroups ORDER BY writer_hostgroup"; - tablename=(char *)"MYSQL GROUP REPLICATION HOSTGROUPS"; - SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL GALERA HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL GALERA HOSTGROUPS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL GALERA HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL GALERA HOSTGROUPS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL GALERA HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL GALERA HOSTGROUPS", query_no_space, strlen(query_no_space)))){ - char *q=(char *)"SELECT * FROM mysql_galera_hostgroups ORDER BY writer_hostgroup"; - tablename=(char *)"MYSQL GALERA HOSTGROUPS"; - SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL AURORA HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL AURORA HOSTGROUPS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL AURORA HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL AURORA HOSTGROUPS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL AURORA HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL AURORA HOSTGROUPS", query_no_space, strlen(query_no_space)))){ - char *q=(char *)"SELECT * FROM mysql_aws_aurora_hostgroups ORDER BY writer_hostgroup"; - tablename=(char *)"MYSQL AURORA HOSTGROUPS"; - SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL HOSTGROUP ATTRIBUTES") && !strncasecmp("CHECKSUM MEMORY MYSQL HOSTGROUP ATTRIBUTES", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL HOSTGROUP ATTRIBUTES") && !strncasecmp("CHECKSUM MEM MYSQL HOSTGROUP ATTRIBUTES", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL HOSTGROUP ATTRIBUTES") && !strncasecmp("CHECKSUM MYSQL HOSTGROUP ATTRIBUTES", query_no_space, strlen(query_no_space)))){ - char *q=(char *)"SELECT * FROM mysql_hostgroup_attributes ORDER BY hostgroup_id"; - tablename=(char *)"MYSQL HOSTGROUP ATTRIBUTES"; - SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL SERVERS SSL PARAMS") && !strncasecmp("CHECKSUM MEMORY MYSQL SERVERS SSL PARAMS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL SERVERS SSL PARAMS") && !strncasecmp("CHECKSUM MEM MYSQL SERVERS SSL PARAMS", query_no_space, strlen(query_no_space))) - || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL SERVERS SSL PARAMS") && !strncasecmp("CHECKSUM MYSQL SERVERS SSL PARAMS", query_no_space, strlen(query_no_space)))){ - char *q=(char *)"SELECT * FROM mysql_servers_ssl_params ORDER BY hostname, port, username"; - tablename=(char *)"MYSQL HOSTGROUP ATTRIBUTES"; - SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); - } - - if (error) { - proxy_error("Error: %s\n", error); - char buf[1024]; - sprintf(buf,"%s", error); - SPA->send_error_msg_to_client(sess, buf); - run_query=false; - } else if (resultset) { - l_free(query_length,query); - char *q=(char *)"SELECT '%s' AS 'table', '%s' AS 'checksum'"; - char *checksum=(char *)resultset->checksum(); - query=(char *)malloc(strlen(q)+strlen(tablename)+strlen(checksum)+1); - sprintf(query,q,tablename,checksum); - query_length = strlen(query); - free(checksum); - delete resultset; - } - goto __run_query; - } - - if (!strncasecmp("SELECT CONFIG INTO OUTFILE", query_no_space, strlen("SELECT CONFIG INTO OUTFILE"))) { - std::string fileName = query_no_space + strlen("SELECT CONFIG INTO OUTFILE"); - fileName.erase(0, fileName.find_first_not_of("\t\n\v\f\r ")); - fileName.erase(fileName.find_last_not_of("\t\n\v\f\r ") + 1); - if (fileName.size() == 0) { - std::stringstream ss; - ss << "ProxySQL Admin Error: empty file name"; - sess->SQLite3_to_MySQL(resultset, (char*)ss.str().c_str(), affected_rows, &sess->client_myds->myprot); - } - std::string data; - data.reserve(100000); - data += config_header; - int rc = pa->proxysql_config().Write_Global_Variables_to_configfile(data); - rc = pa->proxysql_config().Write_MySQL_Users_to_configfile(data); - rc = pa->proxysql_config().Write_MySQL_Query_Rules_to_configfile(data); - rc = pa->proxysql_config().Write_MySQL_Servers_to_configfile(data); - rc = pa->proxysql_config().Write_PgSQL_Users_to_configfile(data); - rc = pa->proxysql_config().Write_PgSQL_Query_Rules_to_configfile(data); - rc = pa->proxysql_config().Write_PgSQL_Servers_to_configfile(data); - rc = pa->proxysql_config().Write_Scheduler_to_configfile(data); - rc = pa->proxysql_config().Write_Restapi_to_configfile(data); - rc = pa->proxysql_config().Write_ProxySQL_Servers_to_configfile(data); - if (rc) { - std::stringstream ss; - ss << "ProxySQL Admin Error: Cannot extract configuration"; - sess->SQLite3_to_MySQL(resultset, (char*)ss.str().c_str(), affected_rows, &sess->client_myds->myprot); - } else { - std::ofstream out; - out.open(fileName); - if (out.is_open()) { - out << data; - out.close(); - if (!out) { - std::stringstream ss; - ss << "ProxySQL Admin Error: Error writing file " << fileName; - sess->SQLite3_to_MySQL(resultset, (char*)ss.str().c_str(), affected_rows, &sess->client_myds->myprot); - } else { - std::stringstream ss; - ss << "File " << fileName << " is saved."; - SPA->send_ok_msg_to_client(sess, (char*)ss.str().c_str(), data.size(), query_no_space); - } - } else { - std::stringstream ss; - ss << "ProxySQL Admin Error: Cannot open file " << fileName; - sess->SQLite3_to_MySQL(resultset, (char*)ss.str().c_str(), affected_rows, &sess->client_myds->myprot); - } - } - run_query = false; - goto __run_query; - } - - if (query_no_space_length==strlen("SELECT CONFIG FILE") && !strncasecmp("SELECT CONFIG FILE", query_no_space, query_no_space_length)) { - std::string data; - data.reserve(100000); - data += config_header; - int rc = pa->proxysql_config().Write_Global_Variables_to_configfile(data); - rc = pa->proxysql_config().Write_MySQL_Users_to_configfile(data); - rc = pa->proxysql_config().Write_MySQL_Query_Rules_to_configfile(data); - rc = pa->proxysql_config().Write_MySQL_Servers_to_configfile(data); - rc = pa->proxysql_config().Write_PgSQL_Users_to_configfile(data); - rc = pa->proxysql_config().Write_PgSQL_Query_Rules_to_configfile(data); - rc = pa->proxysql_config().Write_PgSQL_Servers_to_configfile(data); - rc = pa->proxysql_config().Write_Scheduler_to_configfile(data); - rc = pa->proxysql_config().Write_Restapi_to_configfile(data); - rc = pa->proxysql_config().Write_ProxySQL_Servers_to_configfile(data); - if (rc) { - std::stringstream ss; - ss << "ProxySQL Admin Error: Cannot write proxysql.cnf"; - sess->SQLite3_to_MySQL(resultset, (char*)ss.str().c_str(), affected_rows, &sess->client_myds->myprot); - } else { - char *pta[1]; - pta[0]=NULL; - pta[0]=(char*)data.c_str(); - SQLite3_result* resultset = new SQLite3_result(1); - resultset->add_column_definition(SQLITE_TEXT,"Data"); - resultset->add_row(pta); - sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); - delete resultset; - } - run_query = false; - goto __run_query; - } - - if (strncasecmp("SHOW ", query_no_space, 5)) { - goto __end_show_commands; // in the next block there are only SHOW commands - } - - if (!strncasecmp("SHOW PROMETHEUS METRICS", query_no_space, strlen("SHOW PROMETHEUS METRICS"))) { - char* pta[1]; - pta[0] = NULL; - SQLite3_result* resultset = new SQLite3_result(1); - resultset->add_column_definition(SQLITE_TEXT,"Data"); - - if (__sync_fetch_and_add(&GloMTH->status_variables.threads_initialized, 0) == 1) { - auto result = pa->serial_exposer({}); - pta[0] = (char*)result.second.c_str(); - resultset->add_row(pta); - } else { - resultset->add_row(pta); - } - - sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); - delete resultset; - run_query = false; - - goto __run_query; - } - - if (!strncasecmp("SHOW GLOBAL VARIABLES LIKE 'version'", query_no_space, strlen("SHOW GLOBAL VARIABLES LIKE 'version'"))) { - l_free(query_length,query); - char *q=(char *)"SELECT 'version' Variable_name, '%s' Value FROM global_variables WHERE Variable_name='admin-version'"; - query_length=strlen(q)+20+strlen(PROXYSQL_VERSION); - query=(char *)l_alloc(query_length); - sprintf(query,q,PROXYSQL_VERSION); - goto __run_query; - } - - - if (query_no_space_length==strlen("SHOW TABLES") && !strncasecmp("SHOW TABLES",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT name AS tables FROM sqlite_master WHERE type='table' AND name NOT IN ('sqlite_sequence') ORDER BY name"); - query_length=strlen(query)+1; - goto __run_query; - } - - if (query_no_space_length==strlen("SHOW CHARSET") && !strncasecmp("SHOW CHARSET",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT Charset, Collation AS 'Default collation' FROM mysql_collations WHERE `Default`='Yes'"); - query_length=strlen(query)+1; - goto __run_query; - } - - if (query_no_space_length==strlen("SHOW COLLATION") && !strncasecmp("SHOW COLLATION",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT * FROM mysql_collations"); - query_length=strlen(query)+1; - goto __run_query; - } - - if ((query_no_space_length>15) && (!strncasecmp("SHOW TABLES IN ", query_no_space, 15))) { - strA=query_no_space+15; - strAl=strlen(strA); - strB=(char *)"SELECT name AS tables FROM %s.sqlite_master WHERE type='table' AND name NOT IN ('sqlite_sequence') ORDER BY name"; - strBl=strlen(strB); - int l=strBl+strAl-2; - char *b=(char *)l_alloc(l+1); - snprintf(b,l+1,strB,strA); - b[l]=0; - l_free(query_length,query); - query=b; - query_length=l+1; - goto __run_query; - } - - if ((query_no_space_length>17) && (!strncasecmp("SHOW TABLES FROM ", query_no_space, 17))) { - strA=query_no_space+17; - strAl=strlen(strA); - strB=(char *)"SELECT name AS tables FROM %s.sqlite_master WHERE type='table' AND name NOT IN ('sqlite_sequence') ORDER BY name"; - strBl=strlen(strB); - int l=strBl+strAl-2; - char *b=(char *)l_alloc(l+1); - snprintf(b,l+1,strB,strA); - b[l]=0; - l_free(query_length,query); - query=b; - query_length=l+1; - goto __run_query; - } - - if ((query_no_space_length>17) && (!strncasecmp("SHOW TABLES LIKE ", query_no_space, 17))) { - strA=query_no_space+17; - strAl=strlen(strA); - strB=(char *)"SELECT name AS tables FROM sqlite_master WHERE type='table' AND name LIKE '%s'"; - strBl=strlen(strB); - char *tn=NULL; // tablename - tn=(char *)malloc(strAl+1); - unsigned int i=0, j=0; - while (i<(unsigned int)strAl) { - if (strA[i]!='\\' && strA[i]!='`' && strA[i]!='\'') { - tn[j]=strA[i]; - j++; - } - i++; - } - tn[j]=0; - int l=strBl+strlen(tn)-2; - char *b=(char *)l_alloc(l+1); - snprintf(b,l+1,strB,tn); - b[l]=0; - free(tn); - l_free(query_length,query); - query=b; - query_length=l+1; - goto __run_query; - } - - if (query_no_space_length==strlen("SHOW MYSQL USERS") && !strncasecmp("SHOW MYSQL USERS",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT * FROM mysql_users ORDER BY username, active DESC, username ASC"); - query_length=strlen(query)+1; - goto __run_query; - } - - if (query_no_space_length==strlen("SHOW MYSQL SERVERS") && !strncasecmp("SHOW MYSQL SERVERS",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT * FROM mysql_servers ORDER BY hostgroup_id, hostname, port"); - query_length=strlen(query)+1; - goto __run_query; - } - - if ( - (query_no_space_length==strlen("SHOW GLOBAL VARIABLES") && !strncasecmp("SHOW GLOBAL VARIABLES",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SHOW ALL VARIABLES") && !strncasecmp("SHOW ALL VARIABLES",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SHOW VARIABLES") && !strncasecmp("SHOW VARIABLES",query_no_space, query_no_space_length)) - ) { - l_free(query_length,query); - query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables ORDER BY variable_name"); - query_length=strlen(query)+1; - goto __run_query; - } - - if (GloMyLdapAuth) { - if (query_no_space_length==strlen("SHOW LDAP VARIABLES") && !strncasecmp("SHOW LDAP VARIABLES",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'ldap-\%' ORDER BY variable_name"); - query_length=strlen(query)+1; - goto __run_query; - } - } - - if (query_no_space_length==strlen("SHOW ADMIN VARIABLES") && !strncasecmp("SHOW ADMIN VARIABLES",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'admin-\%' ORDER BY variable_name"); - query_length=strlen(query)+1; - goto __run_query; - } - - if (query_no_space_length==strlen("SHOW MYSQL VARIABLES") && !strncasecmp("SHOW MYSQL VARIABLES",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'mysql-\%' ORDER BY variable_name"); - query_length=strlen(query)+1; - goto __run_query; - } - - if (query_no_space_length==strlen("SHOW MYSQL STATUS") && !strncasecmp("SHOW MYSQL STATUS",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT Variable_Name AS Variable_name, Variable_Value AS Value FROM stats_mysql_global ORDER BY variable_name"); - query_length=strlen(query)+1; - GloAdmin->stats___mysql_global(); - goto __run_query; - } - - strA=(char *)"SHOW CREATE TABLE "; - strB=(char *)"SELECT name AS 'table' , REPLACE(REPLACE(sql,' , ', X'2C0A20202020'),'CREATE TABLE %s (','CREATE TABLE %s ('||X'0A20202020') AS 'Create Table' FROM %s.sqlite_master WHERE type='table' AND name='%s'"; - strAl=strlen(strA); - if (strncasecmp("SHOW CREATE TABLE ", query_no_space, strAl)==0) { - strBl=strlen(strB); - char *dbh=NULL; - char *tbh=NULL; - c_split_2(query_no_space+strAl,".",&dbh,&tbh); - - if (strlen(tbh)==0) { - free(tbh); - tbh=dbh; - dbh=strdup("main"); - } - if (strlen(tbh)>=3 && tbh[0]=='`' && tbh[strlen(tbh)-1]=='`') { // tablename is quoted - char *tbh_tmp=(char *)malloc(strlen(tbh)-1); - strncpy(tbh_tmp,tbh+1,strlen(tbh)-2); - tbh_tmp[strlen(tbh)-2]=0; - free(tbh); - tbh=tbh_tmp; - } - int l=strBl+strlen(tbh)*3+strlen(dbh)-8; - char *buff=(char *)l_alloc(l+1); - snprintf(buff,l+1,strB,tbh,tbh,dbh,tbh); - buff[l]=0; - free(tbh); - free(dbh); - l_free(query_length,query); - query=buff; - query_length=l+1; - goto __run_query; - } - - if ( - (query_no_space_length==strlen("SHOW DATABASES") && !strncasecmp("SHOW DATABASES",query_no_space, query_no_space_length)) - || - (query_no_space_length==strlen("SHOW SCHEMAS") && !strncasecmp("SHOW SCHEMAS",query_no_space, query_no_space_length)) - ) { - l_free(query_length,query); - query=l_strdup("PRAGMA DATABASE_LIST"); - query_length=strlen(query)+1; - goto __run_query; - } - - if (query_no_space_length==strlen("SHOW FULL PROCESSLIST") && !strncasecmp("SHOW FULL PROCESSLIST",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT * FROM stats_mysql_processlist"); - query_length=strlen(query)+1; - goto __run_query; - } - - if (query_no_space_length==strlen("SHOW PROCESSLIST") && !strncasecmp("SHOW PROCESSLIST",query_no_space, query_no_space_length)) { - l_free(query_length,query); - query=l_strdup("SELECT SessionID, user, db, hostgroup, command, time_ms, SUBSTR(info,0,100) info FROM stats_mysql_processlist"); - query_length=strlen(query)+1; - goto __run_query; - } - -__end_show_commands: - - if (query_no_space_length==strlen("SELECT DATABASE()") && !strncasecmp("SELECT DATABASE()",query_no_space, query_no_space_length)) { - l_free(query_length,query); - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - query=l_strdup("SELECT \"admin\" AS 'DATABASE()'"); - } else { - query=l_strdup("SELECT \"stats\" AS 'DATABASE()'"); - } - query_length=strlen(query)+1; - goto __run_query; - } - - // see issue #1022 - if (query_no_space_length==strlen("SELECT DATABASE() AS name") && !strncasecmp("SELECT DATABASE() AS name",query_no_space, query_no_space_length)) { - l_free(query_length,query); - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - query=l_strdup("SELECT \"admin\" AS 'name'"); - } else { - query=l_strdup("SELECT \"stats\" AS 'name'"); - } - query_length=strlen(query)+1; - goto __run_query; - } - - if (sess->session_type == PROXYSQL_SESSION_STATS) { // no admin - if ( - (strncasecmp("PRAGMA",query_no_space,6)==0) - || - (strncasecmp("ATTACH",query_no_space,6)==0) - ) { - proxy_error("[WARNING]: Commands executed from stats interface in Admin Module: \"%s\"\n", query_no_space); - SPA->send_error_msg_to_client(sess, (char *)"Command not allowed"); - run_query=false; - } - } - -__run_query: - if (sess->proxysql_node_address && (__sync_fetch_and_add(&glovars.shutdown,0)==0)) { - if (sess->client_myds->active) { - const string uuid { sess->proxysql_node_address->uuid }; - const string hostname { sess->proxysql_node_address->hostname }; - const string port { std::to_string(sess->proxysql_node_address->port) }; - const string mysql_ifaces { sess->proxysql_node_address->admin_mysql_ifaces }; - - time_t now = time(NULL); - string q = "INSERT OR REPLACE INTO stats_proxysql_servers_clients_status (uuid, hostname, port, admin_mysql_ifaces, last_seen_at) VALUES (\""; - q += uuid; - q += "\",\""; - q += hostname; - q += "\","; - q += port; - q += ",\""; - q += mysql_ifaces; - q += "\","; - q += std::to_string(now) + ")"; - SPA->statsdb->execute(q.c_str()); - - std::map m_labels { { "uuid", uuid }, { "hostname", hostname }, { "port", port } }; - const string m_id { uuid + ":" + hostname + ":" + port }; - - p_update_map_gauge( - SPA->metrics.p_proxysql_servers_clients_status_map, - SPA->metrics.p_dyn_gauge_array[p_admin_dyn_gauge::proxysql_servers_clients_status_last_seen_at], - m_id, m_labels, now - ); - } - } - if (run_query) { - ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - if (SPA->get_read_only()) { // disable writes if the admin interface is in read_only mode - SPA->admindb->execute("PRAGMA query_only = ON"); - SPA->admindb->execute_statement(query, &error , &cols , &affected_rows , &resultset); - SPA->admindb->execute("PRAGMA query_only = OFF"); - } else { - SPA->admindb->execute_statement(query, &error , &cols , &affected_rows , &resultset); - } - if (needs_vacuum) { - SPA->vacuum_stats(true); - } - } else { - SPA->statsdb->execute("PRAGMA query_only = ON"); - SPA->statsdb->execute_statement(query, &error , &cols , &affected_rows , &resultset); - SPA->statsdb->execute("PRAGMA query_only = OFF"); - if (needs_vacuum) { - SPA->vacuum_stats(false); - } - } - if (error == NULL) { - - if constexpr (std::is_same::value) { - sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); - } - else if constexpr (std::is_same::value) { - SQLite3_to_Postgres(sess->client_myds->PSarrayOUT, resultset, error, affected_rows, query); - } - - } else { - char *a = (char *)"ProxySQL Admin Error: "; - char *new_msg = (char *)malloc(strlen(error)+strlen(a)+1); - sprintf(new_msg, "%s%s", a, error); - - if constexpr (std::is_same::value) { - sess->SQLite3_to_MySQL(resultset, new_msg, affected_rows, &sess->client_myds->myprot); - } else if constexpr (std::is_same::value) { - SQLite3_to_Postgres(sess->client_myds->PSarrayOUT, resultset, new_msg, affected_rows, query); - } - - free(new_msg); - free(error); - } - delete resultset; - } - if (run_query == true) { - pthread_mutex_unlock(&pa->sql_query_global_mutex); - } else { - // The admin module may have already been freed in case of "PROXYSQL STOP" - if (strcasecmp("PROXYSQL STOP",query_no_space)) - pthread_mutex_unlock(&pa->sql_query_global_mutex); - } - l_free(pkt->size-sizeof(mysql_hdr),query_no_space); // it is always freed here - l_free(query_length,query); -} - -void ProxySQL_Admin::vacuum_stats(bool is_admin) { - if (variables.vacuum_stats==false) { - return; - } - const vector tablenames = { - "stats_mysql_commands_counters", - "stats_mysql_free_connections", - "stats_mysql_connection_pool", - "stats_mysql_connection_pool_reset", - "stats_mysql_prepared_statements_info", - "stats_mysql_processlist", - "stats_mysql_query_digest", - "stats_mysql_query_digest_reset", - "stats_mysql_query_rules", - "stats_mysql_users", - "stats_proxysql_servers_checksums", - "stats_proxysql_servers_metrics", - "stats_proxysql_servers_status", - }; - string s; - SQLite3DB *tmpdb = NULL; - if (is_admin == true) { - tmpdb = admindb; - } else { - tmpdb = statsdb; - } - for (auto it = tablenames.begin(); it != tablenames.end(); it++) { - s = "DELETE FROM "; - if (is_admin == true) s+= "stats."; - s += *it; - tmpdb->execute(s.c_str()); - } - s = "VACUUM"; - if (is_admin == true) - s+= " stats"; - tmpdb->execute(s.c_str()); -} - - -void *child_mysql(void *arg) { - if (GloMTH == nullptr) { return NULL; } - - pthread_attr_t thread_attr; - size_t tmp_stack_size=0; - if (!pthread_attr_init(&thread_attr)) { - if (!pthread_attr_getstacksize(&thread_attr , &tmp_stack_size )) { - __sync_fetch_and_add(&GloVars.statuses.stack_memory_admin_threads,tmp_stack_size); - } - } - - arg_proxysql_adm*myarg = (arg_proxysql_adm*)arg; - int client = myarg->client_t; - - //struct sockaddr *addr = arg->addr; - //socklen_t addr_size; - - GloMTH->wrlock(); - { - char *s=GloMTH->get_variable((char *)"server_capabilities"); - mysql_thread___server_capabilities=atoi(s); - free(s); - } - GloMTH->wrunlock(); - - struct pollfd fds[1]; - nfds_t nfds=1; - int rc; - pthread_mutex_unlock(&sock_mutex); - MySQL_Thread *mysql_thr=new MySQL_Thread(); - mysql_thr->curtime=monotonic_time(); - GloQPro->init_thread(); - mysql_thr->refresh_variables(); - MySQL_Session *sess=mysql_thr->create_new_session_and_client_data_stream(client); - sess->thread=mysql_thr; - sess->session_type = PROXYSQL_SESSION_ADMIN; - sess->handler_function=admin_session_handler; - MySQL_Data_Stream *myds=sess->client_myds; - sess->start_time=mysql_thr->curtime; - - sess->client_myds->client_addrlen=myarg->addr_size; - sess->client_myds->client_addr=myarg->addr; - - switch (sess->client_myds->client_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)sess->client_myds->client_addr; - char buf[INET_ADDRSTRLEN]; - inet_ntop(sess->client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); - sess->client_myds->addr.addr = strdup(buf); - sess->client_myds->addr.port = htons(ipv4->sin_port); - break; - } - case AF_INET6: { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)sess->client_myds->client_addr; - char buf[INET6_ADDRSTRLEN]; - inet_ntop(sess->client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); - sess->client_myds->addr.addr = strdup(buf); - sess->client_myds->addr.port = htons(ipv6->sin6_port); - break; - } - default: - sess->client_myds->addr.addr = strdup("localhost"); - break; - } - fds[0].fd=client; - fds[0].revents=0; - fds[0].events=POLLIN|POLLOUT; - //free(arg->addr); // do not free - free(arg); - - sess->client_myds->myprot.generate_pkt_initial_handshake(true,NULL,NULL, &sess->thread_session_id, false); - - while (__sync_fetch_and_add(&glovars.shutdown,0)==0) { - if (myds->available_data_out()) { - fds[0].events=POLLIN|POLLOUT; - } else { - fds[0].events=POLLIN; - } - fds[0].revents=0; - rc=poll(fds,nfds,__sync_fetch_and_add(&__admin_refresh_interval,0)); - if (rc == -1) { - if (errno == EINTR) { - continue; - } else { - goto __exit_child_mysql; - } - } - mysql_thr->curtime = monotonic_time(); - myds->revents=fds[0].revents; - int rb = 0; - rb = myds->read_from_net(); - if (myds->net_failure) goto __exit_child_mysql; - myds->read_pkts(); - if (myds->encrypted == true) { - // PMC-10004 - // we probably should use SSL_pending() and/or SSL_has_pending() to determine - // if there is more data to be read, but it doesn't seem to be working. - // Therefore we try to call read_from_net() again as long as there is data. - // Previously we hardcoded 16KB but it seems that it can return in smaller - // chunks of 4KB. - // We finally removed the chunk size as it seems that any size is possible. - while (rb > 0) { - rb = myds->read_from_net(); - if (myds->net_failure) goto __exit_child_mysql; - myds->read_pkts(); - } - } - sess->to_process=1; - int rc=sess->handler(); - if (rc==-1) goto __exit_child_mysql; - } - -__exit_child_mysql: - delete mysql_thr; - - __sync_fetch_and_sub(&GloVars.statuses.stack_memory_admin_threads,tmp_stack_size); - - return NULL; -} - -void* child_postgres(void* arg) { - if (GloPTH == nullptr) { return NULL; } - - pthread_attr_t thread_attr; - size_t tmp_stack_size = 0; - if (!pthread_attr_init(&thread_attr)) { - if (!pthread_attr_getstacksize(&thread_attr, &tmp_stack_size)) { - __sync_fetch_and_add(&GloVars.statuses.stack_memory_admin_threads, tmp_stack_size); - } - } - - arg_proxysql_adm* myarg = (arg_proxysql_adm*)arg; - int client = myarg->client_t; - - //struct sockaddr *addr = arg->addr; - //socklen_t addr_size; - - GloPTH->wrlock(); - { - char* s = GloPTH->get_variable((char*)"server_capabilities"); - mysql_thread___server_capabilities = atoi(s); - free(s); - } - GloPTH->wrunlock(); - - struct pollfd fds[1]; - nfds_t nfds = 1; - int rc; - pthread_mutex_unlock(&sock_mutex); - PgSQL_Thread* pgsql_thr = new PgSQL_Thread(); - pgsql_thr->curtime = monotonic_time(); - GloQPro->init_thread(); - pgsql_thr->refresh_variables(); - PgSQL_Session* sess = pgsql_thr->create_new_session_and_client_data_stream(client); - sess->thread = pgsql_thr; - sess->session_type = PROXYSQL_SESSION_ADMIN; - sess->handler_function=admin_session_handler; - PgSQL_Data_Stream* myds = sess->client_myds; - sess->start_time = pgsql_thr->curtime; - - sess->client_myds->client_addrlen = myarg->addr_size; - sess->client_myds->client_addr = myarg->addr; - - - - switch (sess->client_myds->client_addr->sa_family) { - case AF_INET: { - struct sockaddr_in* ipv4 = (struct sockaddr_in*)sess->client_myds->client_addr; - char buf[INET_ADDRSTRLEN]; - inet_ntop(sess->client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); - sess->client_myds->addr.addr = strdup(buf); - sess->client_myds->addr.port = htons(ipv4->sin_port); - break; - } - case AF_INET6: { - struct sockaddr_in6* ipv6 = (struct sockaddr_in6*)sess->client_myds->client_addr; - char buf[INET6_ADDRSTRLEN]; - inet_ntop(sess->client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); - sess->client_myds->addr.addr = strdup(buf); - sess->client_myds->addr.port = htons(ipv6->sin6_port); - break; - } - default: - sess->client_myds->addr.addr = strdup("localhost"); - break; - } - - fds[0].fd = client; - fds[0].revents = 0; - fds[0].events = POLLIN | POLLOUT; - //free(arg->addr); // do not free - free(arg); - - myds->DSS = STATE_SERVER_HANDSHAKE; - sess->status = CONNECTING_CLIENT; - - while (__sync_fetch_and_add(&glovars.shutdown, 0) == 0) { - if (myds->available_data_out()) { - fds[0].events = POLLIN | POLLOUT; - } - else { - fds[0].events = POLLIN; - } - fds[0].revents = 0; - rc = poll(fds, nfds, __sync_fetch_and_add(&__admin_refresh_interval, 0)); - if (rc == -1) { - if (errno == EINTR) { - continue; - } - else { - goto __exit_child_postgres; - } - } - pgsql_thr->curtime = monotonic_time(); - myds->revents = fds[0].revents; - int rb = 0; - rb = myds->read_from_net(); - if (myds->net_failure) goto __exit_child_postgres; - myds->read_pkts(); - if (myds->encrypted == true) { - // PMC-10004 - // we probably should use SSL_pending() and/or SSL_has_pending() to determine - // if there is more data to be read, but it doesn't seem to be working. - // Therefore we try to call read_from_net() again as long as there is data. - // Previously we hardcoded 16KB but it seems that it can return in smaller - // chunks of 4KB. - // We finally removed the chunk size as it seems that any size is possible. - while (rb > 0) { - rb = myds->read_from_net(); - if (myds->net_failure) goto __exit_child_postgres; - myds->read_pkts(); - } - } - sess->to_process = 1; - int rc = sess->handler(); - if (rc == -1) goto __exit_child_postgres; - } - - - -__exit_child_postgres: - delete pgsql_thr; - - __sync_fetch_and_sub(&GloVars.statuses.stack_memory_admin_threads, tmp_stack_size); - - return NULL; -} - -void* child_telnet(void* arg) -{ - int bytes_read; - char line[LINESIZE+1]; - int client = *(int *)arg; - free(arg); - pthread_mutex_unlock(&sock_mutex); - memset(line,0,LINESIZE+1); - while ((strncmp(line, "quit", 4) != 0) && glovars.shutdown==0) { - bytes_read = recv(client, line, LINESIZE, 0); - if (bytes_read==-1) { - break; - } - char *eow = strchr(line, '\n'); - if (eow) *eow=0; - //SPA->is_command(line); - if (strncmp(line,"shutdown",8)==0) glovars.shutdown=1; - if (send(client, line, strlen(line), MSG_NOSIGNAL)==-1) break; - if (send(client, "\nOK\n", 4, MSG_NOSIGNAL)==-1) break; - } - shutdown(client,SHUT_RDWR); - close(client); - return arg; -} - -/* -void* child_telnet_also(void* arg) -{ - int bytes_read; - char line[LINESIZE+1]; - int client = *(int *)arg; - free(arg); - pthread_mutex_unlock(&sock_mutex); - memset(line,0,LINESIZE+1); - while ((strncmp(line, "quit", 4) != 0) && glovars.shutdown==0) { - bytes_read = recv(client, line, LINESIZE, 0); - if (bytes_read==-1) { - break; - } - char *eow = strchr(line, '\n'); - if (eow) *eow=0; - if (strncmp(line,"shutdown",8)==0) glovars.shutdown=1; - if (send(client, line, strlen(line), MSG_NOSIGNAL)==-1) break; - if (send(client, "\nNOT OK\n", 8, MSG_NOSIGNAL)==-1) break; - } - shutdown(client,SHUT_RDWR); - close(client); - return arg; -} -*/ - -static void * admin_main_loop(void *arg) -{ - int i; - int rc; - int version=0; - struct pollfd *fds=((struct _main_args *)arg)->fds; - int nfds=((struct _main_args *)arg)->nfds; - int *callback_func=((struct _main_args *)arg)->callback_func; - volatile int *shutdown=((struct _main_args *)arg)->shutdown; - char *socket_names[MAX_ADMIN_LISTENERS]; - for (i=0;ischeduler_run_once(); - unsigned long long poll_wait=500000; - if (next_run < curtime + 500000) { - poll_wait=next_run-curtime; - } - if (poll_wait > 500000) { - poll_wait=500000; - } - poll_wait=poll_wait/1000; // conversion to millisecond - rc=poll(fds,nfds,poll_wait); - if ((nostart_ && __sync_val_compare_and_swap(&GloVars.global.nostart,0,1)==0) || __sync_fetch_and_add(&glovars.shutdown,0)==1) { - nostart_=false; - pthread_mutex_unlock(&GloVars.global.start_mutex); - } - if ((rc == -1 && errno == EINTR) || rc==0) { - // poll() timeout, try again - goto __end_while_pool; - } - for (i=1;iaddr=(struct sockaddr *)malloc(sizeof(custom_sockaddr)); - passarg->addr_size = sizeof(custom_sockaddr); - memset(passarg->addr, 0, sizeof(custom_sockaddr)); - passarg->client_t = accept(fds[i].fd, (struct sockaddr*)passarg->addr, &passarg->addr_size); -// printf("Connected: %s:%d sock=%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), client_t); - pthread_attr_getstacksize (&attr, &stacks); -// printf("Default stack size = %d\n", stacks); - pthread_mutex_lock (&sock_mutex); - //client=(int *)malloc(sizeof(int)); - //*client= client_t; - //if ( pthread_create(&child, &attr, child_func[callback_func[i]], client) != 0 ) { - if ( pthread_create(&child, &attr, child_func[callback_func[i]], passarg) != 0 ) { - // LCOV_EXCL_START - perror("pthread_create"); - proxy_error("Thread creation\n"); - assert(0); - // LCOV_EXCL_STOP - } - } - fds[i].revents=0; - } -__end_while_pool: - { - if (GloProxyStats->MySQL_Threads_Handler_timetoget(curtime)) { - if (GloMTH) { - SQLite3_result * resultset=GloMTH->SQL3_GlobalStatus(false); - if (resultset) { - GloProxyStats->MySQL_Threads_Handler_sets(resultset); - delete resultset; - } - } - if (MyHGM) { - SQLite3_result * resultset=MyHGM->SQL3_Get_ConnPool_Stats(); - if (resultset) { - SQLite3_result * resultset2 = NULL; - - // In debug, run the code to generate metrics so that it can be tested even if the web interface plugin isn't loaded. - #ifdef DEBUG - if (true) { - #else - if (GloVars.web_interface_plugin) { - #endif - resultset2 = MyHGM->SQL3_Connection_Pool(false); - } - GloProxyStats->MyHGM_Handler_sets(resultset, resultset2); - delete resultset; - if (resultset2) { - delete resultset2; - } - } - } - } - if (GloProxyStats->MySQL_Query_Cache_timetoget(curtime)) { - if (GloQC) { - SQLite3_result * resultset=GloQC->SQL3_getStats(); - if (resultset) { - GloProxyStats->MySQL_Query_Cache_sets(resultset); - delete resultset; - } - } - } - if (GloProxyStats->mysql_query_digest_to_disk_timetoget(curtime)) { - unsigned long long curtime1=monotonic_time(); - int r1 = SPA->FlushDigestTableToDisk(SPA->statsdb_disk); - unsigned long long curtime2=monotonic_time(); - curtime1 = curtime1/1000; - curtime2 = curtime2/1000; - proxy_info("Automatically saved stats_mysql_query_digest to disk: %llums to write %d entries\n", curtime2-curtime1, r1); - } - if (GloProxyStats->system_cpu_timetoget(curtime)) { - GloProxyStats->system_cpu_sets(); - } -#ifndef NOJEM - if (GloProxyStats->system_memory_timetoget(curtime)) { - GloProxyStats->system_memory_sets(); - } -#endif - } - if (S_amll.get_version()!=version) { - S_amll.wrlock(); - version=S_amll.get_version(); - for (i=1; ipipefd[0]; - fds[nfds].events=POLLIN; - fds[nfds].revents=0; - nfds++; - unsigned int j; - i=0; j=0; - for (j=0; jifaces->len; j++) { - char *add=NULL; char *port=NULL; char *sn=(char *)S_amll.ifaces_mysql->ifaces->index(j); - bool is_ipv6 = false; - char *h = NULL; - if (*sn == '[') { - is_ipv6 = true; - char *p = strchr(sn, ']'); - if (p == NULL) - proxy_error("Invalid IPv6 address: %s\n", sn); - - h = ++sn; // remove first '[' - *p = '\0'; - sn = p++; // remove last ']' - add = h; - port = ++p; // remove ':' - } else { - c_split_2(sn, ":" , &add, &port); - } - -#ifdef SO_REUSEPORT - int s = ( atoi(port) ? listen_on_port(add, atoi(port), 128, true) : listen_on_unix(add, 128)); -#else - int s = ( atoi(port) ? listen_on_port(add, atoi(port), 128) : listen_on_unix(add, 128)); -#endif - //if (s>0) { fds[nfds].fd=s; fds[nfds].events=POLLIN; fds[nfds].revents=0; callback_func[nfds]=0; socket_names[nfds]=strdup(sn); nfds++; } - if (s > 0) { - fds[nfds].fd = s; - fds[nfds].events = POLLIN; - fds[nfds].revents = 0; - callback_func[nfds] = 0; - socket_names[nfds] = strdup(sn); - nfds++; - } - if (is_ipv6 == false) { - if (add) free(add); - if (port) free(port); - } - } - - i = 0; j = 0; - for (; j < S_amll.ifaces_pgsql->ifaces->len; j++) { - char* add = NULL; char* port = NULL; char* sn = (char*)S_amll.ifaces_pgsql->ifaces->index(j); - bool is_ipv6 = false; - char* h = NULL; - if (*sn == '[') { - is_ipv6 = true; - char* p = strchr(sn, ']'); - if (p == NULL) - proxy_error("Invalid IPv6 address: %s\n", sn); - - h = ++sn; // remove first '[' - *p = '\0'; - sn = p++; // remove last ']' - add = h; - port = ++p; // remove ':' - } - else { - c_split_2(sn, ":", &add, &port); - } - -#ifdef SO_REUSEPORT - int s = (atoi(port) ? listen_on_port(add, atoi(port), 128, true) : listen_on_unix(add, 128)); -#else - int s = (atoi(port) ? listen_on_port(add, atoi(port), 128) : listen_on_unix(add, 128)); -#endif - //if (s>0) { fds[nfds].fd=s; fds[nfds].events=POLLIN; fds[nfds].revents=0; callback_func[nfds]=0; socket_names[nfds]=strdup(sn); nfds++; } - if (s > 0) { - fds[nfds].fd = s; - fds[nfds].events = POLLIN; - fds[nfds].revents = 0; - callback_func[nfds] = 2; - socket_names[nfds] = strdup(sn); - nfds++; - } - if (is_ipv6 == false) { - if (add) free(add); - if (port) free(port); - } - } - S_amll.wrunlock(); - } - - } - //if (__sync_add_and_fetch(shutdown,0)==0) __sync_add_and_fetch(shutdown,1); - for (i=0; ip_update_metrics(); - } - // Update pgsql_threads_handler metrics - if (GloPTH) { - GloPTH->p_update_metrics(); - } - // Update mysql_hostgroups_manager metrics - if (MyHGM) { - MyHGM->p_update_metrics(); - } - // Update monitor metrics - if (GloMyMon) { - GloMyMon->p_update_metrics(); - } - // Update query_cache metrics - if (GloQC) { - GloQC->p_update_metrics(); - } - // Update cluster metrics - if (GloProxyCluster) { - GloProxyCluster->p_update_metrics(); - } - - // Update admin metrics - GloAdmin->p_update_metrics(); -} - -ProxySQL_Admin::ProxySQL_Admin() : - serial_exposer(std::function { update_modules_metrics }) -{ -#ifdef DEBUG - debugdb_disk = NULL; - if (glovars.has_debug==false) { -#else - if (glovars.has_debug==true) { -#endif /* DEBUG */ - perror("Incompatible debugging version"); - exit(EXIT_FAILURE); - } - - if (proxysql_version == NULL) { - proxysql_version = strdup(PROXYSQL_VERSION); - } - SPA=this; - - //Initialize locker -#ifdef PA_PTHREAD_MUTEX - pthread_rwlock_init(&rwlock,NULL); -#else - spinlock_rwlock_init(&rwlock); -#endif - -#ifdef PA_PTHREAD_MUTEX - pthread_mutex_init(&mysql_servers_lock, NULL); -#else - spinlock_rwlock_init(&mysql_servers_rwlock); -#endif - -#ifdef PA_PTHREAD_MUTEX - pthread_mutex_init(&pgsql_servers_lock, NULL); -#else - spinlock_rwlock_init(&pgsql_servers_rwlock); -#endif - - pthread_mutex_init(&sql_query_global_mutex, NULL); - - generate_load_save_disk_commands("mysql_firewall", "MYSQL FIREWALL"); - generate_load_save_disk_commands("mysql_query_rules", "MYSQL QUERY RULES"); - generate_load_save_disk_commands("mysql_users", "MYSQL USERS"); - generate_load_save_disk_commands("mysql_servers", "MYSQL SERVERS"); - generate_load_save_disk_commands("mysql_variables", "MYSQL VARIABLES"); - generate_load_save_disk_commands("pgsql_firewall", "PGSQL FIREWALL"); - generate_load_save_disk_commands("pgsql_query_rules", "PGSQL QUERY RULES"); - generate_load_save_disk_commands("pgsql_users", "PGSQL USERS"); - generate_load_save_disk_commands("pgsql_servers", "PGSQL SERVERS"); - generate_load_save_disk_commands("pgsql_variables", "PGSQL VARIABLES"); - generate_load_save_disk_commands("scheduler", "SCHEDULER"); - generate_load_save_disk_commands("restapi", "RESTAPI"); - generate_load_save_disk_commands("proxysql_servers", "PROXYSQL SERVERS"); - - { - // we perform some sanity check - assert(load_save_disk_commands.size() > 0); - for (auto it = load_save_disk_commands.begin(); it != load_save_disk_commands.end(); it++) { - vector& vec1 = get<1>(it->second); - assert(vec1.size() == 3); - vector& vec2 = get<2>(it->second); - assert(vec2.size() == 3); - } - } - - - variables.admin_credentials=strdup("admin:admin"); - variables.stats_credentials=strdup("stats:stats"); - if (GloVars.__cmd_proxysql_admin_socket) { - variables.mysql_ifaces=strdup(GloVars.__cmd_proxysql_admin_socket); - } else { - variables.mysql_ifaces=strdup("0.0.0.0:6032"); // changed. See isseu #1103 - } - variables.pgsql_ifaces= strdup("0.0.0.0:6132"); - variables.telnet_admin_ifaces=NULL; - variables.telnet_stats_ifaces=NULL; - variables.refresh_interval=2000; - variables.mysql_show_processlist_extended = false; - variables.pgsql_show_processlist_extended = false; - //variables.hash_passwords=true; // issue #676 - variables.vacuum_stats=true; // issue #1011 - variables.admin_read_only=false; // by default, the admin interface accepts writes - variables.admin_version=(char *)PROXYSQL_VERSION; - variables.cluster_username=strdup((char *)""); - variables.cluster_password=strdup((char *)""); - variables.cluster_check_interval_ms=1000; - variables.cluster_check_status_frequency=10; - variables.cluster_mysql_query_rules_diffs_before_sync = 3; - variables.cluster_mysql_servers_diffs_before_sync = 3; - variables.cluster_mysql_users_diffs_before_sync = 3; - variables.cluster_proxysql_servers_diffs_before_sync = 3; - variables.cluster_mysql_variables_diffs_before_sync = 3; - variables.cluster_admin_variables_diffs_before_sync = 3; - variables.cluster_ldap_variables_diffs_before_sync = 3; - variables.cluster_mysql_servers_sync_algorithm = 1; - checksum_variables.checksum_mysql_query_rules = true; - checksum_variables.checksum_mysql_servers = true; - checksum_variables.checksum_mysql_users = true; - checksum_variables.checksum_mysql_variables = true; - checksum_variables.checksum_admin_variables = true; - checksum_variables.checksum_ldap_variables = true; - variables.cluster_mysql_query_rules_save_to_disk = true; - variables.cluster_mysql_servers_save_to_disk = true; - variables.cluster_mysql_users_save_to_disk = true; - variables.cluster_proxysql_servers_save_to_disk = true; - variables.cluster_mysql_variables_save_to_disk = true; - variables.cluster_admin_variables_save_to_disk = true; - variables.cluster_ldap_variables_save_to_disk = true; - variables.stats_mysql_connection_pool = 60; - variables.stats_mysql_connections = 60; - variables.stats_mysql_query_cache = 60; - variables.stats_mysql_query_digest_to_disk = 0; - variables.stats_system_cpu = 60; - variables.stats_system_memory = 60; - GloProxyStats->variables.stats_mysql_connection_pool = 60; - GloProxyStats->variables.stats_mysql_connections = 60; - GloProxyStats->variables.stats_mysql_query_cache = 60; - GloProxyStats->variables.stats_mysql_query_digest_to_disk = 0; - GloProxyStats->variables.stats_system_cpu = 60; -#ifndef NOJEM - GloProxyStats->variables.stats_system_memory = 60; -#endif - - variables.restapi_enabled = false; - variables.restapi_enabled_old = false; - variables.restapi_port = 6070; - variables.restapi_port_old = variables.restapi_port; - variables.web_enabled = false; - variables.web_enabled_old = false; - variables.web_port = 6080; - variables.web_port_old = variables.web_port; - variables.web_verbosity = 0; - variables.p_memory_metrics_interval = 61; -#ifdef DEBUG - variables.debug=GloVars.global.gdbg; - all_modules_started = false; - debug_output = 1; - proxysql_set_admin_debug_output(debug_output); -#endif /* DEBUG */ - variables.coredump_generation_interval_ms = 30000; - variables.coredump_generation_threshold = 10; - variables.ssl_keylog_file = strdup(""); - last_p_memory_metrics_ts = 0; - // create the scheduler - scheduler=new ProxySQL_External_Scheduler(); - - match_regexes.opt=(re2::RE2::Options *)new re2::RE2::Options(RE2::Quiet); - re2::RE2::Options *opt2=(re2::RE2::Options *)match_regexes.opt; - opt2->set_case_sensitive(false); - match_regexes.re=(void **)malloc(sizeof(void *)*10); - match_regexes.re[0]=(RE2 *)new RE2("^SELECT\\s+@@max_allowed_packet\\s*", *opt2); - match_regexes.re[1]=(RE2 *)new RE2("^SELECT\\s+@@[0-9A-Za-z_-]+\\s*", *opt2); - match_regexes.re[2]=(RE2 *)new RE2("SHOW\\s+VARIABLES\\s+WHERE", *opt2); - match_regexes.re[3]=(RE2 *)new RE2("SHOW\\s+VARIABLES\\s+LIKE", *opt2); - - // Default initialize prometheus collectable flag - registered_prometheus_collectable = false; - - // Initialize prometheus metrics - init_prometheus_counter_array(admin_metrics_map, this->metrics.p_counter_array); - init_prometheus_gauge_array(admin_metrics_map, this->metrics.p_gauge_array); - init_prometheus_dyn_gauge_array(admin_metrics_map, this->metrics.p_dyn_gauge_array); - - // NOTE: Imposing fixed value to 'version_info' matching 'mysqld_exporter' - this->metrics.p_gauge_array[p_admin_gauge::version_info]->Set(1); -}; - -void ProxySQL_Admin::wrlock() { -#ifdef PA_PTHREAD_MUTEX - pthread_rwlock_wrlock(&rwlock); -#else - spin_wrlock(&rwlock); -#endif -}; - -void ProxySQL_Admin::wrunlock() { -#ifdef PA_PTHREAD_MUTEX - pthread_rwlock_unlock(&rwlock); -#else - spin_wrunlock(&rwlock); -#endif -}; - -void ProxySQL_Admin::mysql_servers_wrlock() { - #ifdef PA_PTHREAD_MUTEX - pthread_mutex_lock(&mysql_servers_lock); - #else - spin_wrlock(&mysql_servers_rwlock); - #endif -}; - -void ProxySQL_Admin::mysql_servers_wrunlock() { - #ifdef PA_PTHREAD_MUTEX - pthread_mutex_unlock(&mysql_servers_lock); - #else - spin_wrunlock(&mysql_servers_rwlock); - #endif -}; - -void ProxySQL_Admin::pgsql_servers_wrlock() { -#ifdef PA_PTHREAD_MUTEX - pthread_mutex_lock(&pgsql_servers_lock); -#else - spin_wrlock(&pgsql_servers_rwlock); -#endif -}; - -void ProxySQL_Admin::pgsql_servers_wrunlock() { -#ifdef PA_PTHREAD_MUTEX - pthread_mutex_unlock(&pgsql_servers_lock); -#else - spin_wrunlock(&pgsql_servers_rwlock); -#endif -}; - -void ProxySQL_Admin::print_version() { - fprintf(stderr,"Standard ProxySQL Admin rev. %s -- %s -- %s\n", PROXYSQL_ADMIN_VERSION, __FILE__, __TIMESTAMP__); -}; - -void ProxySQL_Admin::init_ldap() { - if (GloMyLdapAuth) { - insert_into_tables_defs(tables_defs_admin,"mysql_ldap_mapping", ADMIN_SQLITE_TABLE_MYSQL_LDAP_MAPPING); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_ldap_mapping", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_LDAP_MAPPING); - insert_into_tables_defs(tables_defs_config,"mysql_ldap_mapping", ADMIN_SQLITE_TABLE_MYSQL_LDAP_MAPPING); - } -} - -void ProxySQL_Admin::init_http_server() { - AdminHTTPServer = new ProxySQL_HTTP_Server(); - AdminHTTPServer->init(); - AdminHTTPServer->print_version(); -} - -struct boot_srv_info_t { - string member_id; - string member_host; - uint32_t member_port; - string member_state; - string member_role; - string member_version; -}; - -struct BOOT_SRV_INFO_T { - enum { - MEMBER_ID, - MEMBER_HOST, - MEMBER_PORT, - MEMBER_STATE, - MEMBER_ROLE, - MEMBER_VERSION - }; -}; - -struct boot_user_info_t { - string user; - string ssl_type; - string auth_string; - string auth_plugin; - bool password_expired; -}; - -struct BOOT_USER_INFO_T { - enum { - USER, - SSL_TYPE, - AUTH_STRING, - AUTH_PLUGIN, - PASSWORD_EXPIRED - }; -}; - -struct srv_defs_t { - int64_t weight; - int64_t max_conns; - int32_t use_ssl; -}; - -using boot_srv_cnf_t = pair; - -vector extract_boot_servers_info(MYSQL_RES* servers) { - vector servers_info {}; - - while (MYSQL_ROW row = mysql_fetch_row(servers)) { - servers_info.push_back({ - string { row[BOOT_SRV_INFO_T::MEMBER_ID] }, - string { row[BOOT_SRV_INFO_T::MEMBER_HOST] }, - static_cast(stoi(row[BOOT_SRV_INFO_T::MEMBER_PORT])), - string { row[BOOT_SRV_INFO_T::MEMBER_STATE] }, - string { row[BOOT_SRV_INFO_T::MEMBER_ROLE] }, - string { row[BOOT_SRV_INFO_T::MEMBER_VERSION] }, - }); - } - - return servers_info; -} - -string build_boot_servers_insert(const vector& srvs_info_defs) { - const string t_srvs_insert { - "INSERT INTO mysql_servers (hostgroup_id,hostname,port,status,weight,max_connections,use_ssl) VALUES " - }; - string t_srvs_values {}; - - for (const auto& info_defs : srvs_info_defs) { - const boot_srv_info_t& srv_info = info_defs.first; - const srv_defs_t& srv_defs = info_defs.second; - - const char t_values[] { "(%d, \"%s\", %d, \"%s\", %ld, %ld, %d)" }; - string srv_values = cstr_format( - t_values, - srv_info.member_role == "PRIMARY" ? 0 : 1, // HOSTGROUP_ID - srv_info.member_host.c_str(), // HOSTNAME - srv_info.member_port, // PORT - srv_info.member_state.c_str(), // STATUS - srv_defs.weight, // Weight - srv_defs.max_conns, // Max Connections - srv_defs.use_ssl // UseSSL - ).str; - - if (&info_defs != &srvs_info_defs.back()) { - srv_values += ","; - } - - t_srvs_values += srv_values; - } - - const string servers_insert { t_srvs_insert + t_srvs_values }; - - return servers_insert; -} - -string build_boot_users_insert(MYSQL_RES* users) { - vector users_info {}; - - while (MYSQL_ROW row = mysql_fetch_row(users)) { - users_info.push_back({ - string { row[BOOT_USER_INFO_T::USER] }, - string { row[BOOT_USER_INFO_T::SSL_TYPE] }, - string { row[BOOT_USER_INFO_T::AUTH_STRING] }, - string { row[BOOT_USER_INFO_T::AUTH_PLUGIN] }, - static_cast(atoi(row[BOOT_USER_INFO_T::PASSWORD_EXPIRED])) - }); - } - - // MySQL Users - const string t_users_insert { - "INSERT INTO mysql_users (username,password,active,use_ssl) VALUES " - }; - string t_users_values {}; - - for (const boot_user_info_t& user : users_info) { - uint32_t use_ssl = user.ssl_type.empty() ? 0 : 1; - const char t_values[] { "(\"%s\", \"%s\", %d, %d)" }; - - string srv_values = cstr_format( - t_values, - user.user.c_str(), // USERNAME - user.auth_string.c_str(), // HOSTNAME - 1, // ACTIVE: Always ON - use_ssl // USE_SSL: Dependent on backend user - ).str; - - if (&user != &users_info.back()) { - srv_values += ","; + case AF_INET6: { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)sess->client_myds->client_addr; + char buf[INET6_ADDRSTRLEN]; + inet_ntop(sess->client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); + sess->client_myds->addr.addr = strdup(buf); + sess->client_myds->addr.port = htons(ipv6->sin6_port); + break; } - - t_users_values += srv_values; - } - - const string users_insert { t_users_insert + t_users_values }; - - return users_insert; -} - -map get_cur_hg_attrs(SQLite3DB* admindb) { - map res {}; - - char* error = nullptr; - int cols = 0; - int affected_rows = 0; - SQLite3_result* resultset = NULL; - - admindb->execute_statement( - "SELECT hostgroup_id,servers_defaults FROM mysql_hostgroup_attributes", - &error, &cols, &affected_rows, &resultset - ); - - for (SQLite3_row* row : resultset->rows) { - const int32_t hid = atoi(row->fields[0]); - srv_defs_t srv_defs {}; - srv_defs.weight = 1; - srv_defs.max_conns = 512; - srv_defs.use_ssl = 1; - - nlohmann::json j_srv_defs = nlohmann::json::parse(row->fields[1]); - - const auto weight_check = [] (int64_t weight) -> bool { return weight >= 0; }; - srv_defs.weight = j_get_srv_default_int_val(j_srv_defs, hid, "weight", weight_check); - - const auto max_conns_check = [] (int64_t max_conns) -> bool { return max_conns >= 0; }; - srv_defs.max_conns = j_get_srv_default_int_val(j_srv_defs, hid, "max_connections", max_conns_check); - - const auto use_ssl_check = [] (int32_t use_ssl) -> bool { return use_ssl == 0 || use_ssl == 1; }; - srv_defs.use_ssl = j_get_srv_default_int_val(j_srv_defs, hid, "use_ssl", use_ssl_check); - - res.insert({ hid , srv_defs }); + default: + sess->client_myds->addr.addr = strdup("localhost"); + break; } + fds[0].fd=client; + fds[0].revents=0; + fds[0].events=POLLIN|POLLOUT; + //free(arg->addr); // do not free + free(arg); - delete resultset; - - return res; -} - -vector build_srvs_info_with_defs( - const vector& srvs_info, - const map& hgid_defs, - const srv_defs_t global_defs -) { - vector res {}; - - for (const boot_srv_info_t& srv_info : srvs_info) { - if (srv_info.member_role == "PRIMARY") { - const auto hg_it = hgid_defs.find(0); + sess->client_myds->myprot.generate_pkt_initial_handshake(true,NULL,NULL, &sess->thread_session_id, false); - if (hg_it != hgid_defs.end()) { - res.push_back({ srv_info, hg_it->second }); - } else { - res.push_back({ srv_info, global_defs }); - } + while (__sync_fetch_and_add(&glovars.shutdown,0)==0) { + if (myds->available_data_out()) { + fds[0].events=POLLIN|POLLOUT; } else { - const auto hg_it = hgid_defs.find(1); - - if (hg_it != hgid_defs.end()) { - res.push_back({ srv_info, hg_it->second }); + fds[0].events=POLLIN; + } + fds[0].revents=0; + rc=poll(fds,nfds,__sync_fetch_and_add(&__admin_refresh_interval,0)); + if (rc == -1) { + if (errno == EINTR) { + continue; } else { - res.push_back({ srv_info, global_defs }); + goto __exit_child_mysql; } } - } - - return res; -} - -/** - * @brief Helper function used to check if tables are already filled with data. - * @details Handles the boilerplate operations of executing 'SELECT COUNT(*)' alike queries. - * @param admindb An already initialized instance of a SQLite3DB object to 'mem_admindb'. - * @param query The query to be executed, it's required to be 'SELECT COUNT(*)' alike. - * @return The resulting int of the 'COUNT(*)' in case of success, '-1' otherwise. In case of error, error - * cause are logged, and `assert` is called. - */ -int check_if_user_config(SQLite3DB* admindb, const char* query) { - char* error = nullptr; - int cols = 0; - int affected_rows = 0; - SQLite3_result* resultset = NULL; - - admindb->execute_statement(query, &error, &cols, &affected_rows, &resultset); - if (error) { - proxy_error( - "Aborting due to failed query over SQLite3 - db: '%s', query: '%s', err: %s", admindb->get_url(), query, error - ); - assert(0); - } - - int count = -1; - - if (resultset != nullptr && !resultset->rows.empty() && resultset->rows[0]->cnt >= 0) { - const char* s_count = resultset->rows[0]->fields[0]; - char* p_end = nullptr; - - count = std::strtol(s_count, &p_end, 10); - - if (p_end == s_count || errno == ERANGE) { - proxy_error( - "Aborting due to invalid query output, expected single INT (E.g. 'COUNT(*)') - query: '%s'", query - ); - count = -1; - } - } - - if (count == -1) { - assert(0); - } - - delete resultset; - return count; -}; - -/** - * @brief Definition of an auxiliary table used to store bootstrap variables. - * @details Table is used only to store in configdb bootstrap variables that are required to persist between - * executions. - */ -#define ADMIN_SQLITE_TABLE_BOOTSTRAP_VARIABLES "CREATE TABLE IF NOT EXISTS bootstrap_variables (variable_name VARCHAR NOT NULL PRIMARY KEY , variable_value VARCHAR NOT NULL)" - -bool ProxySQL_Admin::init(const bootstrap_info_t& bootstrap_info) { - cpu_timer cpt; - - if (flush_logs_function == NULL) { - flush_logs_function = flush_logs_handler; - } - - Admin_HTTP_Server = NULL; - AdminRestApiServer = NULL; - AdminHTTPServer = NULL; - -/* - AdminRestApiServer = new ProxySQL_RESTAPI_Server(); - AdminRestApiServer->print_version(); -*/ - - child_func[0]=child_mysql; - child_func[1]=child_telnet; - child_func[2]=child_postgres; - main_shutdown=0; - main_poll_nfds=0; - main_poll_fds=NULL; - main_callback_func=NULL; - - { - int rc=pipe(pipefd); - if (rc) { - perror("Call to pipe() failed"); - exit(EXIT_FAILURE); + mysql_thr->curtime = monotonic_time(); + myds->revents=fds[0].revents; + int rb = 0; + rb = myds->read_from_net(); + if (myds->net_failure) goto __exit_child_mysql; + myds->read_pkts(); + if (myds->encrypted == true) { + // PMC-10004 + // we probably should use SSL_pending() and/or SSL_has_pending() to determine + // if there is more data to be read, but it doesn't seem to be working. + // Therefore we try to call read_from_net() again as long as there is data. + // Previously we hardcoded 16KB but it seems that it can return in smaller + // chunks of 4KB. + // We finally removed the chunk size as it seems that any size is possible. + while (rb > 0) { + rb = myds->read_from_net(); + if (myds->net_failure) goto __exit_child_mysql; + myds->read_pkts(); + } } + sess->to_process=1; + int rc=sess->handler(); + if (rc==-1) goto __exit_child_mysql; } - main_callback_func=(int *)malloc(sizeof(int)*MAX_ADMIN_LISTENERS); - main_poll_fds=(struct pollfd *)malloc(sizeof(struct pollfd)*MAX_ADMIN_LISTENERS); - main_poll_nfds=0; - - pthread_attr_t attr; - pthread_attr_init(&attr); - //pthread_attr_setstacksize (&attr, mystacksize); +__exit_child_mysql: + delete mysql_thr; - admindb=new SQLite3DB(); - admindb->open((char *)"file:mem_admindb?mode=memory&cache=shared", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); - admindb->execute("PRAGMA cache_size = -50000"); - //sqlite3_enable_load_extension(admindb->get_db(),1); - //sqlite3_auto_extension( (void(*)(void))sqlite3_json_init); - statsdb=new SQLite3DB(); - statsdb->open((char *)"file:mem_statsdb?mode=memory&cache=shared", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); + __sync_fetch_and_sub(&GloVars.statuses.stack_memory_admin_threads,tmp_stack_size); - // check if file exists , see #617 - bool admindb_file_exists=Proxy_file_exists(GloVars.admindb); + return NULL; +} - configdb=new SQLite3DB(); - configdb->open((char *)GloVars.admindb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); - // Fully synchronous is not required. See to #1055 - // https://sqlite.org/pragma.html#pragma_synchronous - configdb->execute("PRAGMA synchronous=0"); +void* child_postgres(void* arg) { + if (GloPTH == nullptr) { return NULL; } - monitordb = new SQLite3DB(); - monitordb->open((char *)"file:mem_monitordb?mode=memory&cache=shared", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); - - statsdb_disk = new SQLite3DB(); - statsdb_disk->open((char *)GloVars.statsdb_disk, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); -// char *dbname = (char *)malloc(strlen(GloVars.statsdb_disk)+50); -// sprintf(dbname,"%s?mode=memory&cache=shared",GloVars.statsdb_disk); -// statsdb_disk->open(dbname, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_FULLMUTEX); -// free(dbname); - - statsdb_disk->execute("PRAGMA synchronous=0"); -// GloProxyStats->statsdb_disk = configdb; - GloProxyStats->init(); - - tables_defs_admin=new std::vector; - tables_defs_stats=new std::vector; - tables_defs_config=new std::vector; - - insert_into_tables_defs(tables_defs_admin,"mysql_servers", ADMIN_SQLITE_TABLE_MYSQL_SERVERS); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_servers", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_SERVERS); - insert_into_tables_defs(tables_defs_admin,"mysql_users", ADMIN_SQLITE_TABLE_MYSQL_USERS); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_users", ADMIN_SQLITE_RUNTIME_MYSQL_USERS); - insert_into_tables_defs(tables_defs_admin,"runtime_checksums_values", ADMIN_SQLITE_RUNTIME_CHECKSUMS_VALUES); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_replication_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_REPLICATION_HOSTGROUPS); - insert_into_tables_defs(tables_defs_admin,"mysql_replication_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_REPLICATION_HOSTGROUPS); - insert_into_tables_defs(tables_defs_admin,"mysql_group_replication_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_GROUP_REPLICATION_HOSTGROUPS); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_group_replication_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_GROUP_REPLICATION_HOSTGROUPS); - insert_into_tables_defs(tables_defs_admin,"mysql_galera_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_GALERA_HOSTGROUPS); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_galera_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_GALERA_HOSTGROUPS); - insert_into_tables_defs(tables_defs_admin,"mysql_aws_aurora_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_AWS_AURORA_HOSTGROUPS); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_aws_aurora_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_AWS_AURORA_HOSTGROUPS); - insert_into_tables_defs(tables_defs_admin,"mysql_hostgroup_attributes", ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_hostgroup_attributes", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_HOSTGROUP_ATTRIBUTES); - insert_into_tables_defs(tables_defs_admin,"mysql_servers_ssl_params", ADMIN_SQLITE_TABLE_MYSQL_SERVERS_SSL_PARAMS); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_servers_ssl_params", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_SERVERS_SSL_PARAMS); - insert_into_tables_defs(tables_defs_admin,"mysql_query_rules", ADMIN_SQLITE_TABLE_MYSQL_QUERY_RULES); - insert_into_tables_defs(tables_defs_admin,"mysql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_MYSQL_QUERY_RULES_FAST_ROUTING); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_query_rules", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_QUERY_RULES); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_QUERY_RULES_FAST_ROUTING); - insert_into_tables_defs(tables_defs_admin,"global_variables", ADMIN_SQLITE_TABLE_GLOBAL_VARIABLES); - insert_into_tables_defs(tables_defs_admin,"runtime_global_variables", ADMIN_SQLITE_RUNTIME_GLOBAL_VARIABLES); - insert_into_tables_defs(tables_defs_admin,"mysql_collations", ADMIN_SQLITE_TABLE_MYSQL_COLLATIONS); - insert_into_tables_defs(tables_defs_admin,"scheduler", ADMIN_SQLITE_TABLE_SCHEDULER); - insert_into_tables_defs(tables_defs_admin,"runtime_scheduler", ADMIN_SQLITE_TABLE_RUNTIME_SCHEDULER); - insert_into_tables_defs(tables_defs_admin,"mysql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_USERS); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_FIREWALL_WHITELIST_USERS); - insert_into_tables_defs(tables_defs_admin,"mysql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_RULES); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_FIREWALL_WHITELIST_RULES); - insert_into_tables_defs(tables_defs_admin,"mysql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); - insert_into_tables_defs(tables_defs_admin,"runtime_mysql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); - insert_into_tables_defs(tables_defs_admin,"restapi_routes", ADMIN_SQLITE_TABLE_RESTAPI_ROUTES); - insert_into_tables_defs(tables_defs_admin,"runtime_restapi_routes", ADMIN_SQLITE_TABLE_RUNTIME_RESTAPI_ROUTES); - insert_into_tables_defs(tables_defs_admin,"coredump_filters", ADMIN_SQLITE_TABLE_COREDUMP_FILTERS); - insert_into_tables_defs(tables_defs_admin,"runtime_coredump_filters", ADMIN_SQLITE_RUNTIME_COREDUMP_FILTERS); -#ifdef DEBUG - insert_into_tables_defs(tables_defs_admin,"debug_levels", ADMIN_SQLITE_TABLE_DEBUG_LEVELS); - insert_into_tables_defs(tables_defs_admin,"debug_filters", ADMIN_SQLITE_TABLE_DEBUG_FILTERS); -#endif /* DEBUG */ -#ifdef PROXYSQLCLICKHOUSE - // ClickHouse - if (GloVars.global.clickhouse_server) { - insert_into_tables_defs(tables_defs_admin,"clickhouse_users", ADMIN_SQLITE_TABLE_CLICKHOUSE_USERS); - insert_into_tables_defs(tables_defs_admin,"runtime_clickhouse_users", ADMIN_SQLITE_TABLE_RUNTIME_CLICKHOUSE_USERS); + pthread_attr_t thread_attr; + size_t tmp_stack_size = 0; + if (!pthread_attr_init(&thread_attr)) { + if (!pthread_attr_getstacksize(&thread_attr, &tmp_stack_size)) { + __sync_fetch_and_add(&GloVars.statuses.stack_memory_admin_threads, tmp_stack_size); + } } -#endif /* PROXYSQLCLICKHOUSE */ - // PgSQL - insert_into_tables_defs(tables_defs_admin, "pgsql_servers", ADMIN_SQLITE_TABLE_PGSQL_SERVERS); - insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_servers", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_SERVERS); - insert_into_tables_defs(tables_defs_admin, "pgsql_users", ADMIN_SQLITE_TABLE_PGSQL_USERS); - insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_users", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_USERS); - insert_into_tables_defs(tables_defs_admin, "pgsql_ldap_mapping", ADMIN_SQLITE_TABLE_PGSQL_LDAP_MAPPING); - insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_ldap_mapping", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_LDAP_MAPPING); - insert_into_tables_defs(tables_defs_admin, "pgsql_query_rules", ADMIN_SQLITE_TABLE_PGSQL_QUERY_RULES); - insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_query_rules", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_QUERY_RULES); - insert_into_tables_defs(tables_defs_admin, "pgsql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_PGSQL_QUERY_RULES_FAST_ROUTING); - insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_QUERY_RULES_FAST_ROUTING); - insert_into_tables_defs(tables_defs_admin, "pgsql_hostgroup_attributes", ADMIN_SQLITE_TABLE_PGSQL_HOSTGROUP_ATTRIBUTES); - insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_hostgroup_attributes", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_HOSTGROUP_ATTRIBUTES); - insert_into_tables_defs(tables_defs_admin, "pgsql_replication_hostgroups", ADMIN_SQLITE_TABLE_PGSQL_REPLICATION_HOSTGROUPS); - insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_replication_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_REPLICATION_HOSTGROUPS); - - insert_into_tables_defs(tables_defs_admin, "pgsql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_USERS); - insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_FIREWALL_WHITELIST_USERS); - insert_into_tables_defs(tables_defs_admin, "pgsql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_RULES); - insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_FIREWALL_WHITELIST_RULES); - insert_into_tables_defs(tables_defs_admin, "pgsql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); - insert_into_tables_defs(tables_defs_admin, "runtime_pgsql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_RUNTIME_PGSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); - - insert_into_tables_defs(tables_defs_config, "pgsql_servers", ADMIN_SQLITE_TABLE_PGSQL_SERVERS); - insert_into_tables_defs(tables_defs_config, "pgsql_users", ADMIN_SQLITE_TABLE_PGSQL_USERS); - insert_into_tables_defs(tables_defs_config, "pgsql_ldap_mapping", ADMIN_SQLITE_TABLE_PGSQL_LDAP_MAPPING); - insert_into_tables_defs(tables_defs_config, "pgsql_query_rules", ADMIN_SQLITE_TABLE_PGSQL_QUERY_RULES); - insert_into_tables_defs(tables_defs_config, "pgsql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_PGSQL_QUERY_RULES_FAST_ROUTING); - insert_into_tables_defs(tables_defs_config, "pgsql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_USERS); - insert_into_tables_defs(tables_defs_config, "pgsql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_RULES); - insert_into_tables_defs(tables_defs_config, "pgsql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_PGSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); - // + arg_proxysql_adm* myarg = (arg_proxysql_adm*)arg; + int client = myarg->client_t; - insert_into_tables_defs(tables_defs_config,"mysql_servers", ADMIN_SQLITE_TABLE_MYSQL_SERVERS); - insert_into_tables_defs(tables_defs_config,"mysql_users", ADMIN_SQLITE_TABLE_MYSQL_USERS); - insert_into_tables_defs(tables_defs_config,"mysql_replication_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_REPLICATION_HOSTGROUPS); - insert_into_tables_defs(tables_defs_config,"mysql_group_replication_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_GROUP_REPLICATION_HOSTGROUPS); - insert_into_tables_defs(tables_defs_config,"mysql_galera_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_GALERA_HOSTGROUPS); - insert_into_tables_defs(tables_defs_config,"mysql_aws_aurora_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_AWS_AURORA_HOSTGROUPS); - insert_into_tables_defs(tables_defs_config,"mysql_hostgroup_attributes", ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES); - insert_into_tables_defs(tables_defs_config,"mysql_servers_ssl_params", ADMIN_SQLITE_TABLE_MYSQL_SERVERS_SSL_PARAMS); - insert_into_tables_defs(tables_defs_config,"mysql_query_rules", ADMIN_SQLITE_TABLE_MYSQL_QUERY_RULES); - insert_into_tables_defs(tables_defs_config,"mysql_query_rules_fast_routing", ADMIN_SQLITE_TABLE_MYSQL_QUERY_RULES_FAST_ROUTING); - insert_into_tables_defs(tables_defs_config,"global_variables", ADMIN_SQLITE_TABLE_GLOBAL_VARIABLES); - insert_into_tables_defs(tables_defs_config,"global_settings", ADMIN_SQLITE_TABLE_GLOBAL_SETTINGS); - // the table is not required to be present on disk. Removing it due to #1055 - insert_into_tables_defs(tables_defs_config,"mysql_collations", ADMIN_SQLITE_TABLE_MYSQL_COLLATIONS); - insert_into_tables_defs(tables_defs_config,"scheduler", ADMIN_SQLITE_TABLE_SCHEDULER); - insert_into_tables_defs(tables_defs_config,"mysql_firewall_whitelist_users", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_USERS); - insert_into_tables_defs(tables_defs_config,"mysql_firewall_whitelist_rules", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_RULES); - insert_into_tables_defs(tables_defs_config,"mysql_firewall_whitelist_sqli_fingerprints", ADMIN_SQLITE_TABLE_MYSQL_FIREWALL_WHITELIST_SQLI_FINGERPRINTS); - insert_into_tables_defs(tables_defs_config, "restapi_routes", ADMIN_SQLITE_TABLE_RESTAPI_ROUTES); -#ifdef DEBUG - insert_into_tables_defs(tables_defs_config,"debug_levels", ADMIN_SQLITE_TABLE_DEBUG_LEVELS); - insert_into_tables_defs(tables_defs_config,"debug_filters", ADMIN_SQLITE_TABLE_DEBUG_FILTERS); -#endif /* DEBUG */ -#ifdef PROXYSQLCLICKHOUSE - // ClickHouse - if (GloVars.global.clickhouse_server) { - insert_into_tables_defs(tables_defs_config,"clickhouse_users", ADMIN_SQLITE_TABLE_CLICKHOUSE_USERS); + //struct sockaddr *addr = arg->addr; + //socklen_t addr_size; + + GloPTH->wrlock(); + { + char* s = GloPTH->get_variable((char*)"server_capabilities"); + mysql_thread___server_capabilities = atoi(s); + free(s); } -#endif /* PROXYSQLCLICKHOUSE */ + GloPTH->wrunlock(); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_query_rules", STATS_SQLITE_TABLE_MYSQL_QUERY_RULES); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_commands_counters", STATS_SQLITE_TABLE_MYSQL_COMMANDS_COUNTERS); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_processlist", STATS_SQLITE_TABLE_MYSQL_PROCESSLIST); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_connection_pool", STATS_SQLITE_TABLE_MYSQL_CONNECTION_POOL); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_connection_pool_reset", STATS_SQLITE_TABLE_MYSQL_CONNECTION_POOL_RESET); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_free_connections", STATS_SQLITE_TABLE_MYSQL_FREE_CONNECTIONS); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_query_digest", STATS_SQLITE_TABLE_MYSQL_QUERY_DIGEST); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_query_digest_reset", STATS_SQLITE_TABLE_MYSQL_QUERY_DIGEST_RESET); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_errors", STATS_SQLITE_TABLE_MYSQL_ERRORS); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_errors_reset", STATS_SQLITE_TABLE_MYSQL_ERRORS_RESET); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_global", STATS_SQLITE_TABLE_MYSQL_GLOBAL); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_gtid_executed", STATS_SQLITE_TABLE_MYSQL_GTID_EXECUTED); - insert_into_tables_defs(tables_defs_stats,"stats_memory_metrics", STATS_SQLITE_TABLE_MEMORY_METRICS); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_users", STATS_SQLITE_TABLE_MYSQL_USERS); - insert_into_tables_defs(tables_defs_stats,"global_variables", ADMIN_SQLITE_TABLE_GLOBAL_VARIABLES); // workaround for issue #708 - insert_into_tables_defs(tables_defs_stats,"stats_mysql_prepared_statements_info", ADMIN_SQLITE_TABLE_STATS_MYSQL_PREPARED_STATEMENTS_INFO); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_client_host_cache", STATS_SQLITE_TABLE_MYSQL_CLIENT_HOST_CACHE); - insert_into_tables_defs(tables_defs_stats,"stats_mysql_client_host_cache_reset", STATS_SQLITE_TABLE_MYSQL_CLIENT_HOST_CACHE_RESET); - - // ProxySQL Cluster - insert_into_tables_defs(tables_defs_admin,"proxysql_servers", ADMIN_SQLITE_TABLE_PROXYSQL_SERVERS); - insert_into_tables_defs(tables_defs_config,"proxysql_servers", ADMIN_SQLITE_TABLE_PROXYSQL_SERVERS); - insert_into_tables_defs(tables_defs_admin,"runtime_proxysql_servers", ADMIN_SQLITE_TABLE_RUNTIME_PROXYSQL_SERVERS); - insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_checksums", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_CHECKSUMS); - insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_metrics", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_METRICS); - insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_status", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_STATUS); - insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_clients_status", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_CLIENTS_STATUS); - insert_into_tables_defs(tables_defs_stats,"stats_proxysql_message_metrics", STATS_SQLITE_TABLE_PROXYSQL_MESSAGE_METRICS); - insert_into_tables_defs(tables_defs_stats,"stats_proxysql_message_metrics_reset", STATS_SQLITE_TABLE_PROXYSQL_MESSAGE_METRICS_RESET); - - // init ldap here - init_ldap(); - - // upgrade mysql_servers if needed (upgrade from previous version) - disk_upgrade_mysql_servers(); - - // upgrade mysql_users if needed (upgrade from previous version) - disk_upgrade_mysql_users(); - - // upgrade mysql_query_rules if needed (upgrade from previous version) - disk_upgrade_mysql_query_rules(); - - // upgrade scheduler if needed (upgrade from previous version) - disk_upgrade_scheduler(); - - // upgrade restapi_routes if needed (upgrade from previous version) - disk_upgrade_rest_api_routes(); + struct pollfd fds[1]; + nfds_t nfds = 1; + int rc; + pthread_mutex_unlock(&sock_mutex); + PgSQL_Thread* pgsql_thr = new PgSQL_Thread(); + pgsql_thr->curtime = monotonic_time(); + GloQPro->init_thread(); + pgsql_thr->refresh_variables(); + PgSQL_Session* sess = pgsql_thr->create_new_session_and_client_data_stream(client); + sess->thread = pgsql_thr; + sess->session_type = PROXYSQL_SESSION_ADMIN; + sess->handler_function=admin_session_handler; + PgSQL_Data_Stream* myds = sess->client_myds; + sess->start_time = pgsql_thr->curtime; - check_and_build_standard_tables(admindb, tables_defs_admin); - check_and_build_standard_tables(configdb, tables_defs_config); - check_and_build_standard_tables(statsdb, tables_defs_stats); + sess->client_myds->client_addrlen = myarg->addr_size; + sess->client_myds->client_addr = myarg->addr; - __attach_db(admindb, configdb, (char *)"disk"); - __attach_db(admindb, statsdb, (char *)"stats"); - __attach_db(admindb, monitordb, (char *)"monitor"); - __attach_db(statsdb, monitordb, (char *)"monitor"); - __attach_db(admindb, statsdb_disk, (char *)"stats_history"); - __attach_db(statsdb, statsdb_disk, (char *)"stats_history"); - dump_mysql_collations(); -#ifdef DEBUG - admindb->execute("ATTACH DATABASE 'file:mem_mydb?mode=memory&cache=shared' AS myhgm"); - admindb->execute("ATTACH DATABASE 'file:mem_monitor_internal_db?mode=memory&cache=shared' AS 'monitor_internal'"); - { - string debugdb_disk_path = string(GloVars.datadir) + "/" + "proxysql_debug.db"; - debugdb_disk = new SQLite3DB(); - debugdb_disk->open((char *)debugdb_disk_path.c_str(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); - debugdb_disk->execute("CREATE TABLE IF NOT EXISTS debug_log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , time INT NOT NULL , lapse INT NOT NULL , thread INT NOT NULL , file VARCHAR NOT NULL , line INT NOT NULL , funct VARCHAR NOT NULL , modnum INT NOT NULL , modname VARCHAR NOT NULL , verbosity INT NOT NULL , message VARCHAR , note VARCHAR , backtrace VARCHAR)"); -/* - // DO NOT CREATE INDEX. - // We can create index on a running instance or an archived DB if needed - debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_time ON debug_log (time)"); - debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_thread ON debug_log (thread)"); - debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_file ON debug_log (file)"); - debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_file_line ON debug_log (file,line)"); - debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_funct ON debug_log (funct)"); - debugdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_debug_log_modnum ON debug_log (modnum)"); -*/ - debugdb_disk->execute("PRAGMA synchronous=0"); - debugdb_disk->execute("PRAGMA journal_mode=OFF"); -/* - // DO NOT ATTACH DATABASE - // it seems sqlite starts randomly failing. For example these 2 TAP tests: - // - admin_show_fields_from-t - // - admin_show_table_status-t - string cmd = "ATTACH DATABASE '" + debugdb_disk_path + "' AS debugdb_disk"; - admindb->execute(cmd.c_str()); -*/ - proxysql_set_admin_debugdb_disk(debugdb_disk); + switch (sess->client_myds->client_addr->sa_family) { + case AF_INET: { + struct sockaddr_in* ipv4 = (struct sockaddr_in*)sess->client_myds->client_addr; + char buf[INET_ADDRSTRLEN]; + inet_ntop(sess->client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); + sess->client_myds->addr.addr = strdup(buf); + sess->client_myds->addr.port = htons(ipv4->sin_port); + break; + } + case AF_INET6: { + struct sockaddr_in6* ipv6 = (struct sockaddr_in6*)sess->client_myds->client_addr; + char buf[INET6_ADDRSTRLEN]; + inet_ntop(sess->client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); + sess->client_myds->addr.addr = strdup(buf); + sess->client_myds->addr.port = htons(ipv6->sin6_port); + break; + } + default: + sess->client_myds->addr.addr = strdup("localhost"); + break; } -#endif /* DEBUG */ - -#ifdef DEBUG - flush_debug_levels_runtime_to_database(configdb, false); - flush_debug_levels_runtime_to_database(admindb, true); -#endif /* DEBUG */ - // Set default values for the module variables in the target 'dbs' - flush_mysql_variables___runtime_to_database(configdb, false, false, false); - flush_mysql_variables___runtime_to_database(admindb, false, true, false); + fds[0].fd = client; + fds[0].revents = 0; + fds[0].events = POLLIN | POLLOUT; + //free(arg->addr); // do not free + free(arg); - flush_admin_variables___runtime_to_database(configdb, false, false, false); - flush_admin_variables___runtime_to_database(admindb, false, true, false); + myds->DSS = STATE_SERVER_HANDSHAKE; + sess->status = CONNECTING_CLIENT; - flush_pgsql_variables___runtime_to_database(configdb, false, false, false); - flush_pgsql_variables___runtime_to_database(admindb, false, true, false); + while (__sync_fetch_and_add(&glovars.shutdown, 0) == 0) { + if (myds->available_data_out()) { + fds[0].events = POLLIN | POLLOUT; + } + else { + fds[0].events = POLLIN; + } + fds[0].revents = 0; + rc = poll(fds, nfds, __sync_fetch_and_add(&__admin_refresh_interval, 0)); + if (rc == -1) { + if (errno == EINTR) { + continue; + } + else { + goto __exit_child_postgres; + } + } + pgsql_thr->curtime = monotonic_time(); + myds->revents = fds[0].revents; + int rb = 0; + rb = myds->read_from_net(); + if (myds->net_failure) goto __exit_child_postgres; + myds->read_pkts(); + if (myds->encrypted == true) { + // PMC-10004 + // we probably should use SSL_pending() and/or SSL_has_pending() to determine + // if there is more data to be read, but it doesn't seem to be working. + // Therefore we try to call read_from_net() again as long as there is data. + // Previously we hardcoded 16KB but it seems that it can return in smaller + // chunks of 4KB. + // We finally removed the chunk size as it seems that any size is possible. + while (rb > 0) { + rb = myds->read_from_net(); + if (myds->net_failure) goto __exit_child_postgres; + myds->read_pkts(); + } + } + sess->to_process = 1; + int rc = sess->handler(); + if (rc == -1) goto __exit_child_postgres; + } - load_or_update_global_settings(configdb); - // Insert or update the configuration from 'disk' - __insert_or_replace_maintable_select_disktable(); + +__exit_child_postgres: + delete pgsql_thr; - // removing this line of code. It seems redundant - //flush_admin_variables___database_to_runtime(admindb,true); + __sync_fetch_and_sub(&GloVars.statuses.stack_memory_admin_threads, tmp_stack_size); - // workaround for issue #708 - statsdb->execute("INSERT OR IGNORE INTO global_variables VALUES('mysql-max_allowed_packet',4194304)"); + return NULL; +} +void* child_telnet(void* arg) +{ + int bytes_read; + char line[LINESIZE+1]; + int client = *(int *)arg; + free(arg); + pthread_mutex_unlock(&sock_mutex); + memset(line,0,LINESIZE+1); + while ((strncmp(line, "quit", 4) != 0) && glovars.shutdown==0) { + bytes_read = recv(client, line, LINESIZE, 0); + if (bytes_read==-1) { + break; + } + char *eow = strchr(line, '\n'); + if (eow) *eow=0; + //SPA->is_command(line); + if (strncmp(line,"shutdown",8)==0) glovars.shutdown=1; + if (send(client, line, strlen(line), MSG_NOSIGNAL)==-1) break; + if (send(client, "\nOK\n", 4, MSG_NOSIGNAL)==-1) break; + } + shutdown(client,SHUT_RDWR); + close(client); + return arg; +} -#ifdef DEBUG - if (GloVars.global.gdbg==false && GloVars.__cmd_proxysql_gdbg) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Enabling GloVars.global.gdbg because GloVars.__cmd_proxysql_gdbg==%d\n", GloVars.__cmd_proxysql_gdbg); - GloVars.global.gdbg=true; +/* +void* child_telnet_also(void* arg) +{ + int bytes_read; + char line[LINESIZE+1]; + int client = *(int *)arg; + free(arg); + pthread_mutex_unlock(&sock_mutex); + memset(line,0,LINESIZE+1); + while ((strncmp(line, "quit", 4) != 0) && glovars.shutdown==0) { + bytes_read = recv(client, line, LINESIZE, 0); + if (bytes_read==-1) { + break; + } + char *eow = strchr(line, '\n'); + if (eow) *eow=0; + if (strncmp(line,"shutdown",8)==0) glovars.shutdown=1; + if (send(client, line, strlen(line), MSG_NOSIGNAL)==-1) break; + if (send(client, "\nNOT OK\n", 8, MSG_NOSIGNAL)==-1) break; } - load_debug_to_runtime(); -#endif /* DEBUG */ + shutdown(client,SHUT_RDWR); + close(client); + return arg; +} +*/ - if (GloVars.__cmd_proxysql_reload || GloVars.__cmd_proxysql_initial || admindb_file_exists==false) { // see #617 - if (GloVars.configfile_open) { - proxysql_config().Read_MySQL_Servers_from_configfile(); - proxysql_config().Read_MySQL_Users_from_configfile(); - proxysql_config().Read_MySQL_Query_Rules_from_configfile(); - proxysql_config().Read_Global_Variables_from_configfile("admin"); - proxysql_config().Read_Global_Variables_from_configfile("mysql"); - - proxysql_config().Read_PgSQL_Servers_from_configfile(); - proxysql_config().Read_PgSQL_Users_from_configfile(); - proxysql_config().Read_PgSQL_Query_Rules_from_configfile(); - proxysql_config().Read_Global_Variables_from_configfile("pgsql"); - - proxysql_config().Read_Scheduler_from_configfile(); - proxysql_config().Read_Restapi_from_configfile(); - proxysql_config().Read_ProxySQL_Servers_from_configfile(); - __insert_or_replace_disktable_select_maintable(); - } - } - - /** - * @brief Inserts a default 'mysql_group_replication_hostgroup'. - * @details Uses the following defaults: - * - writer_hostgroup: 0 - * - reader_hostgroup: 1 - * - backup_writer_hostgroup: 2 - * - offline_hostgroup: 3 - * - max_writers: 9 - * - writer_is_also_reader: 0 -> Keep hostgroups separated - * - max_transactions_behind: 0 - * - * The number of writers in 'multi_primary_mode' wont be restricted, user should tune this value to - * convenience. By default 'max_writers' is set to 9, as is the current member limitation for Group - * Replication. - */ - const char insert_def_gr_hgs[] { - "INSERT INTO mysql_group_replication_hostgroups (" - "writer_hostgroup,backup_writer_hostgroup,reader_hostgroup,offline_hostgroup,active,max_writers," - "writer_is_also_reader" - ") VALUES (0,2,1,3,1,9,0)" - }; - vector servers_info {}; +void * admin_main_loop(void *arg) { + int i; + int rc; + int version=0; + struct pollfd *fds=((struct _main_args *)arg)->fds; + int nfds=((struct _main_args *)arg)->nfds; + int *callback_func=((struct _main_args *)arg)->callback_func; + volatile int *shutdown=((struct _main_args *)arg)->shutdown; + char *socket_names[MAX_ADMIN_LISTENERS]; + for (i=0;iexecute(insert_def_gr_hgs); - } else { - proxy_info("Bootstrap config, found previous user 'mysql_group_replication_hostgroups' config, reusing...\n"); - } - - // Stores current user config for 'mysql_hostgroup_attributes::servers_defaults' - map hgid_defs {}; - // Check if user config is present for 'mysql_hostgroup_attributes' - bool user_gr_hg_attrs_cnf = check_if_user_config(admindb, "SELECT COUNT(*) FROM mysql_hostgroup_attributes"); - int32_t have_ssl = 1; - - // SSL explicitly disabled by user for backend connections - if (GloVars.global.gr_bootstrap_ssl_mode) { - if (strcasecmp(GloVars.global.gr_bootstrap_ssl_mode, "DISABLED") == 0) { - have_ssl = 0; - } - } - - const int64_t DEF_GR_SRV_WEIGHT = 1; - const int64_t DEF_GR_SRV_MAX_CONNS = 512; - const int32_t DEF_GR_SRV_USE_SSL = have_ssl; - - // Update 'mysql_hostgroup_attributes' with sensible defaults for the new discovered instances - if (user_gr_hg_attrs_cnf == false) { - const nlohmann::json j_def_attrs { - { "weight", DEF_GR_SRV_WEIGHT }, - { "max_connections", DEF_GR_SRV_MAX_CONNS }, - { "use_ssl", DEF_GR_SRV_USE_SSL } - }; - const string str_def_attrs { j_def_attrs.dump() }; - const string insert_def_hg_attrs { - "INSERT INTO mysql_hostgroup_attributes (hostgroup_id, servers_defaults) VALUES" - " (0,'"+ str_def_attrs + "'), (1,'" + str_def_attrs + "')" - }; - admindb->execute(insert_def_hg_attrs.c_str()); - } else { - proxy_info("Bootstrap config, found previous user 'mysql_hostgroup_attributes' config, reusing...\n"); - hgid_defs = get_cur_hg_attrs(admindb); - } - - // Define the 'global defaults'. Either pure defaults, or user specified (argument). These values are - // supersede if previous user config is found for 'mysql_hostgroup_attributes::servers_defaults'. - srv_defs_t global_srvs_defs {}; - global_srvs_defs.weight = DEF_GR_SRV_WEIGHT; - global_srvs_defs.max_conns = DEF_GR_SRV_MAX_CONNS; - global_srvs_defs.use_ssl = DEF_GR_SRV_USE_SSL; - - servers_info = extract_boot_servers_info(bootstrap_info.servers); - auto full_srvs_info = build_srvs_info_with_defs(servers_info, hgid_defs, global_srvs_defs); - const string servers_insert { build_boot_servers_insert(full_srvs_info) }; - - admindb->execute("DELETE FROM mysql_servers"); - admindb->execute(servers_insert.c_str()); - - const string users_insert { build_boot_users_insert(bootstrap_info.users) }; - admindb->execute("DELETE FROM mysql_users"); - admindb->execute(users_insert.c_str()); - - // Make the configuration persistent - flush_GENERIC__from_to("mysql_servers", "memory_to_disk"); - flush_mysql_users__from_memory_to_disk(); - } - - // Admin variables 'bootstrap' modifications - if (GloVars.global.gr_bootstrap_mode) { - // TODO-NOTE: This MUST go away; 'admin-hash_passwords' will be deprecated - admindb->execute("UPDATE global_variables SET variable_value='false' WHERE variable_name='admin-hash_passwords'"); - } - flush_admin_variables___database_to_runtime(admindb,true); - - if (GloVars.global.gr_bootstrap_mode) { - flush_admin_variables___runtime_to_database(configdb, false, true, false); - } - - // MySQL variables / MySQL Query Rules 'bootstrap' modifications - if (GloVars.global.gr_bootstrap_mode && !servers_info.empty()) { - const uint64_t base_port { - GloVars.global.gr_bootstrap_conf_base_port == 0 ? 6446 : - GloVars.global.gr_bootstrap_conf_base_port - }; - const string bind_addr { - GloVars.global.gr_bootstrap_conf_bind_address == nullptr ? "0.0.0.0" : - string { GloVars.global.gr_bootstrap_conf_bind_address } - }; - const string s_rw_port { std::to_string(base_port) }; - const string s_ro_port { std::to_string(base_port + 1) }; - const string rw_addr { bind_addr + ":" + s_rw_port }; - const string ro_addr { bind_addr + ":" + s_ro_port }; - const string mysql_interfaces { rw_addr + ";" + ro_addr }; - - // Look for the default collation - const MARIADB_CHARSET_INFO* charset_info = proxysql_find_charset_nr(bootstrap_info.server_language); - const char* server_charset = charset_info == nullptr ? "" : charset_info->csname; - const char* server_collation = charset_info == nullptr ? "" : charset_info->name; - - // Holds user specified values, defaults, and implications of variables over others - const map bootstrap_mysql_vars { - { "mysql-server_version", bootstrap_info.server_version.c_str() }, - { "mysql-default_charset", server_charset }, - { "mysql-default_collation_connection", server_collation }, - { "mysql-interfaces", mysql_interfaces.c_str() }, - { "mysql-monitor_username", bootstrap_info.mon_user.c_str() }, - { "mysql-monitor_password", bootstrap_info.mon_pass.c_str() }, - { "mysql-have_ssl", "true" }, - { "mysql-ssl_p2s_ca", GloVars.global.gr_bootstrap_ssl_ca }, - { "mysql-ssl_p2s_capath", GloVars.global.gr_bootstrap_ssl_capath }, - { "mysql-ssl_p2s_cert", GloVars.global.gr_bootstrap_ssl_cert }, - { "mysql-ssl_p2s_cipher", GloVars.global.gr_bootstrap_ssl_cipher }, - { "mysql-ssl_p2s_crl", GloVars.global.gr_bootstrap_ssl_crl }, - { "mysql-ssl_p2s_crlpath", GloVars.global.gr_bootstrap_ssl_crlpath }, - { "mysql-ssl_p2s_key", GloVars.global.gr_bootstrap_ssl_key } - }; - - for (const pair& p_var_val : bootstrap_mysql_vars) { - if (p_var_val.second != nullptr) { - const string& name { p_var_val.first }; - const string& value { p_var_val.second }; - const string update_mysql_var { - "UPDATE global_variables SET variable_value='" + value + "' WHERE variable_name='" + name + "'" - }; - - admindb->execute(update_mysql_var.c_str()); - } - } - - // MySQL Query Rules - Port based RW split - { - // TODO: This should be able to contain in the future Unix socket based rules - const string insert_rw_split_rules { - "INSERT INTO mysql_query_rules (rule_id,active,proxy_port,destination_hostgroup,apply) VALUES " - " (0,1," + s_rw_port + ",0,1), (1,1," + s_ro_port + ",1,1)" - }; - - // Preserve previous user config targeting hostgroups 0/1 - bool user_qr_cnf = check_if_user_config(admindb, "SELECT COUNT(*) FROM mysql_query_rules"); - if (user_qr_cnf == false) { - admindb->execute(insert_rw_split_rules.c_str()); - } else { - proxy_info("Bootstrap config, found previous user 'mysql_query_rules' config, reusing...\n"); + if(GloVars.global.nostart) { + admin_nostart_=true; + pthread_mutex_lock(&GloVars.global.start_mutex); + } + __sync_fetch_and_add(&admin_load_main_,1); + while (glovars.shutdown==0 && *shutdown==0) + { + //int *client; + //int client_t; + //socklen_t addr_size = sizeof(addr); + pthread_t child; + size_t stacks; + unsigned long long curtime=monotonic_time(); + unsigned long long next_run=GloAdmin->scheduler_run_once(); + unsigned long long poll_wait=500000; + if (next_run < curtime + 500000) { + poll_wait=next_run-curtime; + } + if (poll_wait > 500000) { + poll_wait=500000; + } + poll_wait=poll_wait/1000; // conversion to millisecond + rc=poll(fds,nfds,poll_wait); + if ((admin_nostart_ && __sync_val_compare_and_swap(&GloVars.global.nostart,0,1)==0) || __sync_fetch_and_add(&glovars.shutdown,0)==1) { + admin_nostart_=false; + pthread_mutex_unlock(&GloVars.global.start_mutex); + } + if ((rc == -1 && errno == EINTR) || rc==0) { + // poll() timeout, try again + goto __end_while_pool; + } + for (i=1;iaddr=(struct sockaddr *)malloc(sizeof(custom_sockaddr)); + passarg->addr_size = sizeof(custom_sockaddr); + memset(passarg->addr, 0, sizeof(custom_sockaddr)); + passarg->client_t = accept(fds[i].fd, (struct sockaddr*)passarg->addr, &passarg->addr_size); +// printf("Connected: %s:%d sock=%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), client_t); + pthread_attr_getstacksize (&attr, &stacks); +// printf("Default stack size = %d\n", stacks); + pthread_mutex_lock (&sock_mutex); + //client=(int *)malloc(sizeof(int)); + //*client= client_t; + //if ( pthread_create(&child, &attr, child_func[callback_func[i]], client) != 0 ) { + if ( pthread_create(&child, &attr, child_func[callback_func[i]], passarg) != 0 ) { + // LCOV_EXCL_START + perror("pthread_create"); + proxy_error("Thread creation\n"); + assert(0); + // LCOV_EXCL_STOP + } } - - flush_GENERIC__from_to("mysql_query_rules", "memory_to_disk"); + fds[i].revents=0; } +__end_while_pool: + { + if (GloProxyStats->MySQL_Threads_Handler_timetoget(curtime)) { + if (GloMTH) { + SQLite3_result * resultset=GloMTH->SQL3_GlobalStatus(false); + if (resultset) { + GloProxyStats->MySQL_Threads_Handler_sets(resultset); + delete resultset; + } + } + if (MyHGM) { + SQLite3_result * resultset=MyHGM->SQL3_Get_ConnPool_Stats(); + if (resultset) { + SQLite3_result * resultset2 = NULL; - // Store the 'bootstrap_variables' - if (bootstrap_info.rand_gen_user) { - configdb->execute(ADMIN_SQLITE_TABLE_BOOTSTRAP_VARIABLES); - - const string insert_bootstrap_user { - "INSERT INTO bootstrap_variables (variable_name,variable_value) VALUES" - " ('bootstrap_username','" + string { bootstrap_info.mon_user } + "')" - }; - const string insert_bootstrap_pass { - "INSERT INTO bootstrap_variables (variable_name,variable_value) VALUES" - " ('bootstrap_password','" + string { bootstrap_info.mon_pass } + "')" - }; - - configdb->execute("DELETE FROM bootstrap_variables WHERE variable_name='bootstrap_username'"); - configdb->execute(insert_bootstrap_user.c_str()); - configdb->execute("DELETE FROM bootstrap_variables WHERE variable_name='bootstrap_password'"); - configdb->execute(insert_bootstrap_pass.c_str()); + // In debug, run the code to generate metrics so that it can be tested even if the web interface plugin isn't loaded. + #ifdef DEBUG + if (true) { + #else + if (GloVars.web_interface_plugin) { + #endif + resultset2 = MyHGM->SQL3_Connection_Pool(false); + } + GloProxyStats->MyHGM_Handler_sets(resultset, resultset2); + delete resultset; + if (resultset2) { + delete resultset2; + } + } + } + } + if (GloProxyStats->MySQL_Query_Cache_timetoget(curtime)) { + if (GloQC) { + SQLite3_result * resultset=GloQC->SQL3_getStats(); + if (resultset) { + GloProxyStats->MySQL_Query_Cache_sets(resultset); + delete resultset; + } + } + } + if (GloProxyStats->mysql_query_digest_to_disk_timetoget(curtime)) { + unsigned long long curtime1=monotonic_time(); + int r1 = SPA->FlushDigestTableToDisk(SPA->statsdb_disk); + unsigned long long curtime2=monotonic_time(); + curtime1 = curtime1/1000; + curtime2 = curtime2/1000; + proxy_info("Automatically saved stats_mysql_query_digest to disk: %llums to write %d entries\n", curtime2-curtime1, r1); + } + if (GloProxyStats->system_cpu_timetoget(curtime)) { + GloProxyStats->system_cpu_sets(); + } +#ifndef NOJEM + if (GloProxyStats->system_memory_timetoget(curtime)) { + GloProxyStats->system_memory_sets(); + } +#endif } - } - flush_mysql_variables___database_to_runtime(admindb,true); - if (GloVars.global.gr_bootstrap_mode) { - flush_mysql_variables___runtime_to_database(configdb, false, true, false); - } - flush_pgsql_variables___database_to_runtime(admindb, true); -#ifdef PROXYSQLCLICKHOUSE - flush_clickhouse_variables___database_to_runtime(admindb,true); -#endif /* PROXYSQLCLICKHOUSE */ - flush_sqliteserver_variables___database_to_runtime(admindb,true); - - if (GloVars.__cmd_proxysql_admin_socket) { - set_variable((char *)"mysql_ifaces",GloVars.__cmd_proxysql_admin_socket); - } - - S_amll.update_ifaces(variables.mysql_ifaces, &S_amll.ifaces_mysql); - S_amll.update_ifaces(variables.pgsql_ifaces, &S_amll.ifaces_pgsql); - S_amll.update_ifaces(variables.telnet_admin_ifaces, &S_amll.ifaces_telnet_admin); - S_amll.update_ifaces(variables.telnet_stats_ifaces, &S_amll.ifaces_telnet_stats); - - - -// pthread_t admin_thr; - struct _main_args *arg=(struct _main_args *)malloc(sizeof(struct _main_args)); - arg->nfds=main_poll_nfds; - arg->fds=main_poll_fds; - arg->shutdown=&main_shutdown; - arg->callback_func=main_callback_func; - if (pthread_create(&admin_thr, &attr, admin_main_loop, (void *)arg) !=0 ) { - perror("Thread creation"); - exit(EXIT_FAILURE); - } - do { usleep(50); } while (__sync_fetch_and_sub(&load_main_,0)==0); - load_main_=0; + if (S_amll.get_version()!=version) { + S_amll.wrlock(); + version=S_amll.get_version(); + for (i=1; ipipefd[0]; + fds[nfds].events=POLLIN; + fds[nfds].revents=0; + nfds++; + unsigned int j; + i=0; j=0; + for (j=0; jifaces->len; j++) { + char *add=NULL; char *port=NULL; char *sn=(char *)S_amll.ifaces_mysql->ifaces->index(j); + bool is_ipv6 = false; + char *h = NULL; + if (*sn == '[') { + is_ipv6 = true; + char *p = strchr(sn, ']'); + if (p == NULL) + proxy_error("Invalid IPv6 address: %s\n", sn); - // Register the global prometheus registry in the 'serial_exposer' - if (registered_prometheus_collectable == false) { - this->serial_exposer.RegisterCollectable(GloVars.prometheus_registry); - registered_prometheus_collectable = true; - } + h = ++sn; // remove first '[' + *p = '\0'; + sn = p++; // remove last ']' + add = h; + port = ++p; // remove ':' + } else { + c_split_2(sn, ":" , &add, &port); + } -#ifdef DEBUG - std::cerr << "Admin initialized in "; +#ifdef SO_REUSEPORT + int s = ( atoi(port) ? listen_on_port(add, atoi(port), 128, true) : listen_on_unix(add, 128)); +#else + int s = ( atoi(port) ? listen_on_port(add, atoi(port), 128) : listen_on_unix(add, 128)); #endif -return true; -}; - -#ifdef PROXYSQLCLICKHOUSE -void ProxySQL_Admin::init_clickhouse_variables() { - flush_clickhouse_variables___runtime_to_database(configdb, false, false, false); - flush_clickhouse_variables___runtime_to_database(admindb, false, true, false); - flush_clickhouse_variables___database_to_runtime(admindb,true); -} -#endif /* CLICKHOUSE */ - -void ProxySQL_Admin::init_sqliteserver_variables() { - flush_sqliteserver_variables___runtime_to_database(configdb, false, false, false); - flush_sqliteserver_variables___runtime_to_database(admindb, false, true, false); - flush_sqliteserver_variables___database_to_runtime(admindb,true); -} + //if (s>0) { fds[nfds].fd=s; fds[nfds].events=POLLIN; fds[nfds].revents=0; callback_func[nfds]=0; socket_names[nfds]=strdup(sn); nfds++; } + if (s > 0) { + fds[nfds].fd = s; + fds[nfds].events = POLLIN; + fds[nfds].revents = 0; + callback_func[nfds] = 0; + socket_names[nfds] = strdup(sn); + nfds++; + } + if (is_ipv6 == false) { + if (add) free(add); + if (port) free(port); + } + } -void ProxySQL_Admin::init_ldap_variables() { -/* - if (variables.hash_passwords==true) { - proxy_info("Impossible to set admin-hash_passwords=true when LDAP is enabled. Reverting to false\n"); - variables.hash_passwords=false; - } -*/ - flush_ldap_variables___runtime_to_database(configdb, false, false, false); - flush_ldap_variables___runtime_to_database(admindb, false, true, false); - flush_ldap_variables___database_to_runtime(admindb,true); - admindb->execute((char *)"DETACH DATABASE disk"); - check_and_build_standard_tables(admindb, tables_defs_admin); - check_and_build_standard_tables(configdb, tables_defs_config); - __attach_db(admindb, configdb, (char *)"disk"); - admindb->execute("INSERT OR REPLACE INTO main.mysql_ldap_mapping SELECT * FROM disk.mysql_ldap_mapping"); -} + i = 0; j = 0; + for (; j < S_amll.ifaces_pgsql->ifaces->len; j++) { + char* add = NULL; char* port = NULL; char* sn = (char*)S_amll.ifaces_pgsql->ifaces->index(j); + bool is_ipv6 = false; + char* h = NULL; + if (*sn == '[') { + is_ipv6 = true; + char* p = strchr(sn, ']'); + if (p == NULL) + proxy_error("Invalid IPv6 address: %s\n", sn); -void ProxySQL_Admin::init_pgsql_variables() { - flush_pgsql_variables___runtime_to_database(configdb, false, false, false); - flush_pgsql_variables___runtime_to_database(admindb, false, true, false); - flush_pgsql_variables___database_to_runtime(admindb, true); -} + h = ++sn; // remove first '[' + *p = '\0'; + sn = p++; // remove last ']' + add = h; + port = ++p; // remove ':' + } + else { + c_split_2(sn, ":", &add, &port); + } -void ProxySQL_Admin::admin_shutdown() { - int i; -// do { usleep(50); } while (main_shutdown==0); - if (Admin_HTTP_Server) { - if (variables.web_enabled) { - MHD_stop_daemon(Admin_HTTP_Server); - Admin_HTTP_Server = NULL; - } - } - delete AdminHTTPServer; - if (AdminRestApiServer) { - delete AdminRestApiServer; - AdminRestApiServer = NULL; - } - AdminHTTPServer = NULL; - pthread_join(admin_thr, NULL); - delete admindb; - delete statsdb; - delete configdb; - delete monitordb; - delete statsdb_disk; -#ifdef DEBUG - proxysql_set_admin_debugdb_disk(NULL); - delete debugdb_disk; +#ifdef SO_REUSEPORT + int s = (atoi(port) ? listen_on_port(add, atoi(port), 128, true) : listen_on_unix(add, 128)); +#else + int s = (atoi(port) ? listen_on_port(add, atoi(port), 128) : listen_on_unix(add, 128)); #endif - (*proxy_sqlite3_shutdown)(); - if (main_poll_fds) { - for (i=0;i0) { fds[nfds].fd=s; fds[nfds].events=POLLIN; fds[nfds].revents=0; callback_func[nfds]=0; socket_names[nfds]=strdup(sn); nfds++; } + if (s > 0) { + fds[nfds].fd = s; + fds[nfds].events = POLLIN; + fds[nfds].revents = 0; + callback_func[nfds] = 2; + socket_names[nfds] = strdup(sn); + nfds++; + } + if (is_ipv6 == false) { + if (add) free(add); + if (port) free(port); + } + } + S_amll.wrunlock(); } - free(main_poll_fds); - } - if (main_callback_func) { - free(main_callback_func); - } - drop_tables_defs(tables_defs_admin); - delete tables_defs_admin; - drop_tables_defs(tables_defs_stats); - delete tables_defs_stats; - drop_tables_defs(tables_defs_config); - delete tables_defs_config; - shutdown(pipefd[0],SHUT_RDWR); - shutdown(pipefd[1],SHUT_RDWR); - close(pipefd[0]); - close(pipefd[1]); - // delete the scheduler - delete scheduler; - scheduler=NULL; - if (variables.cluster_username) { - free(variables.cluster_username); - } - if (variables.cluster_password) { - free(variables.cluster_password); } - if (variables.mysql_ifaces) { - free(variables.mysql_ifaces); + //if (__sync_add_and_fetch(shutdown,0)==0) __sync_add_and_fetch(shutdown,1); + for (i=0; ip_update_metrics(); } - if (variables.admin_credentials) { - free(variables.admin_credentials); + // Update pgsql_threads_handler metrics + if (GloPTH) { + GloPTH->p_update_metrics(); } - if (variables.stats_credentials) { - free(variables.stats_credentials); + // Update mysql_hostgroups_manager metrics + if (MyHGM) { + MyHGM->p_update_metrics(); } - if (variables.telnet_admin_ifaces) { - free(variables.telnet_admin_ifaces); + // Update monitor metrics + if (GloMyMon) { + GloMyMon->p_update_metrics(); } - if (variables.telnet_stats_ifaces) { - free(variables.telnet_stats_ifaces); + // Update query_cache metrics + if (GloQC) { + GloQC->p_update_metrics(); } - if (variables.ssl_keylog_file) { - free(variables.ssl_keylog_file); + // Update cluster metrics + if (GloProxyCluster) { + GloProxyCluster->p_update_metrics(); } -}; - -ProxySQL_Admin::~ProxySQL_Admin() { - admin_shutdown(); - delete (RE2 *)match_regexes.re[0]; - delete (RE2 *)match_regexes.re[1]; - delete (RE2 *)match_regexes.re[2]; - delete (RE2 *)match_regexes.re[3]; - free(match_regexes.re); - delete (re2::RE2::Options *)match_regexes.opt; - map_test_mysql_firewall_whitelist_rules_cleanup(); -}; - -// This function is used only used to export what collations are available -// it is mostly informative -void ProxySQL_Admin::dump_mysql_collations() { - const MARIADB_CHARSET_INFO * c = mariadb_compiled_charsets; - char buf[1024]; - char *query=(char *)"INSERT INTO mysql_collations VALUES (%d, \"%s\", \"%s\", \"\")"; - admindb->execute("DELETE FROM mysql_collations"); - do { - sprintf(buf,query,c->nr, c->name, c->csname); - admindb->execute(buf); - ++c; - } while (c[0].nr != 0); - admindb->execute("INSERT OR REPLACE INTO mysql_collations SELECT Id, Collation, Charset, 'Yes' FROM mysql_collations JOIN (SELECT MIN(Id) minid FROM mysql_collations GROUP BY Charset) t ON t.minid=mysql_collations.Id"); - // the table is not required to be present on disk. Removing it due to #1055 -// admindb->execute("DELETE FROM disk.mysql_collations"); -// admindb->execute("INSERT INTO disk.mysql_collations SELECT * FROM main.mysql_collations"); + // Update admin metrics + GloAdmin->p_update_metrics(); } -void ProxySQL_Admin::check_and_build_standard_tables(SQLite3DB *db, std::vector *tables_defs) { -// int i; - table_def_t *td; - db->execute("PRAGMA foreign_keys = OFF"); - for (std::vector::iterator it=tables_defs->begin(); it!=tables_defs->end(); ++it) { - td=*it; - db->check_and_build_table(td->table_name, td->table_def); - } - db->execute("PRAGMA foreign_keys = ON"); -}; - - - -void ProxySQL_Admin::insert_into_tables_defs(std::vector *tables_defs, const char *table_name, const char *table_def) { - table_def_t *td = new table_def_t; - td->table_name=strdup(table_name); - td->table_def=strdup(table_def); - tables_defs->push_back(td); -}; +ProxySQL_Admin::ProxySQL_Admin() : + serial_exposer(std::function { update_modules_metrics }) +{ +#ifdef DEBUG + debugdb_disk = NULL; + if (glovars.has_debug==false) { +#else + if (glovars.has_debug==true) { +#endif /* DEBUG */ + perror("Incompatible debugging version"); + exit(EXIT_FAILURE); + } -void ProxySQL_Admin::drop_tables_defs(std::vector *tables_defs) { - table_def_t *td; - while (!tables_defs->empty()) { - td=tables_defs->back(); - free(td->table_name); - td->table_name=NULL; - free(td->table_def); - td->table_def=NULL; - tables_defs->pop_back(); - delete td; + if (proxysql_version == NULL) { + proxysql_version = strdup(PROXYSQL_VERSION); } -}; + SPA=this; -std::map request_headers(const httpserver::http_request& request) { - auto req_headers = request.get_headers(); - std::map result {}; + //Initialize locker +#ifdef PA_PTHREAD_MUTEX + pthread_rwlock_init(&rwlock,NULL); +#else + spinlock_rwlock_init(&rwlock); +#endif - for (const auto& header : req_headers) { - result.insert({header.first, header.second}); - } +#ifdef PA_PTHREAD_MUTEX + pthread_mutex_init(&mysql_servers_lock, NULL); +#else + spinlock_rwlock_init(&mysql_servers_rwlock); +#endif - return result; -} +#ifdef PA_PTHREAD_MUTEX + pthread_mutex_init(&pgsql_servers_lock, NULL); +#else + spinlock_rwlock_init(&pgsql_servers_rwlock); +#endif -std::shared_ptr make_response( - const std::pair, std::string>& res_data -) { - std::shared_ptr response = - std::make_shared(httpserver::string_response(res_data.second)); + pthread_mutex_init(&sql_query_global_mutex, NULL); - for (const auto& h_key_val : res_data.first) { - response->with_header(h_key_val.first, h_key_val.second); - } + generate_load_save_disk_commands("mysql_firewall", "MYSQL FIREWALL"); + generate_load_save_disk_commands("mysql_query_rules", "MYSQL QUERY RULES"); + generate_load_save_disk_commands("mysql_users", "MYSQL USERS"); + generate_load_save_disk_commands("mysql_servers", "MYSQL SERVERS"); + generate_load_save_disk_commands("mysql_variables", "MYSQL VARIABLES"); + generate_load_save_disk_commands("pgsql_firewall", "PGSQL FIREWALL"); + generate_load_save_disk_commands("pgsql_query_rules", "PGSQL QUERY RULES"); + generate_load_save_disk_commands("pgsql_users", "PGSQL USERS"); + generate_load_save_disk_commands("pgsql_servers", "PGSQL SERVERS"); + generate_load_save_disk_commands("pgsql_variables", "PGSQL VARIABLES"); + generate_load_save_disk_commands("scheduler", "SCHEDULER"); + generate_load_save_disk_commands("restapi", "RESTAPI"); + generate_load_save_disk_commands("proxysql_servers", "PROXYSQL SERVERS"); - return response; -} + { + // we perform some sanity check + assert(load_save_disk_commands.size() > 0); + for (auto it = load_save_disk_commands.begin(); it != load_save_disk_commands.end(); it++) { + vector& vec1 = get<1>(it->second); + assert(vec1.size() == 3); + vector& vec2 = get<2>(it->second); + assert(vec2.size() == 3); + } + } -/** - * @brief Checks if the supplied port is available. - * - * @param port_num The port number to check. - * @param free Output parameter. True if the port is free, false otherwise. - * - * @return Returns: - * - '-1' in case 'SO_REUSEADDR' fails to be set for the check. - * - '-2' in case of invalid arguments supplied. - * - '0' otherwise. - */ -int check_port_availability(int port_num, bool* port_free) { - int ecode = 0; - int sfd = 0; - int reuseaddr = 1; - struct sockaddr_in tmp_addr; - if (port_num == 0 || port_free == nullptr) { - return -2; + variables.admin_credentials=strdup("admin:admin"); + variables.stats_credentials=strdup("stats:stats"); + if (GloVars.__cmd_proxysql_admin_socket) { + variables.mysql_ifaces=strdup(GloVars.__cmd_proxysql_admin_socket); + } else { + variables.mysql_ifaces=strdup("0.0.0.0:6032"); // changed. See isseu #1103 } + variables.pgsql_ifaces= strdup("0.0.0.0:6132"); + variables.telnet_admin_ifaces=NULL; + variables.telnet_stats_ifaces=NULL; + variables.refresh_interval=2000; + variables.mysql_show_processlist_extended = false; + variables.pgsql_show_processlist_extended = false; + //variables.hash_passwords=true; // issue #676 + variables.vacuum_stats=true; // issue #1011 + variables.admin_read_only=false; // by default, the admin interface accepts writes + variables.admin_version=(char *)PROXYSQL_VERSION; + variables.cluster_username=strdup((char *)""); + variables.cluster_password=strdup((char *)""); + variables.cluster_check_interval_ms=1000; + variables.cluster_check_status_frequency=10; + variables.cluster_mysql_query_rules_diffs_before_sync = 3; + variables.cluster_mysql_servers_diffs_before_sync = 3; + variables.cluster_mysql_users_diffs_before_sync = 3; + variables.cluster_proxysql_servers_diffs_before_sync = 3; + variables.cluster_mysql_variables_diffs_before_sync = 3; + variables.cluster_admin_variables_diffs_before_sync = 3; + variables.cluster_ldap_variables_diffs_before_sync = 3; + variables.cluster_mysql_servers_sync_algorithm = 1; + checksum_variables.checksum_mysql_query_rules = true; + checksum_variables.checksum_mysql_servers = true; + checksum_variables.checksum_mysql_users = true; + checksum_variables.checksum_mysql_variables = true; + checksum_variables.checksum_admin_variables = true; + checksum_variables.checksum_ldap_variables = true; + variables.cluster_mysql_query_rules_save_to_disk = true; + variables.cluster_mysql_servers_save_to_disk = true; + variables.cluster_mysql_users_save_to_disk = true; + variables.cluster_proxysql_servers_save_to_disk = true; + variables.cluster_mysql_variables_save_to_disk = true; + variables.cluster_admin_variables_save_to_disk = true; + variables.cluster_ldap_variables_save_to_disk = true; + variables.stats_mysql_connection_pool = 60; + variables.stats_mysql_connections = 60; + variables.stats_mysql_query_cache = 60; + variables.stats_mysql_query_digest_to_disk = 0; + variables.stats_system_cpu = 60; + variables.stats_system_memory = 60; + GloProxyStats->variables.stats_mysql_connection_pool = 60; + GloProxyStats->variables.stats_mysql_connections = 60; + GloProxyStats->variables.stats_mysql_query_cache = 60; + GloProxyStats->variables.stats_mysql_query_digest_to_disk = 0; + GloProxyStats->variables.stats_system_cpu = 60; +#ifndef NOJEM + GloProxyStats->variables.stats_system_memory = 60; +#endif - // set 'port_free' to false by default - *port_free = false; + variables.restapi_enabled = false; + variables.restapi_enabled_old = false; + variables.restapi_port = 6070; + variables.restapi_port_old = variables.restapi_port; + variables.web_enabled = false; + variables.web_enabled_old = false; + variables.web_port = 6080; + variables.web_port_old = variables.web_port; + variables.web_verbosity = 0; + variables.p_memory_metrics_interval = 61; +#ifdef DEBUG + variables.debug=GloVars.global.gdbg; + all_modules_started = false; + debug_output = 1; + proxysql_set_admin_debug_output(debug_output); +#endif /* DEBUG */ + variables.coredump_generation_interval_ms = 30000; + variables.coredump_generation_threshold = 10; + variables.ssl_keylog_file = strdup(""); + last_p_memory_metrics_ts = 0; + // create the scheduler + scheduler=new ProxySQL_External_Scheduler(); - sfd = socket(AF_INET, SOCK_STREAM, 0); - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.sin_family = AF_INET; - tmp_addr.sin_port = htons(port_num); - tmp_addr.sin_addr.s_addr = INADDR_ANY; + match_regexes.opt=(re2::RE2::Options *)new re2::RE2::Options(RE2::Quiet); + re2::RE2::Options *opt2=(re2::RE2::Options *)match_regexes.opt; + opt2->set_case_sensitive(false); + match_regexes.re=(void **)malloc(sizeof(void *)*10); + match_regexes.re[0]=(RE2 *)new RE2("^SELECT\\s+@@max_allowed_packet\\s*", *opt2); + match_regexes.re[1]=(RE2 *)new RE2("^SELECT\\s+@@[0-9A-Za-z_-]+\\s*", *opt2); + match_regexes.re[2]=(RE2 *)new RE2("SHOW\\s+VARIABLES\\s+WHERE", *opt2); + match_regexes.re[3]=(RE2 *)new RE2("SHOW\\s+VARIABLES\\s+LIKE", *opt2); - if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddr, sizeof(reuseaddr)) == -1) { - close(sfd); - ecode = -1; - } else { - if (::bind(sfd, (struct sockaddr*)&tmp_addr, sizeof(tmp_addr)) == -1) { - close(sfd); - } else { - *port_free = true; - close(sfd); - } - } + // Default initialize prometheus collectable flag + registered_prometheus_collectable = false; - return ecode; -} + // Initialize prometheus metrics + init_prometheus_counter_array(admin_metrics_map, this->metrics.p_counter_array); + init_prometheus_gauge_array(admin_metrics_map, this->metrics.p_gauge_array); + init_prometheus_dyn_gauge_array(admin_metrics_map, this->metrics.p_dyn_gauge_array); -void ProxySQL_Admin::load_or_update_global_settings(SQLite3DB *db) { - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT variable_name, variable_value FROM global_settings ORDER BY variable_name"; - db->execute_statement(q, &error , &cols , &affected_rows , &resultset); - if (error) { - proxy_error("Error on %s : %s\n", q, error); - } else { - // note: we don't lock, this is done only during bootstrap - { - char *uuid = NULL; - bool write_uuid = true; - // search for uuid - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - if (strcasecmp(r->fields[0],"uuid")==0) { - uuid = strdup(r->fields[1]); - uuid_t uu; - if (uuid) { - if (uuid_parse(uuid,uu)==0) { - // we successful read an UUID - } else { - proxy_error("Ignoring invalid UUID format in global_settings: %s\n", uuid); - free(uuid); - uuid = NULL; - } - } - } - } - if (uuid) { // we found an UUID in the DB - if (GloVars.uuid) { // an UUID is already defined - if (strcmp(uuid, GloVars.uuid)==0) { // the match - proxy_info("Using UUID: %s\n", uuid); - write_uuid = false; - } else { - // they do not match. The one on DB will be replaced - proxy_info("Using UUID: %s . Replacing UUID from database: %s\n", GloVars.uuid, uuid); - } - } else { - // the UUID already defined, so the one in the DB will be used - proxy_info("Using UUID from database: %s\n", uuid); - GloVars.uuid=strdup(uuid); - } - } else { - if (GloVars.uuid) { - // we will write the UUID in the DB - proxy_info("Using UUID: %s . Writing it to database\n", GloVars.uuid); - } else { - // UUID not defined anywhere, we will create a new one - uuid_t uu; - uuid_generate(uu); - char buf[40]; - uuid_unparse(uu, buf); - GloVars.uuid=strdup(buf); - proxy_info("Using UUID: %s , randomly generated. Writing it to database\n", GloVars.uuid); - } - } - if (write_uuid) { - std::string s = "INSERT OR REPLACE INTO global_settings VALUES (\"uuid\", \""; - s += GloVars.uuid; - s += "\")"; - db->execute(s.c_str()); - } - if (uuid) { - free(uuid); - uuid=NULL; - } - } + // NOTE: Imposing fixed value to 'version_info' matching 'mysqld_exporter' + this->metrics.p_gauge_array[p_admin_gauge::version_info]->Set(1); +}; - if (resultset) { - delete resultset; - } - } -} +void ProxySQL_Admin::wrlock() { +#ifdef PA_PTHREAD_MUTEX + pthread_rwlock_wrlock(&rwlock); +#else + spin_wrlock(&rwlock); +#endif +}; -void ProxySQL_Admin::load_restapi_server() { - if (!all_modules_started) { return; } +void ProxySQL_Admin::wrunlock() { +#ifdef PA_PTHREAD_MUTEX + pthread_rwlock_unlock(&rwlock); +#else + spin_wrunlock(&rwlock); +#endif +}; - std::function(const httpserver::http_request&)> prometheus_callback { - [this](const httpserver::http_request& request) { - auto headers = request_headers(request); - auto serial_response = this->serial_exposer(headers); - auto http_response = make_response(serial_response); +void ProxySQL_Admin::mysql_servers_wrlock() { + #ifdef PA_PTHREAD_MUTEX + pthread_mutex_lock(&mysql_servers_lock); + #else + spin_wrlock(&mysql_servers_rwlock); + #endif +}; - return http_response; - } - }; +void ProxySQL_Admin::mysql_servers_wrunlock() { + #ifdef PA_PTHREAD_MUTEX + pthread_mutex_unlock(&mysql_servers_lock); + #else + spin_wrunlock(&mysql_servers_rwlock); + #endif +}; - bool free_restapi_port = false; +void ProxySQL_Admin::pgsql_servers_wrlock() { +#ifdef PA_PTHREAD_MUTEX + pthread_mutex_lock(&pgsql_servers_lock); +#else + spin_wrlock(&pgsql_servers_rwlock); +#endif +}; - // Helper lambda taking a boolean reference as a parameter to check if 'restapi_port' is available. - // In case of port not being free or error, logs an error 'ProxySQL_RestAPI_Server' isn't able to be started. - const auto check_restapi_port = [&](bool& restapi_port_free) -> void { - int e_port_check = check_port_availability(variables.restapi_port, &restapi_port_free); +void ProxySQL_Admin::pgsql_servers_wrunlock() { +#ifdef PA_PTHREAD_MUTEX + pthread_mutex_unlock(&pgsql_servers_lock); +#else + spin_wrunlock(&pgsql_servers_rwlock); +#endif +}; - if (restapi_port_free == false) { - if (e_port_check == -1) { - proxy_error("Unable to start 'ProxySQL_RestAPI_Server', failed to set 'SO_REUSEADDR' to check port availability.\n"); - } else { - proxy_error( - "Unable to start 'ProxySQL_RestAPI_Server', port '%d' already in use.\n", - variables.restapi_port - ); - } - } - }; +void ProxySQL_Admin::print_version() { + fprintf(stderr,"Standard ProxySQL Admin rev. %s -- %s -- %s\n", PROXYSQL_ADMIN_VERSION, __FILE__, __TIMESTAMP__); +}; - if (variables.restapi_enabled != variables.restapi_enabled_old) { - if (variables.restapi_enabled) { - check_restapi_port(free_restapi_port); - } +void ProxySQL_Admin::init_ldap() { + if (GloMyLdapAuth) { + insert_into_tables_defs(tables_defs_admin,"mysql_ldap_mapping", ADMIN_SQLITE_TABLE_MYSQL_LDAP_MAPPING); + insert_into_tables_defs(tables_defs_admin,"runtime_mysql_ldap_mapping", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_LDAP_MAPPING); + insert_into_tables_defs(tables_defs_config,"mysql_ldap_mapping", ADMIN_SQLITE_TABLE_MYSQL_LDAP_MAPPING); + } +} - if (variables.restapi_enabled && free_restapi_port) { - AdminRestApiServer = new ProxySQL_RESTAPI_Server( - variables.restapi_port, {{"/metrics", prometheus_callback}} - ); - } else { - delete AdminRestApiServer; - AdminRestApiServer = NULL; - } - variables.restapi_enabled_old = variables.restapi_enabled; - } else { - if (variables.restapi_port != variables.restapi_port_old) { - if (AdminRestApiServer) { - delete AdminRestApiServer; - AdminRestApiServer = NULL; - } +void ProxySQL_Admin::init_http_server() { + AdminHTTPServer = new ProxySQL_HTTP_Server(); + AdminHTTPServer->init(); + AdminHTTPServer->print_version(); +} - if (variables.restapi_enabled) { - check_restapi_port(free_restapi_port); - } +#ifdef PROXYSQLCLICKHOUSE +void ProxySQL_Admin::init_clickhouse_variables() { + flush_clickhouse_variables___runtime_to_database(configdb, false, false, false); + flush_clickhouse_variables___runtime_to_database(admindb, false, true, false); + flush_clickhouse_variables___database_to_runtime(admindb,true); +} +#endif /* CLICKHOUSE */ - if (variables.restapi_enabled && free_restapi_port) { - AdminRestApiServer = new ProxySQL_RESTAPI_Server( - variables.restapi_port, {{"/metrics", prometheus_callback}} - ); - } - variables.restapi_port_old = variables.restapi_port; - } +void ProxySQL_Admin::init_sqliteserver_variables() { + flush_sqliteserver_variables___runtime_to_database(configdb, false, false, false); + flush_sqliteserver_variables___runtime_to_database(admindb, false, true, false); + flush_sqliteserver_variables___database_to_runtime(admindb,true); +} + +void ProxySQL_Admin::init_ldap_variables() { +/* + if (variables.hash_passwords==true) { + proxy_info("Impossible to set admin-hash_passwords=true when LDAP is enabled. Reverting to false\n"); + variables.hash_passwords=false; } +*/ + flush_ldap_variables___runtime_to_database(configdb, false, false, false); + flush_ldap_variables___runtime_to_database(admindb, false, true, false); + flush_ldap_variables___database_to_runtime(admindb,true); + admindb->execute((char *)"DETACH DATABASE disk"); + check_and_build_standard_tables(admindb, tables_defs_admin); + check_and_build_standard_tables(configdb, tables_defs_config); + __attach_db(admindb, configdb, (char *)"disk"); + admindb->execute("INSERT OR REPLACE INTO main.mysql_ldap_mapping SELECT * FROM disk.mysql_ldap_mapping"); } -void ProxySQL_Admin::load_http_server() { - if (!all_modules_started) { return; } +void ProxySQL_Admin::init_pgsql_variables() { + flush_pgsql_variables___runtime_to_database(configdb, false, false, false); + flush_pgsql_variables___runtime_to_database(admindb, false, true, false); + flush_pgsql_variables___database_to_runtime(admindb, true); +} - if (variables.web_enabled != variables.web_enabled_old) { +void ProxySQL_Admin::admin_shutdown() { + int i; +// do { usleep(50); } while (main_shutdown==0); + if (Admin_HTTP_Server) { if (variables.web_enabled) { - if (GloVars.web_interface_plugin == NULL) { - char *key_pem; - char *cert_pem; - GloVars.get_SSL_pem_mem(&key_pem, &cert_pem); - Admin_HTTP_Server = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_SSL, - variables.web_port, - NULL, NULL, http_handler, NULL, - MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, MHD_OPTION_STRICT_FOR_CLIENT, (int) 1, - MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 4, - MHD_OPTION_NONCE_NC_SIZE, (unsigned int) 300, - MHD_OPTION_HTTPS_MEM_KEY, key_pem, - MHD_OPTION_HTTPS_MEM_CERT, cert_pem, - MHD_OPTION_END); - free(key_pem); - free(cert_pem); - } else { - if (GloWebInterface) { - int sfd = 0; - int reuseaddr = 1; - struct sockaddr_in tmp_addr; - - sfd = socket(AF_INET, SOCK_STREAM, 0); - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.sin_family = AF_INET; - tmp_addr.sin_port = htons(variables.web_port); - tmp_addr.sin_addr.s_addr = INADDR_ANY; - - if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddr, sizeof(reuseaddr)) == -1) { - close(sfd); - proxy_error( - "Unable to start WebInterfacePlugin, failed to set 'SO_REUSEADDR' to check port '%d' availability.\n", - variables.web_port - ); - } else { - if (::bind(sfd, (struct sockaddr*)&tmp_addr, (socklen_t)sizeof(tmp_addr)) == -1) { - close(sfd); - proxy_error( - "Unable to start WebInterfacePlugin, port '%d' already in use.\n", - variables.web_port - ); - } else { - close(sfd); - GloWebInterface->start(variables.web_port); - } - } - } - } - } else { - if (GloVars.web_interface_plugin == NULL) { - MHD_stop_daemon(Admin_HTTP_Server); - Admin_HTTP_Server = NULL; - } else { - if (GloWebInterface) { - GloWebInterface->stop(); - } - } + MHD_stop_daemon(Admin_HTTP_Server); + Admin_HTTP_Server = NULL; } - variables.web_enabled_old = variables.web_enabled; - } else { - if (variables.web_port != variables.web_port_old) { - if (variables.web_enabled) { - if (GloVars.web_interface_plugin == NULL) { - MHD_stop_daemon(Admin_HTTP_Server); - Admin_HTTP_Server = NULL; - char *key_pem; - char *cert_pem; - GloVars.get_SSL_pem_mem(&key_pem, &cert_pem); - Admin_HTTP_Server = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_SSL, - variables.web_port, - NULL, NULL, http_handler, NULL, - MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, MHD_OPTION_STRICT_FOR_CLIENT, (int) 1, - MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 4, - MHD_OPTION_NONCE_NC_SIZE, (unsigned int) 300, - MHD_OPTION_HTTPS_MEM_KEY, key_pem, - MHD_OPTION_HTTPS_MEM_CERT, cert_pem, - MHD_OPTION_END); - free(key_pem); - free(cert_pem); - } else { - if (GloWebInterface) { - GloWebInterface->start(variables.web_port); - } - } - } - variables.web_port_old = variables.web_port; + } + delete AdminHTTPServer; + if (AdminRestApiServer) { + delete AdminRestApiServer; + AdminRestApiServer = NULL; + } + AdminHTTPServer = NULL; + pthread_join(admin_thr, NULL); + delete admindb; + delete statsdb; + delete configdb; + delete monitordb; + delete statsdb_disk; +#ifdef DEBUG + proxysql_set_admin_debugdb_disk(NULL); + delete debugdb_disk; +#endif + (*proxy_sqlite3_shutdown)(); + if (main_poll_fds) { + for (i=0;iexecute_statement(q.c_str(), &error , &cols , &affected_rows , &resultset); - if (error) { - proxy_error("Error on %s : %s\n", q.c_str(), error); - free(error); - return false; + // delete the scheduler + delete scheduler; + scheduler=NULL; + if (variables.cluster_username) { + free(variables.cluster_username); + } + if (variables.cluster_password) { + free(variables.cluster_password); + } + if (variables.mysql_ifaces) { + free(variables.mysql_ifaces); + } + if (variables.pgsql_ifaces) { + free(variables.pgsql_ifaces); + } + if (variables.admin_credentials) { + free(variables.admin_credentials); + } + if (variables.stats_credentials) { + free(variables.stats_credentials); + } + if (variables.telnet_admin_ifaces) { + free(variables.telnet_admin_ifaces); + } + if (variables.telnet_stats_ifaces) { + free(variables.telnet_stats_ifaces); + } + if (variables.ssl_keylog_file) { + free(variables.ssl_keylog_file); } - return true; +}; + +ProxySQL_Admin::~ProxySQL_Admin() { + admin_shutdown(); + delete (RE2 *)match_regexes.re[0]; + delete (RE2 *)match_regexes.re[1]; + delete (RE2 *)match_regexes.re[2]; + delete (RE2 *)match_regexes.re[3]; + free(match_regexes.re); + delete (re2::RE2::Options *)match_regexes.opt; + + map_test_mysql_firewall_whitelist_rules_cleanup(); +}; + +// This function is used only used to export what collations are available +// it is mostly informative +void ProxySQL_Admin::dump_mysql_collations() { + const MARIADB_CHARSET_INFO * c = mariadb_compiled_charsets; + char buf[1024]; + char *query=(char *)"INSERT INTO mysql_collations VALUES (%d, \"%s\", \"%s\", \"\")"; + admindb->execute("DELETE FROM mysql_collations"); + do { + sprintf(buf,query,c->nr, c->name, c->csname); + admindb->execute(buf); + ++c; + } while (c[0].nr != 0); + admindb->execute("INSERT OR REPLACE INTO mysql_collations SELECT Id, Collation, Charset, 'Yes' FROM mysql_collations JOIN (SELECT MIN(Id) minid FROM mysql_collations GROUP BY Charset) t ON t.minid=mysql_collations.Id"); + // the table is not required to be present on disk. Removing it due to #1055 +// admindb->execute("DELETE FROM disk.mysql_collations"); +// admindb->execute("INSERT INTO disk.mysql_collations SELECT * FROM main.mysql_collations"); } -void ProxySQL_Admin::flush_GENERIC_variables__process__database_to_runtime( - const string& modname, SQLite3DB *db, SQLite3_result* resultset, - const bool& lock, const bool& replace, - const std::unordered_set& variables_read_only, - const std::unordered_set& variables_to_delete_silently, - const std::unordered_set& variables_deprecated, - const std::unordered_set& variables_special_values, - std::function special_variable_action -) { - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - bool rc = false; - if (modname == "admin") { - rc = set_variable(r->fields[0],r->fields[1], lock); - } else if (modname == "mysql") { - rc = GloMTH->set_variable(r->fields[0],r->fields[1]); - } else if (modname == "sqliteserver") { - rc = GloSQLite3Server->set_variable(r->fields[0],r->fields[1]); -#ifdef PROXYSQLCLICKHOUSE - } else if (modname == "clickhouse") { - rc = GloClickHouseServer->set_variable(r->fields[0],r->fields[1]); -#endif // PROXYSQLCLICKHOUSE - } else if (modname == "ldap") { - rc = GloMyLdapAuth->set_variable(r->fields[0],r->fields[1]); - } - const string v = string(r->fields[0]); - if (rc==false) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); - if (replace) { - char *val = NULL; - if (modname == "admin") { - val = get_variable(r->fields[0]); - } else if (modname == "mysql") { - val = GloMTH->get_variable(r->fields[0]); - } else if (modname == "sqliteserver") { - val = GloSQLite3Server->get_variable(r->fields[0]); -#ifdef PROXYSQLCLICKHOUSE - } else if (modname == "clickhouse") { - val = GloClickHouseServer->get_variable(r->fields[0]); -#endif // PROXYSQLCLICKHOUSE - } else if (modname == "ldap") { - val = GloMyLdapAuth->get_variable(r->fields[0]); - } - char q[1000]; - if (val) { - if (variables_read_only.count(v) > 0) { - proxy_warning("Impossible to set read-only variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],r->fields[1], val); - } else { - proxy_warning("Impossible to set variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],r->fields[1], val); - } - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"%s-%s\",\"%s\")", modname.c_str(), r->fields[0],val); - db->execute(q); - free(val); - } else { - if (variables_to_delete_silently.count(v) > 0) { - sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"%s-%s\"", modname.c_str(), r->fields[0]); - db->execute(q); - } else if (variables_deprecated.count(v) > 0) { - proxy_error("Global variable %s-%s is deprecated.\n", modname.c_str(), r->fields[0]); - sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"%s-%s\"", modname.c_str(), r->fields[0]); - db->execute(q); - } else { - proxy_warning("Impossible to set not existing variable %s with value \"%s\". Deleting. If the variable name is correct, this version doesn't support it\n", r->fields[0],r->fields[1]); - } - sprintf(q,"DELETE FROM global_variables WHERE variable_name=\"%s-%s\"", modname.c_str(), r->fields[0]); - db->execute(q); - } - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); - if (variables_special_values.count(v) > 0) { - if (special_variable_action != nullptr) { - special_variable_action(v, r->fields[1], db); - } - } - } +void ProxySQL_Admin::check_and_build_standard_tables(SQLite3DB *db, std::vector *tables_defs) { +// int i; + table_def_t *td; + db->execute("PRAGMA foreign_keys = OFF"); + for (std::vector::iterator it=tables_defs->begin(); it!=tables_defs->end(); ++it) { + td=*it; + db->check_and_build_table(td->table_name, td->table_def); + } + db->execute("PRAGMA foreign_keys = ON"); +}; + + + +void ProxySQL_Admin::insert_into_tables_defs(std::vector *tables_defs, const char *table_name, const char *table_def) { + table_def_t *td = new table_def_t; + td->table_name=strdup(table_name); + td->table_def=strdup(table_def); + tables_defs->push_back(td); +}; + +void ProxySQL_Admin::drop_tables_defs(std::vector *tables_defs) { + table_def_t *td; + while (!tables_defs->empty()) { + td=tables_defs->back(); + free(td->table_name); + td->table_name=NULL; + free(td->table_def); + td->table_def=NULL; + tables_defs->pop_back(); + delete td; } -} - -void ProxySQL_Admin::flush_admin_variables___database_to_runtime( - SQLite3DB *db, bool replace, const string& checksum, const time_t epoch, bool lock -) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ADMIN variables. Replace:%d\n", replace); - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - if (flush_GENERIC_variables__retrieve__database_to_runtime("admin", error, cols, affected_rows, resultset) == true) { - wrlock(); - flush_GENERIC_variables__process__database_to_runtime("admin", db, resultset, lock, replace, {"version"}, {"debug"}, {}, {}); - //commit(); NOT IMPLEMENTED +}; - // Checksums are always generated - 'admin-checksum_*' deprecated +std::map request_headers(const httpserver::http_request& request) { + auto req_headers = request.get_headers(); + std::map result {}; - { - // generate checksum for cluster - pthread_mutex_lock(&GloVars.checksum_mutex); - flush_admin_variables___runtime_to_database(admindb, false, false, false, true); - flush_GENERIC_variables__checksum__database_to_runtime("admin", checksum, epoch); - pthread_mutex_unlock(&GloVars.checksum_mutex); - } - wrunlock(); - { - load_http_server(); - load_restapi_server(); - // Update the admin variable for 'web_verbosity' - admin___web_verbosity = variables.web_verbosity; - } + for (const auto& header : req_headers) { + result.insert({header.first, header.second}); } - if (resultset) delete resultset; -} -void ProxySQL_Admin::flush_pgsql_variables___runtime_to_database(SQLite3DB* db, bool replace, bool del, bool onlyifempty, bool runtime, bool use_lock) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing PgSQL variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); - if (onlyifempty) { - char* error = NULL; - int cols = 0; - int affected_rows = 0; - SQLite3_result* resultset = NULL; - char* q = (char*)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'pgsql-%'"; - db->execute_statement(q, &error, &cols, &affected_rows, &resultset); - int matching_rows = 0; - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; - } - else { - for (std::vector::iterator it = resultset->rows.begin(); it != resultset->rows.end(); ++it) { - SQLite3_row* r = *it; - matching_rows += atoi(r->fields[0]); - } - } - if (resultset) delete resultset; - if (matching_rows) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has PgSQL variables - skipping\n"); - return; - } - } - if (del) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting PgSQL variables from global_variables\n"); - db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'pgsql-%'"); - } - static char* a; - static char* b; - if (replace) { - a = (char*)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(?1, ?2)"; - } - else { - a = (char*)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(?1, ?2)"; - } - int rc; - sqlite3_stmt* statement1 = NULL; - sqlite3_stmt* statement2 = NULL; - //sqlite3 *mydb3=db->get_db(); - //rc=(*proxy_sqlite3_prepare_v2)(mydb3, a, -1, &statement1, 0); - rc = db->prepare_v2(a, &statement1); - ASSERT_SQLITE_OK(rc, db); - if (runtime) { - db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'pgsql-%'"); - b = (char*)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(?1, ?2)"; - //rc=(*proxy_sqlite3_prepare_v2)(mydb3, b, -1, &statement2, 0); - rc = db->prepare_v2(b, &statement2); - ASSERT_SQLITE_OK(rc, db); - } - if (use_lock) { - GloPTH->wrlock(); - db->execute("BEGIN"); - } - char** varnames = GloPTH->get_variables_list(); - for (int i = 0; varnames[i]; i++) { - char* val = GloPTH->get_variable(varnames[i]); - char* qualified_name = (char*)malloc(strlen(varnames[i]) + 12); - sprintf(qualified_name, "pgsql-%s", varnames[i]); - rc = (*proxy_sqlite3_bind_text)(statement1, 1, qualified_name, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); - rc = (*proxy_sqlite3_bind_text)(statement1, 2, (val ? val : (char*)""), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); - SAFE_SQLITE3_STEP2(statement1); - rc = (*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, db); - rc = (*proxy_sqlite3_reset)(statement1); ASSERT_SQLITE_OK(rc, db); - if (runtime) { - rc = (*proxy_sqlite3_bind_text)(statement2, 1, qualified_name, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); - rc = (*proxy_sqlite3_bind_text)(statement2, 2, (val ? val : (char*)""), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); - SAFE_SQLITE3_STEP2(statement2); - rc = (*proxy_sqlite3_clear_bindings)(statement2); ASSERT_SQLITE_OK(rc, db); - rc = (*proxy_sqlite3_reset)(statement2); ASSERT_SQLITE_OK(rc, db); - } - if (val) - free(val); - free(qualified_name); - } - if (use_lock) { - db->execute("COMMIT"); - GloPTH->wrunlock(); - } - (*proxy_sqlite3_finalize)(statement1); - if (runtime) - (*proxy_sqlite3_finalize)(statement2); - for (int i = 0; varnames[i]; i++) { - free(varnames[i]); - } - free(varnames); + return result; } -void ProxySQL_Admin::flush_GENERIC_variables__checksum__database_to_runtime(const string& modname, const string& checksum, const time_t epoch) { - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - std::string q; - q="SELECT variable_name, variable_value FROM runtime_global_variables WHERE variable_name LIKE '" + modname + "-\%' "; - if (modname == "mysql") { - q += " AND variable_name NOT IN ('mysql-threads')"; - if (GloVars.cluster_sync_interfaces == false) { - q += " AND variable_name NOT IN " + string(CLUSTER_SYNC_INTERFACES_MYSQL); - } - } else if (modname == "admin") { - if (GloVars.cluster_sync_interfaces == false) { - q += " AND variable_name NOT IN " + string(CLUSTER_SYNC_INTERFACES_ADMIN); - } - } - q += " ORDER BY variable_name"; - admindb->execute_statement(q.c_str(), &error , &cols , &affected_rows , &resultset); - uint64_t hash1 = resultset->raw_checksum(); - uint32_t d32[2]; - char buf[20]; - memcpy(&d32, &hash1, sizeof(hash1)); - sprintf(buf,"0x%0X%0X", d32[0], d32[1]); - ProxySQL_Checksum_Value *checkvar = NULL; - if (modname == "admin") { - checkvar = &GloVars.checksums_values.admin_variables; - } else if (modname == "mysql") { - checkvar = &GloVars.checksums_values.mysql_variables; - } else if (modname == "ldap") { - checkvar = &GloVars.checksums_values.ldap_variables; - } - assert(checkvar != NULL); - checkvar->set_checksum(buf); - checkvar->version++; - time_t t = time(NULL); - if (epoch != 0 && checksum != "" && checkvar->checksum == checksum) { - checkvar->epoch = epoch; - } else { - checkvar->epoch = t; +std::shared_ptr make_response( + const std::pair, std::string>& res_data +) { + std::shared_ptr response = + std::make_shared(httpserver::string_response(res_data.second)); + + for (const auto& h_key_val : res_data.first) { + response->with_header(h_key_val.first, h_key_val.second); } - GloVars.epoch_version = t; - GloVars.generate_global_checksum(); - GloVars.checksums_values.updates_cnt++; - string modnameupper = modname; - for (char &c : modnameupper) { c = std::toupper(c); } - proxy_info( - "Computed checksum for 'LOAD %s VARIABLES TO RUNTIME' was '%s', with epoch '%llu'\n", - modnameupper.c_str(), checkvar->checksum, checkvar->epoch - ); - delete resultset; + + return response; } -void ProxySQL_Admin::flush_mysql_variables___database_to_runtime(SQLite3DB *db, bool replace, const std::string& checksum, const time_t epoch) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing MySQL variables. Replace:%d\n", replace); - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - if (flush_GENERIC_variables__retrieve__database_to_runtime("mysql", error, cols, affected_rows, resultset) == true) { - GloMTH->wrlock(); - char * previous_default_charset = GloMTH->get_variable_string((char *)"default_charset"); - char * previous_default_collation_connection = GloMTH->get_variable_string((char *)"default_collation_connection"); - assert(previous_default_charset); - assert(previous_default_collation_connection); - flush_GENERIC_variables__process__database_to_runtime("mysql", db, resultset, false, replace, {}, {"session_debug"}, {"forward_autocommit"}, - {"default_collation_connection", "default_charset", "show_processlist_extended"}, - [](const std::string& varname, const char *varvalue, SQLite3DB* db) { - if (varname == "default_collation_connection" || varname == "default_charset") { - char *val=GloMTH->get_variable((char *)varname.c_str()); - if (val) { - if (strcmp(val,varvalue)) { - char q[1000]; - proxy_warning("Variable %s with value \"%s\" is being replaced with value \"%s\".\n", varname.c_str(), varvalue, val); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-%s\",\"%s\")", varname.c_str() ,val); - db->execute(q); - } - free(val); - } - } else if (varname == "show_processlist_extended") { - GloAdmin->variables.mysql_show_processlist_extended = atoi(varvalue); - } - } - ); - char q[1000]; - char * default_charset = GloMTH->get_variable_string((char *)"default_charset"); - char * default_collation_connection = GloMTH->get_variable_string((char *)"default_collation_connection"); - assert(default_charset); - assert(default_collation_connection); - MARIADB_CHARSET_INFO * ci = NULL; - ci = proxysql_find_charset_name(default_charset); - if (ci == NULL) { - // invalid charset - proxy_error("Found an incorrect value for mysql-default_charset: %s\n", default_charset); - // let's try to get a charset from collation connection - ci = proxysql_find_charset_collate(default_collation_connection); - if (ci == NULL) { - proxy_error("Found an incorrect value for mysql-default_collation_connection: %s\n", default_collation_connection); - const char *p = mysql_tracked_variables[SQL_CHARACTER_SET].default_value; - ci = proxysql_find_charset_name(p); - assert(ci); - proxy_info("Resetting mysql-default_charset to hardcoded default value: %s\n", ci->csname); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_charset\",\"%s\")", ci->csname); - db->execute(q); - GloMTH->set_variable((char *)"default_charset",ci->csname); - proxy_info("Resetting mysql-default_collation_connection to hardcoded default value: %s\n", ci->name); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_collation_connection\",\"%s\")", ci->name); - db->execute(q); - GloMTH->set_variable((char *)"default_collation_connection",ci->name); - } else { - proxy_info("Changing mysql-default_charset to %s using configured mysql-default_collation_connection %s\n", ci->csname, ci->name); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_charset\",\"%s\")", ci->csname); - db->execute(q); - GloMTH->set_variable((char *)"default_charset",ci->csname); - } - } else { - MARIADB_CHARSET_INFO * cic = NULL; - cic = proxysql_find_charset_collate(default_collation_connection); - if (cic == NULL) { - proxy_error("Found an incorrect value for mysql-default_collation_connection: %s\n", default_collation_connection); - proxy_info("Changing mysql-default_collation_connection to %s using configured mysql-default_charset: %s\n", ci->name, ci->csname); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_collation_connection\",\"%s\")", ci->name); - db->execute(q); - GloMTH->set_variable((char *)"default_collation_connection",ci->name); - } else { - if (strcmp(cic->csname,ci->csname)==0) { - // mysql-default_collation_connection and mysql-default_charset are compatible - } else { - proxy_error("Found incompatible values for mysql-default_charset (%s) and mysql-default_collation_connection (%s)\n", default_charset, default_collation_connection); - bool use_collation = true; - if (strcmp(default_charset, previous_default_charset)) { // charset changed - if (strcmp(default_collation_connection, previous_default_collation_connection)==0) { // collation didn't change - // the user has changed the charset but not the collation - // we use charset as source of truth - use_collation = false; - } - } - if (use_collation) { - proxy_info("Changing mysql-default_charset to %s using configured mysql-default_collation_connection %s\n", cic->csname, cic->name); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_charset\",\"%s\")", cic->csname); - db->execute(q); - GloMTH->set_variable((char *)"default_charset",cic->csname); - } else { - proxy_info("Changing mysql-default_collation_connection to %s using configured mysql-default_charset: %s\n", ci->name, ci->csname); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_collation_connection\",\"%s\")", ci->name); - db->execute(q); - GloMTH->set_variable((char *)"default_collation_connection",ci->name); - } - } - } - } - free(default_charset); - free(default_collation_connection); - free(previous_default_charset); - free(previous_default_collation_connection); - GloMTH->commit(); - GloMTH->wrunlock(); +/** + * @brief Checks if the supplied port is available. + * + * @param port_num The port number to check. + * @param free Output parameter. True if the port is free, false otherwise. + * + * @return Returns: + * - '-1' in case 'SO_REUSEADDR' fails to be set for the check. + * - '-2' in case of invalid arguments supplied. + * - '0' otherwise. + */ +int check_port_availability(int port_num, bool* port_free) { + int ecode = 0; + int sfd = 0; + int reuseaddr = 1; + struct sockaddr_in tmp_addr; - { - // NOTE: 'GloMTH->wrunlock()' should have been called before this point to avoid possible - // deadlocks. See issue #3847. - pthread_mutex_lock(&GloVars.checksum_mutex); - // generate checksum for cluster - flush_mysql_variables___runtime_to_database(admindb, false, false, false, true, true); - flush_GENERIC_variables__checksum__database_to_runtime("mysql", checksum, epoch); - pthread_mutex_unlock(&GloVars.checksum_mutex); - } + if (port_num == 0 || port_free == nullptr) { + return -2; } - if (resultset) delete resultset; -} -void ProxySQL_Admin::flush_sqliteserver_variables___database_to_runtime(SQLite3DB *db, bool replace) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing SQLiteServer variables. Replace:%d\n", replace); - if ( - (GloVars.global.sqlite3_server == false) - || - ( GloSQLite3Server == NULL ) - ) { - return; - } - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - if (flush_GENERIC_variables__retrieve__database_to_runtime("sqliteserver", error, cols, affected_rows, resultset) == true) { - GloSQLite3Server->wrlock(); - flush_GENERIC_variables__process__database_to_runtime("sqliteserver", db, resultset, false, replace, {}, {"session_debug"}, {}, {}); - //GloClickHouse->commit(); - GloSQLite3Server->wrunlock(); - } - if (resultset) delete resultset; -} + // set 'port_free' to false by default + *port_free = false; -void ProxySQL_Admin::flush_sqliteserver_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ClickHouse variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); - if (GloVars.global.sqlite3_server == false) { - return; - } - if (onlyifempty) { - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'sqliteserver-%'"; - db->execute_statement(q, &error , &cols , &affected_rows , &resultset); - int matching_rows=0; - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; + sfd = socket(AF_INET, SOCK_STREAM, 0); + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.sin_family = AF_INET; + tmp_addr.sin_port = htons(port_num); + tmp_addr.sin_addr.s_addr = INADDR_ANY; + + if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddr, sizeof(reuseaddr)) == -1) { + close(sfd); + ecode = -1; + } else { + if (::bind(sfd, (struct sockaddr*)&tmp_addr, sizeof(tmp_addr)) == -1) { + close(sfd); } else { - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - matching_rows+=atoi(r->fields[0]); - } - } - if (resultset) delete resultset; - if (matching_rows) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has ClickHouse variables - skipping\n"); - return; + *port_free = true; + close(sfd); } } - if (del) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting ClickHouse variables from global_variables\n"); - db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'sqliteserver-%'"); - } - if (runtime) { - db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'sqliteserver-%'"); - } - char *a; - char *b=(char *)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(\"sqliteserver-%s\",\"%s\")"; - if (replace) { - a=(char *)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(\"sqliteserver-%s\",\"%s\")"; - } else { - a=(char *)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(\"sqliteserver-%s\",\"%s\")"; - } - int l=strlen(a)+200; - GloSQLite3Server->wrlock(); - char **varnames=GloSQLite3Server->get_variables_list(); - for (int i=0; varnames[i]; i++) { - char *val=GloSQLite3Server->get_variable(varnames[i]); - l+=( varnames[i] ? strlen(varnames[i]) : 6); - l+=( val ? strlen(val) : 6); - char *query=(char *)malloc(l); - sprintf(query, a, varnames[i], val); - if (runtime) { - db->execute(query); - sprintf(query, b, varnames[i], val); - } - db->execute(query); - if (val) - free(val); - free(query); - } - GloSQLite3Server->wrunlock(); - for (int i=0; varnames[i]; i++) { - free(varnames[i]); - } - free(varnames); -} + return ecode; +} -#ifdef PROXYSQLCLICKHOUSE -void ProxySQL_Admin::flush_clickhouse_variables___database_to_runtime(SQLite3DB *db, bool replace) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ClickHouse variables. Replace:%d\n", replace); - if ( - (GloVars.global.clickhouse_server == false) - || - ( GloClickHouseServer == NULL ) - ) { - return; - } +void ProxySQL_Admin::load_or_update_global_settings(SQLite3DB *db) { char *error=NULL; int cols=0; int affected_rows=0; SQLite3_result *resultset=NULL; - if (flush_GENERIC_variables__retrieve__database_to_runtime("clickhouse", error, cols, affected_rows, resultset) == true) { - GloClickHouseServer->wrlock(); - flush_GENERIC_variables__process__database_to_runtime("clickhouse", db, resultset, false, replace, {}, {"session_debug"}, {}, {}); - //GloClickHouse->commit(); - GloClickHouseServer->wrunlock(); - } - if (resultset) delete resultset; -} - -void ProxySQL_Admin::flush_clickhouse_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ClickHouse variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); - if ( - (GloVars.global.clickhouse_server == false) - || - ( GloClickHouseServer == NULL ) - ) { - return; - } - if (onlyifempty) { - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'clickhouse-%'"; - db->execute_statement(q, &error , &cols , &affected_rows , &resultset); - int matching_rows=0; - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; - } else { - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - matching_rows+=atoi(r->fields[0]); - } - } - if (resultset) delete resultset; - if (matching_rows) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has ClickHouse variables - skipping\n"); - return; - } - } - if (del) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting ClickHouse variables from global_variables\n"); - db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'clickhouse-%'"); - } - if (runtime) { - db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'clickhouse-%'"); - } - char *a; - char *b=(char *)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(\"clickhouse-%s\",\"%s\")"; - if (replace) { - a=(char *)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(\"clickhouse-%s\",\"%s\")"; - } else { - a=(char *)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(\"clickhouse-%s\",\"%s\")"; - } - int l=strlen(a)+200; - GloClickHouseServer->wrlock(); - char **varnames=GloClickHouseServer->get_variables_list(); - for (int i=0; varnames[i]; i++) { - char *val=GloClickHouseServer->get_variable(varnames[i]); - l+=( varnames[i] ? strlen(varnames[i]) : 6); - l+=( val ? strlen(val) : 6); - char *query=(char *)malloc(l); - sprintf(query, a, varnames[i], val); - if (runtime) { - db->execute(query); - sprintf(query, b, varnames[i], val); - } - db->execute(query); - if (val) - free(val); - free(query); - } - GloClickHouseServer->wrunlock(); - for (int i=0; varnames[i]; i++) { - free(varnames[i]); - } - free(varnames); -} -#endif /* PROXYSQLCLICKHOUSE */ - -void ProxySQL_Admin::flush_pgsql_variables___database_to_runtime(SQLite3DB* db, bool replace, const std::string& checksum, const time_t epoch) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing PgSQL variables. Replace:%d\n", replace); - char* error = NULL; - int cols = 0; - int affected_rows = 0; - SQLite3_result* resultset = NULL; - char* q = (char*)"SELECT substr(variable_name,7) vn, variable_value FROM global_variables WHERE variable_name LIKE 'pgsql-%'"; - admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); + char *q=(char *)"SELECT variable_name, variable_value FROM global_settings ORDER BY variable_name"; + db->execute_statement(q, &error , &cols , &affected_rows , &resultset); if (error) { proxy_error("Error on %s : %s\n", q, error); - return; - } - else { - GloPTH->wrlock(); - char* previous_default_charset = GloPTH->get_variable_string((char*)"default_charset"); - char* previous_default_collation_connection = GloPTH->get_variable_string((char*)"default_collation_connection"); - assert(previous_default_charset); - assert(previous_default_collation_connection); - for (std::vector::iterator it = resultset->rows.begin(); it != resultset->rows.end(); ++it) { - SQLite3_row* r = *it; - const char* value = r->fields[1]; - bool rc = GloPTH->set_variable(r->fields[0], value); - if (rc == false) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0], value); - if (replace) { - char* val = GloPTH->get_variable(r->fields[0]); - char q[1000]; - if (val) { - if (strcmp(val, value)) { - proxy_warning("Impossible to set variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0], value, val); - sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-%s\",\"%s\")", r->fields[0], val); - db->execute(q); - } - free(val); - } - else { - if (strcmp(r->fields[0], (char*)"session_debug") == 0) { - sprintf(q, "DELETE FROM disk.global_variables WHERE variable_name=\"pgsql-%s\"", r->fields[0]); - db->execute(q); - } - else { - if (strcmp(r->fields[0], (char*)"forward_autocommit") == 0) { - if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) { - proxy_error("Global variable pgsql-forward_autocommit is deprecated. See issue #3253\n"); - } - sprintf(q, "DELETE FROM disk.global_variables WHERE variable_name=\"pgsql-%s\"", r->fields[0]); - db->execute(q); - } - else { - proxy_warning("Impossible to set not existing variable %s with value \"%s\". Deleting. If the variable name is correct, this version doesn't support it\n", r->fields[0], r->fields[1]); - } + } else { + // note: we don't lock, this is done only during bootstrap + { + char *uuid = NULL; + bool write_uuid = true; + // search for uuid + for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { + SQLite3_row *r=*it; + if (strcasecmp(r->fields[0],"uuid")==0) { + uuid = strdup(r->fields[1]); + uuid_t uu; + if (uuid) { + if (uuid_parse(uuid,uu)==0) { + // we successful read an UUID + } else { + proxy_error("Ignoring invalid UUID format in global_settings: %s\n", uuid); + free(uuid); + uuid = NULL; } - sprintf(q, "DELETE FROM global_variables WHERE variable_name=\"pgsql-%s\"", r->fields[0]); - db->execute(q); } } } - else { - if ( - (strcmp(r->fields[0], "default_collation_connection") == 0) - || (strcmp(r->fields[0], "default_charset") == 0) - ) { - char* val = GloPTH->get_variable(r->fields[0]); - char q[1000]; - if (val) { - if (strcmp(val, value)) { - proxy_warning("Variable %s with value \"%s\" is being replaced with value \"%s\".\n", r->fields[0], value, val); - sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-%s\",\"%s\")", r->fields[0], val); - db->execute(q); - } - free(val); + if (uuid) { // we found an UUID in the DB + if (GloVars.uuid) { // an UUID is already defined + if (strcmp(uuid, GloVars.uuid)==0) { // the match + proxy_info("Using UUID: %s\n", uuid); + write_uuid = false; + } else { + // they do not match. The one on DB will be replaced + proxy_info("Using UUID: %s . Replacing UUID from database: %s\n", GloVars.uuid, uuid); } + } else { + // the UUID already defined, so the one in the DB will be used + proxy_info("Using UUID from database: %s\n", uuid); + GloVars.uuid=strdup(uuid); } - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Set variable %s with value \"%s\"\n", r->fields[0], value); - if (strcmp(r->fields[0], (char*)"show_processlist_extended") == 0) { - variables.pgsql_show_processlist_extended = atoi(value); + } else { + if (GloVars.uuid) { + // we will write the UUID in the DB + proxy_info("Using UUID: %s . Writing it to database\n", GloVars.uuid); + } else { + // UUID not defined anywhere, we will create a new one + uuid_t uu; + uuid_generate(uu); + char buf[40]; + uuid_unparse(uu, buf); + GloVars.uuid=strdup(buf); + proxy_info("Using UUID: %s , randomly generated. Writing it to database\n", GloVars.uuid); } } - // } - } - - char q[1000]; - char* default_charset = GloPTH->get_variable_string((char*)"default_charset"); - char* default_collation_connection = GloPTH->get_variable_string((char*)"default_collation_connection"); - assert(default_charset); - assert(default_collation_connection); - MARIADB_CHARSET_INFO* ci = NULL; - ci = proxysql_find_charset_name(default_charset); - if (ci == NULL) { - // invalid charset - proxy_error("Found an incorrect value for pgsql-default_charset: %s\n", default_charset); - // let's try to get a charset from collation connection - ci = proxysql_find_charset_collate(default_collation_connection); - if (ci == NULL) { - proxy_error("Found an incorrect value for pgsql-default_collation_connection: %s\n", default_collation_connection); - const char* p = mysql_tracked_variables[SQL_CHARACTER_SET].default_value; - ci = proxysql_find_charset_name(p); - assert(ci); - proxy_info("Resetting pgsql-default_charset to hardcoded default value: %s\n", ci->csname); - sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_charset\",\"%s\")", ci->csname); - db->execute(q); - GloPTH->set_variable((char*)"default_charset", ci->csname); - proxy_info("Resetting pgsql-default_collation_connection to hardcoded default value: %s\n", ci->name); - sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_collation_connection\",\"%s\")", ci->name); - db->execute(q); - GloPTH->set_variable((char*)"default_collation_connection", ci->name); - } - else { - proxy_info("Changing pgsql-default_charset to %s using configured pgsql-default_collation_connection %s\n", ci->csname, ci->name); - sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_charset\",\"%s\")", ci->csname); - db->execute(q); - GloPTH->set_variable((char*)"default_charset", ci->csname); - } - } - else { - MARIADB_CHARSET_INFO* cic = NULL; - cic = proxysql_find_charset_collate(default_collation_connection); - if (cic == NULL) { - proxy_error("Found an incorrect value for pgsql-default_collation_connection: %s\n", default_collation_connection); - proxy_info("Changing pgsql-default_collation_connection to %s using configured pgsql-default_charset: %s\n", ci->name, ci->csname); - sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_collation_connection\",\"%s\")", ci->name); - db->execute(q); - GloPTH->set_variable((char*)"default_collation_connection", ci->name); + if (write_uuid) { + std::string s = "INSERT OR REPLACE INTO global_settings VALUES (\"uuid\", \""; + s += GloVars.uuid; + s += "\")"; + db->execute(s.c_str()); } - else { - if (strcmp(cic->csname, ci->csname) == 0) { - // pgsql-default_collation_connection and pgsql-default_charset are compatible - } - else { - proxy_error("Found incompatible values for pgsql-default_charset (%s) and pgsql-default_collation_connection (%s)\n", default_charset, default_collation_connection); - bool use_collation = true; - if (strcmp(default_charset, previous_default_charset)) { // charset changed - if (strcmp(default_collation_connection, previous_default_collation_connection) == 0) { // collation didn't change - // the user has changed the charset but not the collation - // we use charset as source of truth - use_collation = false; - } - } - if (use_collation) { - proxy_info("Changing pgsql-default_charset to %s using configured pgsql-default_collation_connection %s\n", cic->csname, cic->name); - sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_charset\",\"%s\")", cic->csname); - db->execute(q); - GloPTH->set_variable((char*)"default_charset", cic->csname); - } - else { - proxy_info("Changing pgsql-default_collation_connection to %s using configured pgsql-default_charset: %s\n", ci->name, ci->csname); - sprintf(q, "INSERT OR REPLACE INTO global_variables VALUES(\"pgsql-default_collation_connection\",\"%s\")", ci->name); - db->execute(q); - GloPTH->set_variable((char*)"default_collation_connection", ci->name); - } - } + if (uuid) { + free(uuid); + uuid=NULL; } } - free(default_charset); - free(default_collation_connection); - free(previous_default_charset); - free(previous_default_collation_connection); - GloPTH->commit(); - GloPTH->wrunlock(); - /* Checksums are always generated - 'admin-checksum_*' deprecated - { - // NOTE: 'GloPTH->wrunlock()' should have been called before this point to avoid possible - // deadlocks. See issue #3847. - pthread_mutex_lock(&GloVars.checksum_mutex); - // generate checksum for cluster - flush_mysql_variables___runtime_to_database(admindb, false, false, false, true, true); - char* error = NULL; - int cols = 0; - int affected_rows = 0; - SQLite3_result* resultset = NULL; - std::string q; - q = "SELECT variable_name, variable_value FROM runtime_global_variables WHERE variable_name LIKE 'mysql-\%' AND variable_name NOT IN ('mysql-threads')"; - if (GloVars.cluster_sync_interfaces == false) { - q += " AND variable_name NOT IN " + string(CLUSTER_SYNC_INTERFACES_MYSQL); - } - q += " ORDER BY variable_name"; - admindb->execute_statement(q.c_str(), &error, &cols, &affected_rows, &resultset); - uint64_t hash1 = resultset->raw_checksum(); - uint32_t d32[2]; - char buf[20]; - memcpy(&d32, &hash1, sizeof(hash1)); - sprintf(buf, "0x%0X%0X", d32[0], d32[1]); - GloVars.checksums_values.mysql_variables.set_checksum(buf); - GloVars.checksums_values.mysql_variables.version++; - time_t t = time(NULL); - if (epoch != 0 && checksum != "" && GloVars.checksums_values.mysql_variables.checksum == checksum) { - GloVars.checksums_values.mysql_variables.epoch = epoch; - } - else { - GloVars.checksums_values.mysql_variables.epoch = t; - } - GloVars.epoch_version = t; - GloVars.generate_global_checksum(); - GloVars.checksums_values.updates_cnt++; - pthread_mutex_unlock(&GloVars.checksum_mutex); + if (resultset) { delete resultset; } - proxy_info( - "Computed checksum for 'LOAD MYSQL VARIABLES TO RUNTIME' was '%s', with epoch '%llu'\n", - GloVars.checksums_values.mysql_variables.checksum, GloVars.checksums_values.mysql_variables.epoch - ); - */ } - if (resultset) delete resultset; } -void ProxySQL_Admin::flush_mysql_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime, bool use_lock) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing MySQL variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); - if (onlyifempty) { - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'mysql-%'"; - db->execute_statement(q, &error , &cols , &affected_rows , &resultset); - int matching_rows=0; - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; - } else { - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - matching_rows+=atoi(r->fields[0]); +void ProxySQL_Admin::load_restapi_server() { + if (!all_modules_started) { return; } + + std::function(const httpserver::http_request&)> prometheus_callback { + [this](const httpserver::http_request& request) { + auto headers = request_headers(request); + auto serial_response = this->serial_exposer(headers); + auto http_response = make_response(serial_response); + + return http_response; + } + }; + + bool free_restapi_port = false; + + // Helper lambda taking a boolean reference as a parameter to check if 'restapi_port' is available. + // In case of port not being free or error, logs an error 'ProxySQL_RestAPI_Server' isn't able to be started. + const auto check_restapi_port = [&](bool& restapi_port_free) -> void { + int e_port_check = check_port_availability(variables.restapi_port, &restapi_port_free); + + if (restapi_port_free == false) { + if (e_port_check == -1) { + proxy_error("Unable to start 'ProxySQL_RestAPI_Server', failed to set 'SO_REUSEADDR' to check port availability.\n"); + } else { + proxy_error( + "Unable to start 'ProxySQL_RestAPI_Server', port '%d' already in use.\n", + variables.restapi_port + ); } } - if (resultset) delete resultset; - if (matching_rows) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has MySQL variables - skipping\n"); - return; + }; + + if (variables.restapi_enabled != variables.restapi_enabled_old) { + if (variables.restapi_enabled) { + check_restapi_port(free_restapi_port); } - } - if (del) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting MySQL variables from global_variables\n"); - db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'mysql-%'"); - } - static char *a; - static char *b; - if (replace) { - a=(char *)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(?1, ?2)"; + + if (variables.restapi_enabled && free_restapi_port) { + AdminRestApiServer = new ProxySQL_RESTAPI_Server( + variables.restapi_port, {{"/metrics", prometheus_callback}} + ); + } else { + delete AdminRestApiServer; + AdminRestApiServer = NULL; + } + variables.restapi_enabled_old = variables.restapi_enabled; } else { - a=(char *)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(?1, ?2)"; - } - int rc; - sqlite3_stmt *statement1=NULL; - sqlite3_stmt *statement2=NULL; - - rc=db->prepare_v2(a, &statement1); - ASSERT_SQLITE_OK(rc, db); - if (runtime) { - db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'mysql-%'"); - b=(char *)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(?1, ?2)"; - - rc=db->prepare_v2(b, &statement2); - ASSERT_SQLITE_OK(rc, db); - } - if (use_lock) { - GloMTH->wrlock(); - db->execute("BEGIN"); - } - char **varnames=GloMTH->get_variables_list(); - for (int i=0; varnames[i]; i++) { - char *val=GloMTH->get_variable(varnames[i]); - char *qualified_name=(char *)malloc(strlen(varnames[i])+7); - sprintf(qualified_name, "mysql-%s", varnames[i]); - rc=(*proxy_sqlite3_bind_text)(statement1, 1, qualified_name, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); - rc=(*proxy_sqlite3_bind_text)(statement1, 2, (val ? val : (char *)""), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); - SAFE_SQLITE3_STEP2(statement1); - rc=(*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, db); - rc=(*proxy_sqlite3_reset)(statement1); ASSERT_SQLITE_OK(rc, db); - if (runtime) { - rc=(*proxy_sqlite3_bind_text)(statement2, 1, qualified_name, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); - rc=(*proxy_sqlite3_bind_text)(statement2, 2, (val ? val : (char *)""), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); - SAFE_SQLITE3_STEP2(statement2); - rc=(*proxy_sqlite3_clear_bindings)(statement2); ASSERT_SQLITE_OK(rc, db); - rc=(*proxy_sqlite3_reset)(statement2); ASSERT_SQLITE_OK(rc, db); - } - if (val) - free(val); - free(qualified_name); - } - if (use_lock) { - db->execute("COMMIT"); - GloMTH->wrunlock(); - } - (*proxy_sqlite3_finalize)(statement1); - if (runtime) - (*proxy_sqlite3_finalize)(statement2); - for (int i=0; varnames[i]; i++) { - free(varnames[i]); - } - free(varnames); -} + if (variables.restapi_port != variables.restapi_port_old) { + if (AdminRestApiServer) { + delete AdminRestApiServer; + AdminRestApiServer = NULL; + } -void ProxySQL_Admin::flush_ldap_variables___database_to_runtime(SQLite3DB *db, bool replace, const std::string& checksum, const time_t epoch) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing LDAP variables. Replace:%d\n", replace); - if (GloMyLdapAuth == NULL) { - return; - } - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - if (flush_GENERIC_variables__retrieve__database_to_runtime("ldap", error, cols, affected_rows, resultset) == true) { - GloMyLdapAuth->wrlock(); - flush_GENERIC_variables__process__database_to_runtime("admin", db, resultset, false, replace, {}, {}, {}, {}); - GloMyLdapAuth->wrunlock(); + if (variables.restapi_enabled) { + check_restapi_port(free_restapi_port); + } - // Checksums are always generated - 'admin-checksum_*' deprecated - { - pthread_mutex_lock(&GloVars.checksum_mutex); - // generate checksum for cluster - flush_ldap_variables___runtime_to_database(admindb, false, false, false, true); - flush_GENERIC_variables__checksum__database_to_runtime("ldap", checksum, epoch); - pthread_mutex_unlock(&GloVars.checksum_mutex); + if (variables.restapi_enabled && free_restapi_port) { + AdminRestApiServer = new ProxySQL_RESTAPI_Server( + variables.restapi_port, {{"/metrics", prometheus_callback}} + ); + } + variables.restapi_port_old = variables.restapi_port; } } - if (resultset) delete resultset; } -void ProxySQL_Admin::flush_ldap_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing LDAP variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); - if (GloMyLdapAuth == NULL) { - return; - } - if (onlyifempty) { - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'ldap-%'"; - db->execute_statement(q, &error , &cols , &affected_rows , &resultset); - int matching_rows=0; - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; +void ProxySQL_Admin::load_http_server() { + if (!all_modules_started) { return; } + + if (variables.web_enabled != variables.web_enabled_old) { + if (variables.web_enabled) { + if (GloVars.web_interface_plugin == NULL) { + char *key_pem; + char *cert_pem; + GloVars.get_SSL_pem_mem(&key_pem, &cert_pem); + Admin_HTTP_Server = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_SSL, + variables.web_port, + NULL, NULL, http_handler, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, MHD_OPTION_STRICT_FOR_CLIENT, (int) 1, + MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 4, + MHD_OPTION_NONCE_NC_SIZE, (unsigned int) 300, + MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, + MHD_OPTION_END); + free(key_pem); + free(cert_pem); + } else { + if (GloWebInterface) { + int sfd = 0; + int reuseaddr = 1; + struct sockaddr_in tmp_addr; + + sfd = socket(AF_INET, SOCK_STREAM, 0); + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.sin_family = AF_INET; + tmp_addr.sin_port = htons(variables.web_port); + tmp_addr.sin_addr.s_addr = INADDR_ANY; + + if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddr, sizeof(reuseaddr)) == -1) { + close(sfd); + proxy_error( + "Unable to start WebInterfacePlugin, failed to set 'SO_REUSEADDR' to check port '%d' availability.\n", + variables.web_port + ); + } else { + if (::bind(sfd, (struct sockaddr*)&tmp_addr, (socklen_t)sizeof(tmp_addr)) == -1) { + close(sfd); + proxy_error( + "Unable to start WebInterfacePlugin, port '%d' already in use.\n", + variables.web_port + ); + } else { + close(sfd); + GloWebInterface->start(variables.web_port); + } + } + } + } } else { - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - matching_rows+=atoi(r->fields[0]); + if (GloVars.web_interface_plugin == NULL) { + MHD_stop_daemon(Admin_HTTP_Server); + Admin_HTTP_Server = NULL; + } else { + if (GloWebInterface) { + GloWebInterface->stop(); + } } - } - if (resultset) delete resultset; - if (matching_rows) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has LDAP variables - skipping\n"); - return; + } + variables.web_enabled_old = variables.web_enabled; + } else { + if (variables.web_port != variables.web_port_old) { + if (variables.web_enabled) { + if (GloVars.web_interface_plugin == NULL) { + MHD_stop_daemon(Admin_HTTP_Server); + Admin_HTTP_Server = NULL; + char *key_pem; + char *cert_pem; + GloVars.get_SSL_pem_mem(&key_pem, &cert_pem); + Admin_HTTP_Server = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_SSL, + variables.web_port, + NULL, NULL, http_handler, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, MHD_OPTION_STRICT_FOR_CLIENT, (int) 1, + MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 4, + MHD_OPTION_NONCE_NC_SIZE, (unsigned int) 300, + MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, + MHD_OPTION_END); + free(key_pem); + free(cert_pem); + } else { + if (GloWebInterface) { + GloWebInterface->start(variables.web_port); + } + } + } + variables.web_port_old = variables.web_port; } } - if (del) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting LDAP variables from global_variables\n"); - db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'ldap-%'"); - } - if (runtime) { - db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'ldap-%'"); - } - char *a; - char *b=(char *)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(\"ldap-%s\",\"%s\")"; - if (replace) { - a=(char *)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(\"ldap-%s\",\"%s\")"; - } else { - a=(char *)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(\"ldap-%s\",\"%s\")"; - } - int l=strlen(a)+200; - GloMyLdapAuth->wrlock(); - char **varnames=GloMyLdapAuth->get_variables_list(); - for (int i=0; varnames[i]; i++) { - char *val=GloMyLdapAuth->get_variable(varnames[i]); - l+=( varnames[i] ? strlen(varnames[i]) : 6); - l+=( val ? strlen(val) : 6); - char *query=(char *)malloc(l); - sprintf(query, a, varnames[i], val); - if (runtime) { - db->execute(query); - sprintf(query, b, varnames[i], val); - } - db->execute(query); - if (val) - free(val); - free(query); - } - GloMyLdapAuth->wrunlock(); - for (int i=0; varnames[i]; i++) { - free(varnames[i]); - } - free(varnames); } char **ProxySQL_Admin::get_variables_list() { @@ -10126,70 +4828,6 @@ void ProxySQL_Admin::save_pgsql_firewall_from_runtime(bool _runtime) { } } -void ProxySQL_Admin::flush_admin_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ADMIN variables. Replace:%d, Delete:%d, Only_If_Empty:%d\n", replace, del, onlyifempty); - if (onlyifempty) { - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT COUNT(*) FROM global_variables WHERE variable_name LIKE 'admin-%'"; - db->execute_statement(q, &error , &cols , &affected_rows , &resultset); - int matching_rows=0; - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; - } else { - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - matching_rows+=atoi(r->fields[0]); - } - } - if (resultset) delete resultset; - if (matching_rows) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Table global_variables has ADMIN variables - skipping\n"); - return; - } - } - if (del) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Deleting ADMIN variables from global_variables\n"); - db->execute("DELETE FROM global_variables WHERE variable_name LIKE 'admin-%'"); - } - if (runtime) { - db->execute("DELETE FROM runtime_global_variables WHERE variable_name LIKE 'admin-%'"); - } - char *a; - char *b=(char *)"INSERT INTO runtime_global_variables(variable_name, variable_value) VALUES(\"admin-%s\",\"%s\")"; - if (replace) { - a=(char *)"REPLACE INTO global_variables(variable_name, variable_value) VALUES(\"admin-%s\",\"%s\")"; - } else { - a=(char *)"INSERT OR IGNORE INTO global_variables(variable_name, variable_value) VALUES(\"admin-%s\",\"%s\")"; - } - int l=strlen(a)+200; - - char **varnames=get_variables_list(); - for (int i=0; varnames[i]; i++) { - char *val=get_variable(varnames[i]); - l+=( varnames[i] ? strlen(varnames[i]) : 6); - l+=( val ? strlen(val) : 6); - char *query=(char *)malloc(l); - sprintf(query, a, varnames[i], val); - db->execute(query); - if (runtime) { - sprintf(query, b, varnames[i], val); - db->execute(query); - } - if (val) - free(val); - free(query); - } - for (int i=0; varnames[i]; i++) { - free(varnames[i]); - } - free(varnames); - -} - #ifdef DEBUG void ProxySQL_Admin::flush_debug_levels_runtime_to_database(SQLite3DB *db, bool replace) { int i; diff --git a/lib/ProxySQL_Admin_Stats.cpp b/lib/ProxySQL_Admin_Stats.cpp index d7b79876a..219faea9c 100644 --- a/lib/ProxySQL_Admin_Stats.cpp +++ b/lib/ProxySQL_Admin_Stats.cpp @@ -25,8 +25,8 @@ } while (rc!=SQLITE_DONE);\ } while (0) -extern bool proxysql_mysql_paused; -extern bool proxysql_pgsql_paused; +extern bool admin_proxysql_mysql_paused; +extern bool admin_proxysql_pgsql_paused; extern MySQL_Authentication *GloMyAuth; extern MySQL_LDAP_Authentication *GloMyLdapAuth; extern Query_Cache *GloQC; @@ -50,11 +50,11 @@ void ProxySQL_Admin::p_update_metrics() { this->p_update_stmt_metrics(); // updated mysql_listener_paused - int st = ( proxysql_mysql_paused == true ? 1 : 0); + int st = ( admin_proxysql_mysql_paused == true ? 1 : 0); this->metrics.p_gauge_array[p_admin_gauge::mysql_listener_paused]->Set(st); // updated pgsql_listener_paused - st = (proxysql_pgsql_paused == true ? 1 : 0); + st = ( admin_proxysql_pgsql_paused == true ? 1 : 0); this->metrics.p_gauge_array[p_admin_gauge::pgsql_listener_paused]->Set(st); } @@ -556,7 +556,7 @@ void ProxySQL_Admin::stats___mysql_global() { } { vn=(char *)"mysql_listener_paused"; - sprintf(bu, "%s", ( proxysql_mysql_paused==true ? "true" : "false") ); + sprintf(bu, "%s", ( admin_proxysql_mysql_paused==true ? "true" : "false") ); query=(char *)malloc(strlen(a)+strlen(vn)+strlen(bu)+16); sprintf(query,a,vn,bu); statsdb->execute(query);