diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index fb898e20e..14f5c5956 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -9,7 +9,7 @@ #include "ProxySQL_RESTAPI_Server.hpp" typedef struct { uint32_t hash; uint32_t key; } t_symstruct; - +class ProxySQL_Config; class Scheduler_Row { public: @@ -270,12 +270,7 @@ class ProxySQL_Admin { void stats___mysql_prepared_statements_info(); void stats___mysql_gtid_executed(); - int Read_Global_Variables_from_configfile(const char *prefix); - int Read_MySQL_Users_from_configfile(); - int Read_MySQL_Query_Rules_from_configfile(); - int Read_MySQL_Servers_from_configfile(); - int Read_Scheduler_from_configfile(); - int Read_ProxySQL_Servers_from_configfile(); + ProxySQL_Config& proxysql_config(); void flush_error_log(); bool GenericRefreshStatistics(const char *query_no_space, unsigned int query_no_space_length, bool admin); diff --git a/include/proxysql_config.h b/include/proxysql_config.h new file mode 100644 index 000000000..1c88f9144 --- /dev/null +++ b/include/proxysql_config.h @@ -0,0 +1,30 @@ +#ifndef __PROXYSQL_CONFIG_H__ +#define __PROXYSQL_CONFIG_H__ + +#include + +class SQLite3DB; + +class ProxySQL_Config { + SQLite3DB* admindb; +public: + ProxySQL_Config(SQLite3DB* db); + virtual ~ProxySQL_Config(); + + int Read_Global_Variables_from_configfile(const char *prefix); + int Read_MySQL_Users_from_configfile(); + int Read_MySQL_Query_Rules_from_configfile(); + int Read_MySQL_Servers_from_configfile(); + int Read_Scheduler_from_configfile(); + int Read_ProxySQL_Servers_from_configfile(); + + void addField(std::string& data, const char* name, const char* value, const char* dq="\""); + int Write_Global_Variables_to_configfile(std::string& data); + int Write_MySQL_Users_to_configfile(std::string& data); + int Write_MySQL_Query_Rules_to_configfile(std::string& data); + int Write_MySQL_Servers_to_configfile(std::string& data); + int Write_Scheduler_to_configfile(std::string& data); + int Write_ProxySQL_Servers_to_configfile(std::string& data); +}; + +#endif diff --git a/lib/Makefile b/lib/Makefile index fd38ed041..2159404d5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -95,7 +95,7 @@ default: libproxysql.a _OBJ = c_tokenizer.o OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) -_OBJ_CXX = ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo SQLite3_Server.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo +_OBJ_CXX = ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo SQLite3_Server.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo OBJ_CXX = $(patsubst %,$(ODIR)/%,$(_OBJ_CXX)) HEADERS = ../include/*.h ../include/*.hpp diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index dabef4654..c1036c707 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -1,9 +1,11 @@ #include // std::cout +#include #include // std::sort #include // std::vector #include "re2/re2.h" #include "re2/regexp.h" #include "proxysql.h" +#include "proxysql_config.h" #include "cpp.h" #include "MySQL_Data_Stream.h" @@ -100,6 +102,36 @@ static char * load_file (const char *filename) { return buffer; } +const char* config_header = "########################################################################################\n" + "# This config file is parsed using libconfig , and its grammar is described in:\n" + "# http://www.hyperrealm.com/libconfig/libconfig_manual.html#Configuration-File-Grammar\n" + "# Grammar is also copied at the end of this file\n" + "########################################################################################\n" + "\n" + "########################################################################################\n" + "# IMPORTANT INFORMATION REGARDING THIS CONFIGURATION FILE:\n" + "########################################################################################\n" + "# On startup, ProxySQL reads its config file (if present) to determine its datadir.\n" + "# What happens next depends on if the database file (disk) is present in the defined\n" + "# datadir (i.e. \"/var/lib/proxysql/proxysql.db\").\n" + "#\n" + "# If the database file is found, ProxySQL initializes its in-memory configuration from\n" + "# the persisted on-disk database. So, disk configuration gets loaded into memory and\n" + "# then propagated towards the runtime configuration.\n" + "#\n" + "# If the database file is not found and a config file exists, the config file is parsed\n" + "# and its content is loaded into the in-memory database, to then be both saved on-disk\n" + "# database and loaded at runtime.\n" + "#\n" + "# IMPORTANT: If a database file is found, the config file is NOT parsed. In this case\n" + "# ProxySQL initializes its in-memory configuration from the persisted on-disk\n" + "# database ONLY. In other words, the configuration found in the proxysql.cnf\n" + "# file is only used to initial the on-disk database read on the first startup.\n" + "#\n" + "# In order to FORCE a re-initialise of the on-disk database from the configuration file\n" + "# the ProxySQL service should be started with \"service proxysql initial\".\n" + "#\n" + "########################################################################################\n"; /* int sqlite3_json_init( @@ -558,6 +590,11 @@ int ProxySQL_Test___GetDigestTable(bool reset, bool use_swap) { return r; } +ProxySQL_Config& ProxySQL_Admin::proxysql_config() { + static ProxySQL_Config instance = ProxySQL_Config(admindb); + return instance; +} + int ProxySQL_Admin::FlushDigestTableToDisk(SQLite3DB *_db) { int r = 0; if (!GloQPro) return 0; @@ -1348,7 +1385,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if (GloVars.confFile->OpenFile(NULL)==true) { ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; int rows=0; - rows=SPA->Read_Scheduler_from_configfile(); + rows=SPA->proxysql_config().Read_Scheduler_from_configfile(); proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded scheduler from CONFIG\n"); SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL, rows); GloVars.confFile->CloseFile(); @@ -1567,7 +1604,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if (GloVars.confFile->OpenFile(NULL)==true) { ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; int rows=0; - rows=SPA->Read_MySQL_Users_from_configfile(); + rows=SPA->proxysql_config().Read_MySQL_Users_from_configfile(); proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql users from CONFIG\n"); SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL, rows); GloVars.confFile->CloseFile(); @@ -1945,7 +1982,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if (GloVars.confFile->OpenFile(NULL)==true) { int rows=0; ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - rows=SPA->Read_Global_Variables_from_configfile("mysql"); + rows=SPA->proxysql_config().Read_Global_Variables_from_configfile("mysql"); proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql variables from CONFIG\n"); SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL, rows); GloVars.confFile->CloseFile(); @@ -2043,7 +2080,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if (GloVars.confFile->OpenFile(NULL)==true) { ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; int rows=0; - rows=SPA->Read_MySQL_Servers_from_configfile(); + rows=SPA->proxysql_config().Read_MySQL_Servers_from_configfile(); proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql servers from CONFIG\n"); SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL, rows); GloVars.confFile->CloseFile(); @@ -2159,7 +2196,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if (GloVars.confFile->OpenFile(NULL)==true) { ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; int rows=0; - rows=SPA->Read_ProxySQL_Servers_from_configfile(); + rows=SPA->proxysql_config().Read_ProxySQL_Servers_from_configfile(); proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded ProxySQL servers from CONFIG\n"); SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL, rows); GloVars.confFile->CloseFile(); @@ -2306,7 +2343,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if (GloVars.confFile->OpenFile(NULL)==true) { ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; int rows=0; - rows=SPA->Read_MySQL_Query_Rules_from_configfile(); + 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_MySQL_OK(&sess->client_myds->myprot, NULL, rows); GloVars.confFile->CloseFile(); @@ -2436,7 +2473,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if (GloVars.confFile->OpenFile(NULL)==true) { int rows=0; ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - rows=SPA->Read_Global_Variables_from_configfile("admin"); + rows=SPA->proxysql_config().Read_Global_Variables_from_configfile("admin"); proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded admin variables from CONFIG\n"); SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL, rows); GloVars.confFile->CloseFile(); @@ -3785,6 +3822,81 @@ void admin_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } + if (!strncasecmp("SELECT CONFIG TO", query_no_space, strlen("SELECT CONFIG TO"))) { + std::string fileName = query_no_space + strlen("SELECT CONFIG TO"); + 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_Scheduler_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_MySQL_OK(&sess->client_myds->myprot, (char*)ss.str().c_str(), data.size()); + } + } 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_Scheduler_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 } @@ -4804,13 +4916,13 @@ bool ProxySQL_Admin::init() { if (GloVars.__cmd_proxysql_reload || GloVars.__cmd_proxysql_initial || admindb_file_exists==false) { // see #617 if (GloVars.configfile_open) { - Read_MySQL_Servers_from_configfile(); - Read_MySQL_Users_from_configfile(); - Read_MySQL_Query_Rules_from_configfile(); - Read_Global_Variables_from_configfile("admin"); - Read_Global_Variables_from_configfile("mysql"); - Read_Scheduler_from_configfile(); - Read_ProxySQL_Servers_from_configfile(); + 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_Scheduler_from_configfile(); + proxysql_config().Read_ProxySQL_Servers_from_configfile(); __insert_or_replace_disktable_select_maintable(); } } @@ -9764,723 +9876,6 @@ char * ProxySQL_Admin::load_mysql_query_rules_to_runtime() { return NULL; } -int ProxySQL_Admin::Read_Global_Variables_from_configfile(const char *prefix) { - const Setting& root = GloVars.confFile->cfg.getRoot(); - char *groupname=(char *)malloc(strlen(prefix)+strlen((char *)"_variables")+1); - sprintf(groupname,"%s%s",prefix,"_variables"); - if (root.exists(groupname)==false) { - free(groupname); - return 0; - } - const Setting &group = root[(const char *)groupname]; - int count = group.getLength(); - //fprintf(stderr, "Found %d %s_variables\n",count, prefix); - int i; - admindb->execute("PRAGMA foreign_keys = OFF"); - char *q=(char *)"INSERT OR REPLACE INTO global_variables VALUES (\"%s-%s\", \"%s\")"; - for (i=0; i< count; i++) { - const Setting &sett = group[i]; - const char *n=sett.getName(); - bool value_bool; - int value_int; - std::string value_string=""; - if (group.lookupValue(n, value_bool)) { - value_string = (value_bool ? "true" : "false"); - } else { - if (group.lookupValue(n, value_int)) { - value_string = std::to_string(value_int); - } else { - group.lookupValue(n, value_string); - } - } - //fprintf(stderr,"%s = %s\n", n, value_string.c_str()); - char *query=(char *)malloc(strlen(q)+strlen(prefix)+strlen(n)+strlen(value_string.c_str())); - sprintf(query,q, prefix, n, value_string.c_str()); - //fprintf(stderr, "%s\n", query); - admindb->execute(query); - free(query); - } - admindb->execute("PRAGMA foreign_keys = ON"); - free(groupname); - return i; -} - -int ProxySQL_Admin::Read_MySQL_Users_from_configfile() { - const Setting& root = GloVars.confFile->cfg.getRoot(); - if (root.exists("mysql_users")==false) return 0; - const Setting &mysql_users = root["mysql_users"]; - int count = mysql_users.getLength(); - //fprintf(stderr, "Found %d users\n",count); - int i; - int rows=0; - admindb->execute("PRAGMA foreign_keys = OFF"); - char *q=(char *)"INSERT OR REPLACE INTO mysql_users (username, password, active, use_ssl, default_hostgroup, default_schema, schema_locked, transaction_persistent, fast_forward, max_connections, comment) VALUES ('%s', '%s', %d, %d, %d, '%s', %d, %d, %d, %d, '%s')"; - for (i=0; i< count; i++) { - const Setting &user = mysql_users[i]; - std::string username; - std::string password=""; - int active=1; - int use_ssl=0; - int default_hostgroup=0; - std::string default_schema=""; - int schema_locked=0; - int transaction_persistent=1; - int fast_forward=0; - int max_connections=10000; - std::string comment=""; - if (user.lookupValue("username", username)==false) continue; - user.lookupValue("password", password); - user.lookupValue("default_hostgroup", default_hostgroup); - user.lookupValue("active", active); - user.lookupValue("use_ssl", use_ssl); - //if (user.lookupValue("default_schema", default_schema)==false) default_schema=""; - user.lookupValue("default_schema", default_schema); - user.lookupValue("schema_locked", schema_locked); - user.lookupValue("transaction_persistent", transaction_persistent); - user.lookupValue("fast_forward", fast_forward); - user.lookupValue("max_connections", max_connections); - user.lookupValue("comment", comment); - char *o1=strdup(comment.c_str()); - char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(username.c_str())+strlen(password.c_str())+strlen(o)+128); - sprintf(query,q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, default_schema.c_str(), schema_locked, transaction_persistent, fast_forward, max_connections, o); - admindb->execute(query); - if (o!=o1) free(o); - free(o1); - free(query); - rows++; - } - admindb->execute("PRAGMA foreign_keys = ON"); - return rows; -} - -int ProxySQL_Admin::Read_Scheduler_from_configfile() { - const Setting& root = GloVars.confFile->cfg.getRoot(); - if (root.exists("scheduler")==false) return 0; - const Setting &schedulers = root["scheduler"]; - int count = schedulers.getLength(); - //fprintf(stderr, "Found %d users\n",count); - int i; - int rows=0; - admindb->execute("PRAGMA foreign_keys = OFF"); - char *q=(char *)"INSERT OR REPLACE INTO scheduler (id, active, interval_ms, filename, arg1, arg2, arg3, arg4, arg5, comment) VALUES (%d, %d, %d, '%s', %s, %s, %s, %s, %s, '%s')"; - for (i=0; i< count; i++) { - const Setting &sched = schedulers[i]; - int id; - int active=1; - - std::string filename; - - bool arg1_exists=false; - std::string arg1; - bool arg2_exists=false; - std::string arg2; - bool arg3_exists=false; - std::string arg3; - bool arg4_exists=false; - std::string arg4; - bool arg5_exists=false; - std::string arg5; - - // variable for parsing interval_ms - int interval_ms=0; - - std::string comment=""; - - // validate arguments - if (sched.lookupValue("id", id)==false) continue; - sched.lookupValue("active", active); - sched.lookupValue("interval_ms", interval_ms); - if (sched.lookupValue("filename", filename)==false) continue; - if (sched.lookupValue("arg1", arg1)) arg1_exists=true; - if (sched.lookupValue("arg2", arg2)) arg2_exists=true; - if (sched.lookupValue("arg3", arg3)) arg3_exists=true; - if (sched.lookupValue("arg4", arg4)) arg4_exists=true; - if (sched.lookupValue("arg5", arg5)) arg5_exists=true; - sched.lookupValue("comment", comment); - - - int query_len=0; - query_len+=strlen(q) + - strlen(std::to_string(id).c_str()) + - strlen(std::to_string(active).c_str()) + - strlen(std::to_string(interval_ms).c_str()) + - strlen(filename.c_str()) + - ( arg1_exists ? strlen(arg1.c_str()) : 0 ) + 4 + - ( arg2_exists ? strlen(arg2.c_str()) : 0 ) + 4 + - ( arg3_exists ? strlen(arg3.c_str()) : 0 ) + 4 + - ( arg4_exists ? strlen(arg4.c_str()) : 0 ) + 4 + - ( arg5_exists ? strlen(arg5.c_str()) : 0 ) + 4 + - strlen(comment.c_str()) + - 40; - char *query=(char *)malloc(query_len); - if (arg1_exists) - arg1="\'" + arg1 + "\'"; - else - arg1 = "NULL"; - if (arg2_exists) - arg2="\'" + arg2 + "\'"; - else - arg2 = "NULL"; - if (arg3_exists) - arg3="\'" + arg3 + "\'"; - else - arg3 = "NULL"; - if (arg4_exists) - arg4="\'" + arg4 + "\'"; - else - arg4 = "NULL"; - if (arg5_exists) - arg5="\'" + arg5 + "\'"; - else - arg5 = "NULL"; - - sprintf(query, q, - id, active, - interval_ms, - filename.c_str(), - arg1.c_str(), - arg2.c_str(), - arg3.c_str(), - arg4.c_str(), - arg5.c_str(), - comment.c_str() - ); - admindb->execute(query); - free(query); - rows++; - } - admindb->execute("PRAGMA foreign_keys = ON"); - return rows; -} - -int ProxySQL_Admin::Read_MySQL_Query_Rules_from_configfile() { - const Setting& root = GloVars.confFile->cfg.getRoot(); - if (root.exists("mysql_query_rules")==false) return 0; - const Setting &mysql_query_rules = root["mysql_query_rules"]; - int count = mysql_query_rules.getLength(); - //fprintf(stderr, "Found %d users\n",count); - int i; - int rows=0; - admindb->execute("PRAGMA foreign_keys = OFF"); - char *q=(char *)"INSERT OR REPLACE INTO mysql_query_rules (rule_id, active, username, schemaname, flagIN, client_addr, proxy_addr, proxy_port, digest, match_digest, match_pattern, negate_match_pattern, re_modifiers, flagOUT, replace_pattern, destination_hostgroup, cache_ttl, cache_empty_result, cache_timeout, reconnect, timeout, retries, delay, next_query_flagIN, mirror_flagOUT, mirror_hostgroup, error_msg, ok_msg, sticky_conn, multiplex, gtid_from_hostgroup, log, apply, comment) VALUES (%d, %d, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s)"; - for (i=0; i< count; i++) { - const Setting &rule = mysql_query_rules[i]; - int rule_id; - int active=1; - bool username_exists=false; - std::string username; - bool schemaname_exists=false; - std::string schemaname; - int flagIN=0; - - // variables for parsing client_addr - bool client_addr_exists=false; - std::string client_addr; - - // variables for parsing proxy_addr - bool proxy_addr_exists=false; - std::string proxy_addr; - - // variable for parsing proxy_port - int proxy_port=-1; - - // variables for parsing digest - bool digest_exists=false; - std::string digest; - - - bool match_digest_exists=false; - std::string match_digest; - bool match_pattern_exists=false; - std::string match_pattern; - int negate_match_pattern=0; - - bool re_modifiers_exists=false; - std::string re_modifiers; - - int flagOUT=-1; - bool replace_pattern_exists=false; - std::string replace_pattern; - int destination_hostgroup=-1; - int next_query_flagIN=-1; - int mirror_flagOUT=-1; - int mirror_hostgroup=-1; - int cache_ttl=-1; - int cache_empty_result=-1; - int cache_timeout=-1; - int reconnect=-1; - int timeout=-1; - int retries=-1; - int delay=-1; - bool error_msg_exists=false; - std::string error_msg; - bool OK_msg_exists=false; - std::string OK_msg; - - int sticky_conn=-1; - int multiplex=-1; - int gtid_from_hostgroup = -1; - - // variable for parsing log - int log=-1; - - int apply=0; - - bool comment_exists=false; - std::string comment; - - // validate arguments - if (rule.lookupValue("rule_id", rule_id)==false) { - proxy_error("Admin: detected a mysql_query_rules in config file without a mandatory rule_id\n"); - continue; - } - rule.lookupValue("active", active); - if (rule.lookupValue("username", username)) username_exists=true; - if (rule.lookupValue("schemaname", schemaname)) schemaname_exists=true; - rule.lookupValue("flagIN", flagIN); - - if (rule.lookupValue("client_addr", client_addr)) client_addr_exists=true; - if (rule.lookupValue("proxy_addr", proxy_addr)) proxy_addr_exists=true; - rule.lookupValue("proxy_port", proxy_port); - if (rule.lookupValue("digest", digest)) digest_exists=true; - - if (rule.lookupValue("match_digest", match_digest)) match_digest_exists=true; - if (rule.lookupValue("match_pattern", match_pattern)) match_pattern_exists=true; - rule.lookupValue("negate_match_pattern", negate_match_pattern); - if (rule.lookupValue("re_modifiers", re_modifiers)) { - } else { - re_modifiers = "CASELESS"; - } - re_modifiers_exists=true; - rule.lookupValue("flagOUT", flagOUT); - if (rule.lookupValue("replace_pattern", replace_pattern)) replace_pattern_exists=true; - rule.lookupValue("destination_hostgroup", destination_hostgroup); - rule.lookupValue("next_query_flagIN", next_query_flagIN); - rule.lookupValue("mirror_flagOUT", mirror_flagOUT); - rule.lookupValue("mirror_hostgroup", mirror_hostgroup); - rule.lookupValue("cache_ttl", cache_ttl); - rule.lookupValue("cache_empty_result", cache_empty_result); - rule.lookupValue("cache_timeout", cache_timeout); - rule.lookupValue("reconnect", reconnect); - rule.lookupValue("timeout", timeout); - rule.lookupValue("retries", retries); - rule.lookupValue("delay", delay); - - if (rule.lookupValue("error_msg", error_msg)) error_msg_exists=true; - if (rule.lookupValue("OK_msg", OK_msg)) OK_msg_exists=true; - - rule.lookupValue("sticky_conn", sticky_conn); - rule.lookupValue("multiplex", multiplex); - rule.lookupValue("gtid_from_hostgroup", gtid_from_hostgroup); - - rule.lookupValue("log", log); - - rule.lookupValue("apply", apply); - if (rule.lookupValue("comment", comment)) comment_exists=true; - - - //if (user.lookupValue("default_schema", default_schema)==false) default_schema=""; - int query_len=0; - query_len+=strlen(q) + - strlen(std::to_string(rule_id).c_str()) + - strlen(std::to_string(active).c_str()) + - ( username_exists ? strlen(username.c_str()) : 0 ) + 4 + - ( schemaname_exists ? strlen(schemaname.c_str()) : 0 ) + 4 + - strlen(std::to_string(flagIN).c_str()) + 4 + - - ( client_addr_exists ? strlen(client_addr.c_str()) : 0 ) + 4 + - ( proxy_addr_exists ? strlen(proxy_addr.c_str()) : 0 ) + 4 + - strlen(std::to_string(proxy_port).c_str()) + 4 + - - ( match_digest_exists ? strlen(match_digest.c_str()) : 0 ) + 4 + - ( match_pattern_exists ? strlen(match_pattern.c_str()) : 0 ) + 4 + - strlen(std::to_string(negate_match_pattern).c_str()) + 4 + - ( re_modifiers_exists ? strlen(re_modifiers.c_str()) : 0 ) + 4 + - strlen(std::to_string(flagOUT).c_str()) + 4 + - ( replace_pattern_exists ? strlen(replace_pattern.c_str()) : 0 ) + 4 + - strlen(std::to_string(destination_hostgroup).c_str()) + 4 + - strlen(std::to_string(cache_ttl).c_str()) + 4 + - strlen(std::to_string(cache_empty_result).c_str()) + 4 + - strlen(std::to_string(cache_timeout).c_str()) + 4 + - strlen(std::to_string(reconnect).c_str()) + 4 + - strlen(std::to_string(timeout).c_str()) + 4 + - strlen(std::to_string(next_query_flagIN).c_str()) + 4 + - strlen(std::to_string(mirror_flagOUT).c_str()) + 4 + - strlen(std::to_string(mirror_hostgroup).c_str()) + 4 + - strlen(std::to_string(retries).c_str()) + 4 + - strlen(std::to_string(delay).c_str()) + 4 + - ( error_msg_exists ? strlen(error_msg.c_str()) : 0 ) + 4 + - ( OK_msg_exists ? strlen(OK_msg.c_str()) : 0 ) + 4 + - strlen(std::to_string(sticky_conn).c_str()) + 4 + - strlen(std::to_string(multiplex).c_str()) + 4 + - strlen(std::to_string(gtid_from_hostgroup).c_str()) + 4 + - strlen(std::to_string(log).c_str()) + 4 + - strlen(std::to_string(apply).c_str()) + 4 + - ( comment_exists ? strlen(comment.c_str()) : 0 ) + 4 + - 64; - char *query=(char *)malloc(query_len); - if (username_exists) - username="\"" + username + "\""; - else - username = "NULL"; - if (schemaname_exists) - schemaname="\"" + schemaname + "\""; - else - schemaname = "NULL"; - - if (client_addr_exists) - client_addr="\"" + client_addr + "\""; - else - client_addr = "NULL"; - if (proxy_addr_exists) - proxy_addr="\"" + proxy_addr + "\""; - else - proxy_addr = "NULL"; - if (digest_exists) - digest="\"" + digest + "\""; - else - digest = "NULL"; - - if (match_digest_exists) - match_digest="\"" + match_digest + "\""; - else - match_digest = "NULL"; - if (match_pattern_exists) - match_pattern="\"" + match_pattern + "\""; - else - match_pattern = "NULL"; - if (replace_pattern_exists) - replace_pattern="\"" + replace_pattern + "\""; - else - replace_pattern = "NULL"; - if (error_msg_exists) - error_msg="\"" + error_msg + "\""; - else - error_msg = "NULL"; - if (OK_msg_exists) - OK_msg="\"" + OK_msg + "\""; - else - OK_msg = "NULL"; - if (re_modifiers_exists) - re_modifiers="\"" + re_modifiers + "\""; - else - re_modifiers = "NULL"; - if (comment_exists) - comment="\"" + comment + "\""; - else - comment = "NULL"; - - - sprintf(query, q, - rule_id, active, - username.c_str(), - schemaname.c_str(), - ( flagIN >= 0 ? std::to_string(flagIN).c_str() : "NULL") , - client_addr.c_str(), - proxy_addr.c_str(), - ( proxy_port >= 0 ? std::to_string(proxy_port).c_str() : "NULL") , - digest.c_str(), - match_digest.c_str(), - match_pattern.c_str(), - ( negate_match_pattern == 0 ? 0 : 1) , - re_modifiers.c_str(), - ( flagOUT >= 0 ? std::to_string(flagOUT).c_str() : "NULL") , - replace_pattern.c_str(), - ( destination_hostgroup >= 0 ? std::to_string(destination_hostgroup).c_str() : "NULL") , - ( cache_ttl >= 0 ? std::to_string(cache_ttl).c_str() : "NULL") , - ( cache_empty_result >= 0 ? std::to_string(cache_empty_result).c_str() : "NULL") , - ( cache_timeout >= 0 ? std::to_string(cache_timeout).c_str() : "NULL") , - ( reconnect >= 0 ? std::to_string(reconnect).c_str() : "NULL") , - ( timeout >= 0 ? std::to_string(timeout).c_str() : "NULL") , - ( retries >= 0 ? std::to_string(retries).c_str() : "NULL") , - ( delay >= 0 ? std::to_string(delay).c_str() : "NULL") , - ( next_query_flagIN >= 0 ? std::to_string(next_query_flagIN).c_str() : "NULL") , - ( mirror_flagOUT >= 0 ? std::to_string(mirror_flagOUT).c_str() : "NULL") , - ( mirror_hostgroup >= 0 ? std::to_string(mirror_hostgroup).c_str() : "NULL") , - error_msg.c_str(), - OK_msg.c_str(), - ( sticky_conn >= 0 ? std::to_string(sticky_conn).c_str() : "NULL") , - ( multiplex >= 0 ? std::to_string(multiplex).c_str() : "NULL") , - ( gtid_from_hostgroup >= 0 ? std::to_string(gtid_from_hostgroup).c_str() : "NULL") , - ( log >= 0 ? std::to_string(log).c_str() : "NULL") , - ( apply == 0 ? 0 : 1) , - comment.c_str() - ); - //fprintf(stderr, "%s\n", query); - admindb->execute(query); - free(query); - rows++; - } - admindb->execute("PRAGMA foreign_keys = ON"); - return rows; -} - -int ProxySQL_Admin::Read_MySQL_Servers_from_configfile() { - const Setting& root = GloVars.confFile->cfg.getRoot(); - int i; - int rows=0; - admindb->execute("PRAGMA foreign_keys = OFF"); - if (root.exists("mysql_servers")==true) { - const Setting &mysql_servers = root["mysql_servers"]; - int count = mysql_servers.getLength(); - //fprintf(stderr, "Found %d servers\n",count); - char *q=(char *)"INSERT OR REPLACE INTO mysql_servers (hostname, port, gtid_port, hostgroup_id, compression, weight, status, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment) VALUES (\"%s\", %d, %d, %d, %d, %d, \"%s\", %d, %d, %d, %d, '%s')"; - for (i=0; i< count; i++) { - const Setting &server = mysql_servers[i]; - std::string address; - std::string status="ONLINE"; - int port=3306; - int gtid_port=0; - int hostgroup; - int weight=1; - int compression=0; - int max_connections=1000; // default - int max_replication_lag=0; // default - int use_ssl=0; - int max_latency_ms=0; - std::string comment=""; - if (server.lookupValue("address", address)==false) { - if (server.lookupValue("hostname", address)==false) { - continue; - } - } - server.lookupValue("port", port); - server.lookupValue("gtid_port", gtid_port); - if (server.lookupValue("hostgroup", hostgroup)==false) { - if (server.lookupValue("hostgroup_id", hostgroup)==false) { - continue; - } - } - server.lookupValue("status", status); - if ( - (strcasecmp(status.c_str(),(char *)"ONLINE")) - && (strcasecmp(status.c_str(),(char *)"SHUNNED")) - && (strcasecmp(status.c_str(),(char *)"OFFLINE_SOFT")) - && (strcasecmp(status.c_str(),(char *)"OFFLINE_HARD")) - ) { - status="ONLINE"; - } - server.lookupValue("compression", compression); - server.lookupValue("weight", weight); - server.lookupValue("max_connections", max_connections); - server.lookupValue("max_replication_lag", max_replication_lag); - server.lookupValue("use_ssl", use_ssl); - server.lookupValue("max_latency_ms", max_latency_ms); - server.lookupValue("comment", comment); - char *o1=strdup(comment.c_str()); - char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(status.c_str())+strlen(address.c_str())+strlen(o)+128); - sprintf(query,q, address.c_str(), port, gtid_port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, o); - //fprintf(stderr, "%s\n", query); - admindb->execute(query); - if (o!=o1) free(o); - free(o1); - free(query); - rows++; - } - } - if (root.exists("mysql_replication_hostgroups")==true) { - const Setting &mysql_replication_hostgroups = root["mysql_replication_hostgroups"]; - int count = mysql_replication_hostgroups.getLength(); - char *q=(char *)"INSERT OR REPLACE INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, comment, check_type) VALUES (%d, %d, '%s', '%s')"; - for (i=0; i< count; i++) { - const Setting &line = mysql_replication_hostgroups[i]; - int writer_hostgroup; - int reader_hostgroup; - std::string comment=""; - std::string check_type=""; - if (line.lookupValue("writer_hostgroup", writer_hostgroup)==false) continue; - if (line.lookupValue("reader_hostgroup", reader_hostgroup)==false) continue; - line.lookupValue("comment", comment); - char *o1=strdup(comment.c_str()); - char *o=escape_string_single_quotes(o1, false); - line.lookupValue("check_type", check_type); - if ( - (strcasecmp(check_type.c_str(),(char *)"read_only")) - && (strcasecmp(check_type.c_str(),(char *)"innodb_read_only")) - && (strcasecmp(check_type.c_str(),(char *)"super_read_only")) - ) { - check_type="read_only"; - } - char *t1=strdup(check_type.c_str()); - char *t=escape_string_single_quotes(t1, false); - char *query=(char *)malloc(strlen(q)+strlen(o)+strlen(t)+32); - sprintf(query,q, writer_hostgroup, reader_hostgroup, o, t); - //fprintf(stderr, "%s\n", query); - admindb->execute(query); - if (o!=o1) free(o); - free(o1); - if (t!=t1) free(t); - free(t1); - free(query); - rows++; - } - } - if (root.exists("mysql_group_replication_hostgroups")==true) { - const Setting &mysql_group_replication_hostgroups = root["mysql_group_replication_hostgroups"]; - int count = mysql_group_replication_hostgroups.getLength(); - char *q=(char *)"INSERT OR REPLACE INTO mysql_group_replication_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, comment) VALUES (%d, %d, %d, %d, %d, %d, %d, %d, '%s')"; - for (i=0; i< count; i++) { - const Setting &line = mysql_group_replication_hostgroups[i]; - int writer_hostgroup; - int backup_writer_hostgroup; - int reader_hostgroup; - int offline_hostgroup; - int active=1; // default - int max_writers; - int writer_is_also_reader; - int max_transactions_behind; - std::string comment=""; - if (line.lookupValue("writer_hostgroup", writer_hostgroup)==false) continue; - if (line.lookupValue("backup_writer_hostgroup", backup_writer_hostgroup)==false) continue; - if (line.lookupValue("reader_hostgroup", reader_hostgroup)==false) continue; - if (line.lookupValue("offline_hostgroup", offline_hostgroup)==false) continue; - if (line.lookupValue("max_writers", max_writers)==false) max_writers=1; - if (line.lookupValue("writer_is_also_reader", writer_is_also_reader)==false) writer_is_also_reader=0; - if (line.lookupValue("max_transactions_behind", max_transactions_behind)==false) max_transactions_behind=0; - line.lookupValue("comment", comment); - char *o1=strdup(comment.c_str()); - char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(o)+128); // 128 vs sizeof(int)*8 - sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, o); - //fprintf(stderr, "%s\n", query); - admindb->execute(query); - if (o!=o1) free(o); - free(o1); - free(query); - rows++; - } - } - if (root.exists("mysql_galera_hostgroups")==true) { - const Setting &mysql_galera_hostgroups = root["mysql_galera_hostgroups"]; - int count = mysql_galera_hostgroups.getLength(); - char *q=(char *)"INSERT OR REPLACE INTO mysql_galera_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, comment) VALUES (%d, %d, %d, %d, %d, %d, %d, %d, '%s')"; - for (i=0; i< count; i++) { - const Setting &line = mysql_galera_hostgroups[i]; - int writer_hostgroup; - int backup_writer_hostgroup; - int reader_hostgroup; - int offline_hostgroup; - int active=1; // default - int max_writers; - int writer_is_also_reader; - int max_transactions_behind; - std::string comment=""; - if (line.lookupValue("writer_hostgroup", writer_hostgroup)==false) continue; - if (line.lookupValue("backup_writer_hostgroup", backup_writer_hostgroup)==false) continue; - if (line.lookupValue("reader_hostgroup", reader_hostgroup)==false) continue; - if (line.lookupValue("offline_hostgroup", offline_hostgroup)==false) continue; - if (line.lookupValue("max_writers", max_writers)==false) max_writers=1; - if (line.lookupValue("writer_is_also_reader", writer_is_also_reader)==false) writer_is_also_reader=0; - if (line.lookupValue("max_transactions_behind", max_transactions_behind)==false) max_transactions_behind=0; - line.lookupValue("comment", comment); - char *o1=strdup(comment.c_str()); - char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(o)+128); // 128 vs sizeof(int)*8 - sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, o); - //fprintf(stderr, "%s\n", query); - admindb->execute(query); - if (o!=o1) free(o); - free(o1); - free(query); - rows++; - } - } - if (root.exists("mysql_aws_aurora_hostgroups")==true) { - const Setting &mysql_aws_aurora_hostgroups = root["mysql_aws_aurora_hostgroups"]; - int count = mysql_aws_aurora_hostgroups.getLength(); - char *q=(char *)"INSERT OR REPLACE INTO mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, aurora_port, domain_name, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, comment ) VALUES (%d, %d, %d, %d, '%s', %d, %d, %d, %d, %d, %d, %d, %d, '%s')"; - for (i=0; i< count; i++) { - const Setting &line = mysql_aws_aurora_hostgroups[i]; - int writer_hostgroup; - int reader_hostgroup; - int active=1; // default - int aurora_port; - int max_lag_ms; - int add_lag_ms; - int min_lag_ms; - int lag_num_checks; - int check_interval_ms; - int check_timeout_ms; - int writer_is_also_reader; - int new_reader_weight; - std::string comment=""; - std::string domain_name=""; - if (line.lookupValue("writer_hostgroup", writer_hostgroup)==false) continue; - if (line.lookupValue("reader_hostgroup", reader_hostgroup)==false) continue; - if (line.lookupValue("aurora_port", aurora_port)==false) aurora_port=3306; - if (line.lookupValue("max_lag_ms", max_lag_ms)==false) max_lag_ms=600000; - if (line.lookupValue("check_interval_ms", check_interval_ms)==false) check_interval_ms=1000; - if (line.lookupValue("check_timeout_ms", check_timeout_ms)==false) check_timeout_ms=1000; - if (line.lookupValue("writer_is_also_reader", writer_is_also_reader)==false) writer_is_also_reader=0; - if (line.lookupValue("new_reader_weight", new_reader_weight)==false) new_reader_weight=1; - if (line.lookupValue("add_lag_ms", add_lag_ms)==false) add_lag_ms=30; - if (line.lookupValue("min_lag_ms", min_lag_ms)==false) min_lag_ms=30; - if (line.lookupValue("lag_num_checks", lag_num_checks)==false) lag_num_checks=1; - line.lookupValue("comment", comment); - line.lookupValue("domain_name", domain_name); - char *o1=strdup(comment.c_str()); - char *o=escape_string_single_quotes(o1, false); - char *p1=strdup(domain_name.c_str()); - char *p=escape_string_single_quotes(p1, false); - char *query=(char *)malloc(strlen(q)+strlen(o)+strlen(p)+256); // 128 vs sizeof(int)*8 - sprintf(query,q, writer_hostgroup, reader_hostgroup, active, aurora_port, p, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, o); - //fprintf(stderr, "%s\n", query); - admindb->execute(query); - if (o!=o1) free(o); - free(o1); - if (p!=p1) free(p); - free(p1); - free(query); - rows++; - } - } - admindb->execute("PRAGMA foreign_keys = ON"); - return rows; -} - -int ProxySQL_Admin::Read_ProxySQL_Servers_from_configfile() { - const Setting& root = GloVars.confFile->cfg.getRoot(); - int i; - int rows=0; - admindb->execute("PRAGMA foreign_keys = OFF"); - if (root.exists("proxysql_servers")==true) { - const Setting &mysql_servers = root["proxysql_servers"]; - int count = mysql_servers.getLength(); - //fprintf(stderr, "Found %d servers\n",count); - char *q=(char *)"INSERT OR REPLACE INTO proxysql_servers (hostname, port, weight, comment) VALUES (\"%s\", %d, %d, '%s')"; - for (i=0; i< count; i++) { - const Setting &server = mysql_servers[i]; - std::string address; - int port; - int weight=0; - std::string comment=""; - if (server.lookupValue("address", address)==false) { - if (server.lookupValue("hostname", address)==false) { - continue; - } - } - if (server.lookupValue("port", port)==false) continue; - server.lookupValue("weight", weight); - server.lookupValue("comment", comment); - char *o1=strdup(comment.c_str()); - char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(address.c_str())+strlen(o)+128); - sprintf(query, q, address.c_str(), port, weight, o); - proxy_info("Cluster: Adding ProxySQL Servers %s:%d from config file\n", address.c_str(), port); - //fprintf(stderr, "%s\n", query); - admindb->execute(query); - if (o!=o1) free(o); - free(o1); - free(query); - rows++; - } - } - admindb->execute("PRAGMA foreign_keys = ON"); - return rows; -} - extern "C" ProxySQL_Admin * create_ProxySQL_Admin_func() { return new ProxySQL_Admin(); } diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp new file mode 100644 index 000000000..62b1828ca --- /dev/null +++ b/lib/ProxySQL_Config.cpp @@ -0,0 +1,1204 @@ +#include "proxysql_config.h" +#include "proxysql.h" +#include "cpp.h" + +#include + +ProxySQL_Config::ProxySQL_Config(SQLite3DB* db) { + assert(db); + + admindb = db; +} + +ProxySQL_Config:: ~ProxySQL_Config() { +} + +void ProxySQL_Config::addField(std::string& data, const char* name, const char* value, const char* dq) { + std::stringstream ss; + if (!value || !strlen(value)) return; + ss << "\t\t" << name << "=" << dq << value << dq << "\n"; + data += ss.str(); +} + +int ProxySQL_Config::Read_Global_Variables_from_configfile(const char *prefix) { + const Setting& root = GloVars.confFile->cfg.getRoot(); + char *groupname=(char *)malloc(strlen(prefix)+strlen((char *)"_variables")+1); + sprintf(groupname,"%s%s",prefix,"_variables"); + if (root.exists(groupname)==false) { + free(groupname); + return 0; + } + const Setting &group = root[(const char *)groupname]; + int count = group.getLength(); + //fprintf(stderr, "Found %d %s_variables\n",count, prefix); + int i; + admindb->execute("PRAGMA foreign_keys = OFF"); + char *q=(char *)"INSERT OR REPLACE INTO global_variables VALUES (\"%s-%s\", \"%s\")"; + for (i=0; i< count; i++) { + const Setting &sett = group[i]; + const char *n=sett.getName(); + bool value_bool; + int value_int; + std::string value_string=""; + if (group.lookupValue(n, value_bool)) { + value_string = (value_bool ? "true" : "false"); + } else { + if (group.lookupValue(n, value_int)) { + value_string = std::to_string(value_int); + } else { + group.lookupValue(n, value_string); + } + } + //fprintf(stderr,"%s = %s\n", n, value_string.c_str()); + char *query=(char *)malloc(strlen(q)+strlen(prefix)+strlen(n)+strlen(value_string.c_str())); + sprintf(query,q, prefix, n, value_string.c_str()); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + free(query); + } + admindb->execute("PRAGMA foreign_keys = ON"); + free(groupname); + return i; +} + +int ProxySQL_Config::Write_MySQL_Users_to_configfile(std::string& data) { + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* sqlite_resultset = NULL; + + char *query=(char *)"SELECT * FROM mysql_users"; + admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); + if (error) { + proxy_error("Error on read from mysql_users: %s\n", error); + return -1; + } else { + if (sqlite_resultset) { + data += "mysql_users:\n(\n"; + bool isNext = false; + for (auto r : sqlite_resultset->rows) { + if (isNext) + data += ",\n"; + data += "\t{\n"; + addField(data, "username", r->fields[0]); + addField(data, "password", r->fields[1]); + addField(data, "active", r->fields[2], ""); + addField(data, "use_ssl", r->fields[3], ""); + addField(data, "default_hostgroup", r->fields[4], ""); + addField(data, "default_schema", r->fields[5]); + addField(data, "schema_locked", r->fields[6], ""); + addField(data, "transaction_persistent", r->fields[7], ""); + addField(data, "fast_forward", r->fields[8], ""); + addField(data, "backend", r->fields[9], ""); + addField(data, "frontend", r->fields[10], ""); + addField(data, "max_connections", r->fields[11], ""); + addField(data, "comment", r->fields[12]); + data += "\t}"; + isNext = true; + } + data += "\n)\n"; + } + } + + if (sqlite_resultset) + delete sqlite_resultset; + + return 0; +} + +int ProxySQL_Config::Read_MySQL_Users_from_configfile() { + const Setting& root = GloVars.confFile->cfg.getRoot(); + if (root.exists("mysql_users")==false) return 0; + const Setting &mysql_users = root["mysql_users"]; + int count = mysql_users.getLength(); + //fprintf(stderr, "Found %d users\n",count); + int i; + int rows=0; + admindb->execute("PRAGMA foreign_keys = OFF"); + char *q=(char *)"INSERT OR REPLACE INTO mysql_users (username, password, active, use_ssl, default_hostgroup, default_schema, schema_locked, transaction_persistent, fast_forward, max_connections, comment) VALUES ('%s', '%s', %d, %d, %d, '%s', %d, %d, %d, %d, '%s')"; + for (i=0; i< count; i++) { + const Setting &user = mysql_users[i]; + std::string username; + std::string password=""; + int active=1; + int use_ssl=0; + int default_hostgroup=0; + std::string default_schema=""; + int schema_locked=0; + int transaction_persistent=1; + int fast_forward=0; + int max_connections=10000; + std::string comment=""; + if (user.lookupValue("username", username)==false) { + proxy_error("Admin: detected a mysql_users in config file without a mandatory username\n"); + continue; + } + user.lookupValue("password", password); + user.lookupValue("default_hostgroup", default_hostgroup); + user.lookupValue("active", active); + user.lookupValue("use_ssl", use_ssl); + //if (user.lookupValue("default_schema", default_schema)==false) default_schema=""; + user.lookupValue("default_schema", default_schema); + user.lookupValue("schema_locked", schema_locked); + user.lookupValue("transaction_persistent", transaction_persistent); + user.lookupValue("fast_forward", fast_forward); + user.lookupValue("max_connections", max_connections); + user.lookupValue("comment", comment); + char *o1=strdup(comment.c_str()); + char *o=escape_string_single_quotes(o1, false); + char *query=(char *)malloc(strlen(q)+strlen(username.c_str())+strlen(password.c_str())+strlen(o)+128); + sprintf(query,q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, default_schema.c_str(), schema_locked, transaction_persistent, fast_forward, max_connections, o); + admindb->execute(query); + if (o!=o1) free(o); + free(o1); + free(query); + rows++; + } + admindb->execute("PRAGMA foreign_keys = ON"); + return rows; +} + +int ProxySQL_Config::Write_Scheduler_to_configfile(std::string& data) { + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* sqlite_resultset = NULL; + + char *query=(char *)"SELECT * FROM scheduler"; + admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); + if (error) { + proxy_error("Error on read from scheduler: %s\n", error); + return -1; + } else { + if (sqlite_resultset) { + data += "scheduler:\n(\n"; + bool isNext = false; + for (auto r : sqlite_resultset->rows) { + if (isNext) + data += ",\n"; + data += "\t{\n"; + addField(data, "id", r->fields[0], ""); + addField(data, "active", r->fields[1], ""); + addField(data, "interval_ms", r->fields[2], ""); + addField(data, "filename", r->fields[3]); + addField(data, "arg1", r->fields[4]); + addField(data, "arg2", r->fields[5]); + addField(data, "arg3", r->fields[6]); + addField(data, "arg4", r->fields[7]); + addField(data, "arg5", r->fields[8]); + addField(data, "comment", r->fields[9]); + + data += "\t}"; + isNext = true; + } + data += "\n)\n"; + } + } + + if (sqlite_resultset) + delete sqlite_resultset; + + return 0; +} + +int ProxySQL_Config::Read_Scheduler_from_configfile() { + const Setting& root = GloVars.confFile->cfg.getRoot(); + if (root.exists("scheduler")==false) return 0; + const Setting &schedulers = root["scheduler"]; + int count = schedulers.getLength(); + //fprintf(stderr, "Found %d users\n",count); + int i; + int rows=0; + admindb->execute("PRAGMA foreign_keys = OFF"); + char *q=(char *)"INSERT OR REPLACE INTO scheduler (id, active, interval_ms, filename, arg1, arg2, arg3, arg4, arg5, comment) VALUES (%d, %d, %d, '%s', %s, %s, %s, %s, %s, '%s')"; + for (i=0; i< count; i++) { + const Setting &sched = schedulers[i]; + int id; + int active=1; + + std::string filename; + + bool arg1_exists=false; + std::string arg1; + bool arg2_exists=false; + std::string arg2; + bool arg3_exists=false; + std::string arg3; + bool arg4_exists=false; + std::string arg4; + bool arg5_exists=false; + std::string arg5; + + // variable for parsing interval_ms + int interval_ms=0; + + std::string comment=""; + + // validate arguments + if (sched.lookupValue("id", id)==false) { + proxy_error("Admin: detected a scheduler in config file without a mandatory id\n"); + continue; + } + sched.lookupValue("active", active); + sched.lookupValue("interval_ms", interval_ms); + if (sched.lookupValue("filename", filename)==false) { + proxy_error("Admin: detected a scheduler in config file without a mandatory filename\n"); + continue; + } + if (sched.lookupValue("arg1", arg1)) arg1_exists=true; + if (sched.lookupValue("arg2", arg2)) arg2_exists=true; + if (sched.lookupValue("arg3", arg3)) arg3_exists=true; + if (sched.lookupValue("arg4", arg4)) arg4_exists=true; + if (sched.lookupValue("arg5", arg5)) arg5_exists=true; + sched.lookupValue("comment", comment); + + + int query_len=0; + query_len+=strlen(q) + + strlen(std::to_string(id).c_str()) + + strlen(std::to_string(active).c_str()) + + strlen(std::to_string(interval_ms).c_str()) + + strlen(filename.c_str()) + + ( arg1_exists ? strlen(arg1.c_str()) : 0 ) + 4 + + ( arg2_exists ? strlen(arg2.c_str()) : 0 ) + 4 + + ( arg3_exists ? strlen(arg3.c_str()) : 0 ) + 4 + + ( arg4_exists ? strlen(arg4.c_str()) : 0 ) + 4 + + ( arg5_exists ? strlen(arg5.c_str()) : 0 ) + 4 + + strlen(comment.c_str()) + + 40; + char *query=(char *)malloc(query_len); + if (arg1_exists) + arg1="\'" + arg1 + "\'"; + else + arg1 = "NULL"; + if (arg2_exists) + arg2="\'" + arg2 + "\'"; + else + arg2 = "NULL"; + if (arg3_exists) + arg3="\'" + arg3 + "\'"; + else + arg3 = "NULL"; + if (arg4_exists) + arg4="\'" + arg4 + "\'"; + else + arg4 = "NULL"; + if (arg5_exists) + arg5="\'" + arg5 + "\'"; + else + arg5 = "NULL"; + + sprintf(query, q, + id, active, + interval_ms, + filename.c_str(), + arg1.c_str(), + arg2.c_str(), + arg3.c_str(), + arg4.c_str(), + arg5.c_str(), + comment.c_str() + ); + admindb->execute(query); + free(query); + rows++; + } + admindb->execute("PRAGMA foreign_keys = ON"); + return rows; +} + +int ProxySQL_Config::Write_MySQL_Query_Rules_to_configfile(std::string& data) { + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* sqlite_resultset = NULL; + + char *query=(char *)"SELECT * FROM mysql_query_rules"; + admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); + if (error) { + proxy_error("Error on read from mysql_query_rules : %s\n", error); + return -1; + } else { + if (sqlite_resultset) { + std::string prefix; + data += "mysql_query_rules:\n(\n"; + bool isNext = false; + for (auto r : sqlite_resultset->rows) { + if (isNext) + data += ",\n"; + data += "\t{\n"; + addField(data, "rule_id", r->fields[0], ""); + addField(data, "active", r->fields[1], ""); + addField(data, "username", r->fields[2]); + addField(data, "schemaname", r->fields[3]); + addField(data, "flagIN", r->fields[4], ""); + addField(data, "client_addr", r->fields[5]); + addField(data, "proxy_addr", r->fields[6]); + addField(data, "proxy_port", r->fields[7], ""); + addField(data, "digest", r->fields[8]); + addField(data, "match_digest", r->fields[9]); + addField(data, "match_pattern", r->fields[10]); + addField(data, "negate_match_pattern", r->fields[11], ""); + addField(data, "re_modifiers", r->fields[12]); + addField(data, "flagOUT", r->fields[13], ""); + addField(data, "replace_pattern", r->fields[14]); + addField(data, "destination_hostgroup", r->fields[15], ""); + addField(data, "cache_ttl", r->fields[16], ""); + addField(data, "cache_empty_result", r->fields[17], ""); + addField(data, "cache_timeout", r->fields[18], ""); + addField(data, "reconnect", r->fields[19], ""); + addField(data, "timeout", r->fields[20], ""); + addField(data, "retries", r->fields[21], ""); + addField(data, "delay", r->fields[22], ""); + addField(data, "next_query_flagIN", r->fields[23], ""); + addField(data, "mirror_flagOUT", r->fields[24], ""); + addField(data, "mirror_hostgroup", r->fields[25], ""); + addField(data, "error_msg", r->fields[26]); + addField(data, "OK_msg", r->fields[27]); + addField(data, "sticky_conn", r->fields[28], ""); + addField(data, "multiplex", r->fields[29], ""); + addField(data, "gtid_from_hostgroup", r->fields[30], ""); + addField(data, "log", r->fields[31], ""); + addField(data, "apply", r->fields[32], ""); + addField(data, "comment", r->fields[33]); + + data += "\t}"; + isNext = true; + } + data += "\n)\n"; + } + } + + if (sqlite_resultset) + delete sqlite_resultset; + + return 0; +} + +int ProxySQL_Config::Read_MySQL_Query_Rules_from_configfile() { + const Setting& root = GloVars.confFile->cfg.getRoot(); + if (root.exists("mysql_query_rules")==false) return 0; + const Setting &mysql_query_rules = root["mysql_query_rules"]; + int count = mysql_query_rules.getLength(); + //fprintf(stderr, "Found %d users\n",count); + int i; + int rows=0; + admindb->execute("PRAGMA foreign_keys = OFF"); + char *q=(char *)"INSERT OR REPLACE INTO mysql_query_rules (rule_id, active, username, schemaname, flagIN, client_addr, proxy_addr, proxy_port, digest, match_digest, match_pattern, negate_match_pattern, re_modifiers, flagOUT, replace_pattern, destination_hostgroup, cache_ttl, cache_empty_result, cache_timeout, reconnect, timeout, retries, delay, next_query_flagIN, mirror_flagOUT, mirror_hostgroup, error_msg, ok_msg, sticky_conn, multiplex, gtid_from_hostgroup, log, apply, comment) VALUES (%d, %d, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s)"; + for (i=0; i< count; i++) { + const Setting &rule = mysql_query_rules[i]; + int rule_id; + int active=1; + bool username_exists=false; + std::string username; + bool schemaname_exists=false; + std::string schemaname; + int flagIN=0; + + // variables for parsing client_addr + bool client_addr_exists=false; + std::string client_addr; + + // variables for parsing proxy_addr + bool proxy_addr_exists=false; + std::string proxy_addr; + + // variable for parsing proxy_port + int proxy_port=-1; + + // variables for parsing digest + bool digest_exists=false; + std::string digest; + + + bool match_digest_exists=false; + std::string match_digest; + bool match_pattern_exists=false; + std::string match_pattern; + int negate_match_pattern=0; + + bool re_modifiers_exists=false; + std::string re_modifiers; + + int flagOUT=-1; + bool replace_pattern_exists=false; + std::string replace_pattern; + int destination_hostgroup=-1; + int next_query_flagIN=-1; + int mirror_flagOUT=-1; + int mirror_hostgroup=-1; + int cache_ttl=-1; + int cache_empty_result=-1; + int cache_timeout=-1; + int reconnect=-1; + int timeout=-1; + int retries=-1; + int delay=-1; + bool error_msg_exists=false; + std::string error_msg; + bool OK_msg_exists=false; + std::string OK_msg; + + int sticky_conn=-1; + int multiplex=-1; + int gtid_from_hostgroup = -1; + + // variable for parsing log + int log=-1; + + int apply=0; + + bool comment_exists=false; + std::string comment; + + // validate arguments + if (rule.lookupValue("rule_id", rule_id)==false) { + proxy_error("Admin: detected a mysql_query_rules in config file without a mandatory rule_id\n"); + continue; + } + rule.lookupValue("active", active); + if (rule.lookupValue("username", username)) username_exists=true; + if (rule.lookupValue("schemaname", schemaname)) schemaname_exists=true; + rule.lookupValue("flagIN", flagIN); + + if (rule.lookupValue("client_addr", client_addr)) client_addr_exists=true; + if (rule.lookupValue("proxy_addr", proxy_addr)) proxy_addr_exists=true; + rule.lookupValue("proxy_port", proxy_port); + if (rule.lookupValue("digest", digest)) digest_exists=true; + + if (rule.lookupValue("match_digest", match_digest)) match_digest_exists=true; + if (rule.lookupValue("match_pattern", match_pattern)) match_pattern_exists=true; + rule.lookupValue("negate_match_pattern", negate_match_pattern); + if (rule.lookupValue("re_modifiers", re_modifiers)) { + } else { + re_modifiers = "CASELESS"; + } + re_modifiers_exists=true; + rule.lookupValue("flagOUT", flagOUT); + if (rule.lookupValue("replace_pattern", replace_pattern)) replace_pattern_exists=true; + rule.lookupValue("destination_hostgroup", destination_hostgroup); + rule.lookupValue("next_query_flagIN", next_query_flagIN); + rule.lookupValue("mirror_flagOUT", mirror_flagOUT); + rule.lookupValue("mirror_hostgroup", mirror_hostgroup); + rule.lookupValue("cache_ttl", cache_ttl); + rule.lookupValue("cache_empty_result", cache_empty_result); + rule.lookupValue("cache_timeout", cache_timeout); + rule.lookupValue("reconnect", reconnect); + rule.lookupValue("timeout", timeout); + rule.lookupValue("retries", retries); + rule.lookupValue("delay", delay); + + if (rule.lookupValue("error_msg", error_msg)) error_msg_exists=true; + if (rule.lookupValue("OK_msg", OK_msg)) OK_msg_exists=true; + + rule.lookupValue("sticky_conn", sticky_conn); + rule.lookupValue("multiplex", multiplex); + rule.lookupValue("gtid_from_hostgroup", gtid_from_hostgroup); + + rule.lookupValue("log", log); + + rule.lookupValue("apply", apply); + if (rule.lookupValue("comment", comment)) comment_exists=true; + + + //if (user.lookupValue("default_schema", default_schema)==false) default_schema=""; + int query_len=0; + query_len+=strlen(q) + + strlen(std::to_string(rule_id).c_str()) + + strlen(std::to_string(active).c_str()) + + ( username_exists ? strlen(username.c_str()) : 0 ) + 4 + + ( schemaname_exists ? strlen(schemaname.c_str()) : 0 ) + 4 + + strlen(std::to_string(flagIN).c_str()) + 4 + + + ( client_addr_exists ? strlen(client_addr.c_str()) : 0 ) + 4 + + ( proxy_addr_exists ? strlen(proxy_addr.c_str()) : 0 ) + 4 + + strlen(std::to_string(proxy_port).c_str()) + 4 + + + ( match_digest_exists ? strlen(match_digest.c_str()) : 0 ) + 4 + + ( match_pattern_exists ? strlen(match_pattern.c_str()) : 0 ) + 4 + + strlen(std::to_string(negate_match_pattern).c_str()) + 4 + + ( re_modifiers_exists ? strlen(re_modifiers.c_str()) : 0 ) + 4 + + strlen(std::to_string(flagOUT).c_str()) + 4 + + ( replace_pattern_exists ? strlen(replace_pattern.c_str()) : 0 ) + 4 + + strlen(std::to_string(destination_hostgroup).c_str()) + 4 + + strlen(std::to_string(cache_ttl).c_str()) + 4 + + strlen(std::to_string(cache_empty_result).c_str()) + 4 + + strlen(std::to_string(cache_timeout).c_str()) + 4 + + strlen(std::to_string(reconnect).c_str()) + 4 + + strlen(std::to_string(timeout).c_str()) + 4 + + strlen(std::to_string(next_query_flagIN).c_str()) + 4 + + strlen(std::to_string(mirror_flagOUT).c_str()) + 4 + + strlen(std::to_string(mirror_hostgroup).c_str()) + 4 + + strlen(std::to_string(retries).c_str()) + 4 + + strlen(std::to_string(delay).c_str()) + 4 + + ( error_msg_exists ? strlen(error_msg.c_str()) : 0 ) + 4 + + ( OK_msg_exists ? strlen(OK_msg.c_str()) : 0 ) + 4 + + strlen(std::to_string(sticky_conn).c_str()) + 4 + + strlen(std::to_string(multiplex).c_str()) + 4 + + strlen(std::to_string(gtid_from_hostgroup).c_str()) + 4 + + strlen(std::to_string(log).c_str()) + 4 + + strlen(std::to_string(apply).c_str()) + 4 + + ( comment_exists ? strlen(comment.c_str()) : 0 ) + 4 + + 64; + char *query=(char *)malloc(query_len); + if (username_exists) + username="\"" + username + "\""; + else + username = "NULL"; + if (schemaname_exists) + schemaname="\"" + schemaname + "\""; + else + schemaname = "NULL"; + + if (client_addr_exists) + client_addr="\"" + client_addr + "\""; + else + client_addr = "NULL"; + if (proxy_addr_exists) + proxy_addr="\"" + proxy_addr + "\""; + else + proxy_addr = "NULL"; + if (digest_exists) + digest="\"" + digest + "\""; + else + digest = "NULL"; + + if (match_digest_exists) + match_digest="\"" + match_digest + "\""; + else + match_digest = "NULL"; + if (match_pattern_exists) + match_pattern="\"" + match_pattern + "\""; + else + match_pattern = "NULL"; + if (replace_pattern_exists) + replace_pattern="\"" + replace_pattern + "\""; + else + replace_pattern = "NULL"; + if (error_msg_exists) + error_msg="\"" + error_msg + "\""; + else + error_msg = "NULL"; + if (OK_msg_exists) + OK_msg="\"" + OK_msg + "\""; + else + OK_msg = "NULL"; + if (re_modifiers_exists) + re_modifiers="\"" + re_modifiers + "\""; + else + re_modifiers = "NULL"; + if (comment_exists) + comment="\"" + comment + "\""; + else + comment = "NULL"; + + + sprintf(query, q, + rule_id, active, + username.c_str(), + schemaname.c_str(), + ( flagIN >= 0 ? std::to_string(flagIN).c_str() : "NULL") , + client_addr.c_str(), + proxy_addr.c_str(), + ( proxy_port >= 0 ? std::to_string(proxy_port).c_str() : "NULL") , + digest.c_str(), + match_digest.c_str(), + match_pattern.c_str(), + ( negate_match_pattern == 0 ? 0 : 1) , + re_modifiers.c_str(), + ( flagOUT >= 0 ? std::to_string(flagOUT).c_str() : "NULL") , + replace_pattern.c_str(), + ( destination_hostgroup >= 0 ? std::to_string(destination_hostgroup).c_str() : "NULL") , + ( cache_ttl >= 0 ? std::to_string(cache_ttl).c_str() : "NULL") , + ( cache_empty_result >= 0 ? std::to_string(cache_empty_result).c_str() : "NULL") , + ( cache_timeout >= 0 ? std::to_string(cache_timeout).c_str() : "NULL") , + ( reconnect >= 0 ? std::to_string(reconnect).c_str() : "NULL") , + ( timeout >= 0 ? std::to_string(timeout).c_str() : "NULL") , + ( retries >= 0 ? std::to_string(retries).c_str() : "NULL") , + ( delay >= 0 ? std::to_string(delay).c_str() : "NULL") , + ( next_query_flagIN >= 0 ? std::to_string(next_query_flagIN).c_str() : "NULL") , + ( mirror_flagOUT >= 0 ? std::to_string(mirror_flagOUT).c_str() : "NULL") , + ( mirror_hostgroup >= 0 ? std::to_string(mirror_hostgroup).c_str() : "NULL") , + error_msg.c_str(), + OK_msg.c_str(), + ( sticky_conn >= 0 ? std::to_string(sticky_conn).c_str() : "NULL") , + ( multiplex >= 0 ? std::to_string(multiplex).c_str() : "NULL") , + ( gtid_from_hostgroup >= 0 ? std::to_string(gtid_from_hostgroup).c_str() : "NULL") , + ( log >= 0 ? std::to_string(log).c_str() : "NULL") , + ( apply == 0 ? 0 : 1) , + comment.c_str() + ); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + free(query); + rows++; + } + admindb->execute("PRAGMA foreign_keys = ON"); + return rows; +} + +int ProxySQL_Config::Write_MySQL_Servers_to_configfile(std::string& data) { + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* sqlite_resultset = NULL; + + char *query=(char *)"SELECT * FROM mysql_servers"; + admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); + if (error) { + proxy_error("Error on read from mysql_query_rules : %s\n", error); + return -1; + } else { + if (sqlite_resultset) { + data += "mysql_servers:\n(\n"; + bool isNext = false; + for (auto r : sqlite_resultset->rows) { + if (isNext) + data += ",\n"; + data += "\t{\n"; + addField(data, "hostgroup_id", r->fields[0], ""); + addField(data, "hostname", r->fields[1]); + addField(data, "port", r->fields[2], ""); + addField(data, "gtid_port", r->fields[3], ""); + addField(data, "status", r->fields[4]); + addField(data, "weight", r->fields[5], ""); + addField(data, "compression", r->fields[6], ""); + addField(data, "max_connections", r->fields[7], ""); + addField(data, "max_replication_lag", r->fields[8], ""); + addField(data, "use_ssl", r->fields[9], ""); + addField(data, "max_latency_ms", r->fields[10], ""); + addField(data, "comment", r->fields[11]); + + data += "\t}"; + isNext = true; + } + data += "\n)\n"; + } + } + + if (sqlite_resultset) + delete sqlite_resultset; + + query=(char *)"SELECT * FROM mysql_replication_hostgroups"; + admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); + if (error) { + proxy_error("Error on read from mysql_replication_hostgroups : %s\n", error); + return -1; + } else { + if (sqlite_resultset) { + data += "mysql_replication_hostgroups:\n(\n"; + bool isNext = false; + for (auto r : sqlite_resultset->rows) { + if (isNext) + data += ",\n"; + data += "\t{\n"; + addField(data, "writer_hostgroup", r->fields[0], ""); + addField(data, "reader_hostgroup", r->fields[1], ""); + addField(data, "check_type", r->fields[2]); + addField(data, "comment", r->fields[3]); + + data += "\t}"; + isNext = true; + } + data += "\n)\n"; + } + } + + if (sqlite_resultset) + delete sqlite_resultset; + + query=(char *)"SELECT * FROM mysql_group_replication_hostgroups"; + admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); + if (error) { + proxy_error("Error on read from mysql_group_replication_hostgroups : %s\n", error); + return -1; + } else { + if (sqlite_resultset) { + data += "mysql_group_replication_hostgroups:\n(\n"; + bool isNext = false; + for (auto r : sqlite_resultset->rows) { + if (isNext) + data += ",\n"; + data += "\t{\n"; + addField(data, "writer_hostgroup", r->fields[0], ""); + addField(data, "backup_writer_hostgroup", r->fields[1], ""); + addField(data, "reader_hostgroup", r->fields[2], ""); + addField(data, "offline_hostgroup", r->fields[3], ""); + addField(data, "active", r->fields[4], ""); + addField(data, "max_writers", r->fields[5], ""); + addField(data, "writer_is_also_reader", r->fields[6], ""); + addField(data, "max_transactions_behind", r->fields[7], ""); + addField(data, "comment", r->fields[8]); + + data += "\t}"; + isNext = true; + } + data += "\n)\n"; + } + } + + if (sqlite_resultset) + delete sqlite_resultset; + + query=(char *)"SELECT * FROM mysql_galera_hostgroups"; + admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); + if (error) { + proxy_error("Error on read from mysql_galera_hostgroups: %s\n", error); + return -1; + } else { + if (sqlite_resultset) { + data += "mysql_galera_hostgroups:\n(\n"; + bool isNext = false; + for (auto r : sqlite_resultset->rows) { + if (isNext) + data += ",\n"; + data += "\t{\n"; + addField(data, "writer_hostgroup", r->fields[0], ""); + addField(data, "backup_writer_hostgroup", r->fields[1], ""); + addField(data, "reader_hostgroup", r->fields[2], ""); + addField(data, "offline_hostgroup", r->fields[3], ""); + addField(data, "active", r->fields[4], ""); + addField(data, "max_writers", r->fields[5], ""); + addField(data, "writer_is_also_reader", r->fields[6], ""); + addField(data, "max_transactions_behind", r->fields[7], ""); + addField(data, "comment", r->fields[8]); + + data += "\t}"; + isNext = true; + } + data += "\n)\n"; + } + } + + if (sqlite_resultset) + delete sqlite_resultset; + + query=(char *)"SELECT * FROM mysql_aws_aurora_hostgroups"; + admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); + if (error) { + proxy_error("Error on read from mysql_aws_aurora_hostgroups: %s\n", error); + return -1; + } else { + if (sqlite_resultset) { + data += "mysql_aws_aurora_hostgroups:\n(\n"; + bool isNext = false; + for (auto r : sqlite_resultset->rows) { + if (isNext) + data += ",\n"; + data += "\t{\n"; + addField(data, "writer_hostgroup", r->fields[0], ""); + addField(data, "reader_hostgroup", r->fields[1], ""); + addField(data, "active", r->fields[2], ""); + addField(data, "aurora_port", r->fields[3], ""); + addField(data, "domain_name", r->fields[4]); + addField(data, "max_lag_ms", r->fields[5], ""); + addField(data, "check_interval_ms", r->fields[6], ""); + addField(data, "check_timeout_ms", r->fields[7], ""); + addField(data, "writer_is_also_reader", r->fields[8], ""); + addField(data, "new_reader_weight", r->fields[9], ""); + addField(data, "add_lag_ms", r->fields[10], ""); + addField(data, "min_lag_ms", r->fields[11], ""); + addField(data, "lag_num_checks", r->fields[12], ""); + addField(data, "comment", r->fields[13]); + + data += "\t}"; + isNext = true; + } + data += "\n)\n"; + } + } + + if (sqlite_resultset) + delete sqlite_resultset; + + return 0; +} + +int ProxySQL_Config::Read_MySQL_Servers_from_configfile() { + const Setting& root = GloVars.confFile->cfg.getRoot(); + int i; + int rows=0; + admindb->execute("PRAGMA foreign_keys = OFF"); + if (root.exists("mysql_servers")==true) { + const Setting &mysql_servers = root["mysql_servers"]; + int count = mysql_servers.getLength(); + //fprintf(stderr, "Found %d servers\n",count); + char *q=(char *)"INSERT OR REPLACE INTO mysql_servers (hostname, port, gtid_port, hostgroup_id, compression, weight, status, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment) VALUES (\"%s\", %d, %d, %d, %d, %d, \"%s\", %d, %d, %d, %d, '%s')"; + for (i=0; i< count; i++) { + const Setting &server = mysql_servers[i]; + std::string address; + std::string status="ONLINE"; + int port=3306; + int gtid_port=0; + int hostgroup; + int weight=1; + int compression=0; + int max_connections=1000; // default + int max_replication_lag=0; // default + int use_ssl=0; + int max_latency_ms=0; + std::string comment=""; + if (server.lookupValue("address", address)==false) { + if (server.lookupValue("hostname", address)==false) { + proxy_error("Admin: detected a mysql_servers in config file without a mandatory hostname\n"); + continue; + } + } + server.lookupValue("port", port); + server.lookupValue("gtid_port", gtid_port); + if (server.lookupValue("hostgroup", hostgroup)==false) { + if (server.lookupValue("hostgroup_id", hostgroup)==false) { + proxy_error("Admin: detected a mysql_servers in config file without a mandatory hostgroup_id\n"); + continue; + } + } + server.lookupValue("status", status); + if ( + (strcasecmp(status.c_str(),(char *)"ONLINE")) + && (strcasecmp(status.c_str(),(char *)"SHUNNED")) + && (strcasecmp(status.c_str(),(char *)"OFFLINE_SOFT")) + && (strcasecmp(status.c_str(),(char *)"OFFLINE_HARD")) + ) { + status="ONLINE"; + } + server.lookupValue("compression", compression); + server.lookupValue("weight", weight); + server.lookupValue("max_connections", max_connections); + server.lookupValue("max_replication_lag", max_replication_lag); + server.lookupValue("use_ssl", use_ssl); + server.lookupValue("max_latency_ms", max_latency_ms); + server.lookupValue("comment", comment); + char *o1=strdup(comment.c_str()); + char *o=escape_string_single_quotes(o1, false); + char *query=(char *)malloc(strlen(q)+strlen(status.c_str())+strlen(address.c_str())+strlen(o)+128); + sprintf(query,q, address.c_str(), port, gtid_port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, o); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + if (o!=o1) free(o); + free(o1); + free(query); + rows++; + } + } + if (root.exists("mysql_replication_hostgroups")==true) { + const Setting &mysql_replication_hostgroups = root["mysql_replication_hostgroups"]; + int count = mysql_replication_hostgroups.getLength(); + char *q=(char *)"INSERT OR REPLACE INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, comment, check_type) VALUES (%d, %d, '%s', '%s')"; + for (i=0; i< count; i++) { + const Setting &line = mysql_replication_hostgroups[i]; + int writer_hostgroup; + int reader_hostgroup; + std::string comment=""; + std::string check_type=""; + if (line.lookupValue("writer_hostgroup", writer_hostgroup)==false) { + proxy_error("Admin: detected a mysql_replication_hostgroups in config file without a mandatory writer_hostgroup\n"); + continue; + } + if (line.lookupValue("reader_hostgroup", reader_hostgroup)==false) { + proxy_error("Admin: detected a mysql_replication_hostgroups in config file without a mandatory reader_hostgroup\n"); + continue; + } + line.lookupValue("comment", comment); + char *o1=strdup(comment.c_str()); + char *o=escape_string_single_quotes(o1, false); + line.lookupValue("check_type", check_type); + if ( + (strcasecmp(check_type.c_str(),(char *)"read_only")) + && (strcasecmp(check_type.c_str(),(char *)"innodb_read_only")) + && (strcasecmp(check_type.c_str(),(char *)"super_read_only")) + ) { + check_type="read_only"; + } + char *t1=strdup(check_type.c_str()); + char *t=escape_string_single_quotes(t1, false); + char *query=(char *)malloc(strlen(q)+strlen(o)+strlen(t)+32); + sprintf(query,q, writer_hostgroup, reader_hostgroup, o, t); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + if (o!=o1) free(o); + free(o1); + if (t!=t1) free(t); + free(t1); + free(query); + rows++; + } + } + if (root.exists("mysql_group_replication_hostgroups")==true) { + const Setting &mysql_group_replication_hostgroups = root["mysql_group_replication_hostgroups"]; + int count = mysql_group_replication_hostgroups.getLength(); + char *q=(char *)"INSERT OR REPLACE INTO mysql_group_replication_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, comment) VALUES (%d, %d, %d, %d, %d, %d, %d, %d, '%s')"; + for (i=0; i< count; i++) { + const Setting &line = mysql_group_replication_hostgroups[i]; + int writer_hostgroup; + int backup_writer_hostgroup; + int reader_hostgroup; + int offline_hostgroup; + int active=1; // default + int max_writers; + int writer_is_also_reader; + int max_transactions_behind; + std::string comment=""; + if (line.lookupValue("writer_hostgroup", writer_hostgroup)==false) { + proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory writer_hostgroup\n"); + continue; + } + if (line.lookupValue("backup_writer_hostgroup", backup_writer_hostgroup)==false) { + proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory backup_writer_hostgroup\n"); + continue; + } + if (line.lookupValue("reader_hostgroup", reader_hostgroup)==false) { + proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory reader_hostgroup\n"); + continue; + } + if (line.lookupValue("offline_hostgroup", offline_hostgroup)==false) { + proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory offline_hostgroup\n"); + continue; + } + if (line.lookupValue("max_writers", max_writers)==false) max_writers=1; + if (line.lookupValue("writer_is_also_reader", writer_is_also_reader)==false) writer_is_also_reader=0; + if (line.lookupValue("max_transactions_behind", max_transactions_behind)==false) max_transactions_behind=0; + line.lookupValue("comment", comment); + char *o1=strdup(comment.c_str()); + char *o=escape_string_single_quotes(o1, false); + char *query=(char *)malloc(strlen(q)+strlen(o)+128); // 128 vs sizeof(int)*8 + sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, o); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + if (o!=o1) free(o); + free(o1); + free(query); + rows++; + } + } + if (root.exists("mysql_galera_hostgroups")==true) { + const Setting &mysql_galera_hostgroups = root["mysql_galera_hostgroups"]; + int count = mysql_galera_hostgroups.getLength(); + char *q=(char *)"INSERT OR REPLACE INTO mysql_galera_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, comment) VALUES (%d, %d, %d, %d, %d, %d, %d, %d, '%s')"; + for (i=0; i< count; i++) { + const Setting &line = mysql_galera_hostgroups[i]; + int writer_hostgroup; + int backup_writer_hostgroup; + int reader_hostgroup; + int offline_hostgroup; + int active=1; // default + int max_writers; + int writer_is_also_reader; + int max_transactions_behind; + std::string comment=""; + if (line.lookupValue("writer_hostgroup", writer_hostgroup)==false) { + proxy_error("Admin: detected a mysql_galera_hostgroups in config file without a mandatory writer_hostgroup\n"); + continue; + } + if (line.lookupValue("backup_writer_hostgroup", backup_writer_hostgroup)==false) { + proxy_error("Admin: detected a mysql_galera_hostgroups in config file without a mandatory backup_writer_hostgroup\n"); + continue; + } + if (line.lookupValue("reader_hostgroup", reader_hostgroup)==false) { + proxy_error("Admin: detected a mysql_galera_hostgroups in config file without a mandatory reader_hostgroup\n"); + continue; + } + if (line.lookupValue("offline_hostgroup", offline_hostgroup)==false) { + proxy_error("Admin: detected a mysql_galera_hostgroups in config file without a mandatory offline_hostgroup\n"); + continue; + } + if (line.lookupValue("max_writers", max_writers)==false) max_writers=1; + if (line.lookupValue("writer_is_also_reader", writer_is_also_reader)==false) writer_is_also_reader=0; + if (line.lookupValue("max_transactions_behind", max_transactions_behind)==false) max_transactions_behind=0; + line.lookupValue("comment", comment); + char *o1=strdup(comment.c_str()); + char *o=escape_string_single_quotes(o1, false); + char *query=(char *)malloc(strlen(q)+strlen(o)+128); // 128 vs sizeof(int)*8 + sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, o); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + if (o!=o1) free(o); + free(o1); + free(query); + rows++; + } + } + if (root.exists("mysql_aws_aurora_hostgroups")==true) { + const Setting &mysql_aws_aurora_hostgroups = root["mysql_aws_aurora_hostgroups"]; + int count = mysql_aws_aurora_hostgroups.getLength(); + char *q=(char *)"INSERT OR REPLACE INTO mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, aurora_port, domain_name, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, comment ) VALUES (%d, %d, %d, %d, '%s', %d, %d, %d, %d, %d, %d, %d, %d, '%s')"; + for (i=0; i< count; i++) { + const Setting &line = mysql_aws_aurora_hostgroups[i]; + int writer_hostgroup; + int reader_hostgroup; + int active=1; // default + int aurora_port; + int max_lag_ms; + int add_lag_ms; + int min_lag_ms; + int lag_num_checks; + int check_interval_ms; + int check_timeout_ms; + int writer_is_also_reader; + int new_reader_weight; + std::string comment=""; + std::string domain_name=""; + if (line.lookupValue("writer_hostgroup", writer_hostgroup)==false) { + proxy_error("Admin: detected a mysql_aws_aurora_hostgroups in config file without a mandatory writer_hostgroup\n"); + continue; + } + if (line.lookupValue("reader_hostgroup", reader_hostgroup)==false) { + proxy_error("Admin: detected a mysql_aws_aurora_hostgroups in config file without a mandatory reader_hostgroup\n"); + continue; + } + if (line.lookupValue("aurora_port", aurora_port)==false) aurora_port=3306; + if (line.lookupValue("max_lag_ms", max_lag_ms)==false) max_lag_ms=600000; + if (line.lookupValue("check_interval_ms", check_interval_ms)==false) check_interval_ms=1000; + if (line.lookupValue("check_timeout_ms", check_timeout_ms)==false) check_timeout_ms=1000; + if (line.lookupValue("writer_is_also_reader", writer_is_also_reader)==false) writer_is_also_reader=0; + if (line.lookupValue("new_reader_weight", new_reader_weight)==false) new_reader_weight=1; + if (line.lookupValue("add_lag_ms", add_lag_ms)==false) add_lag_ms=30; + if (line.lookupValue("min_lag_ms", min_lag_ms)==false) min_lag_ms=30; + if (line.lookupValue("lag_num_checks", lag_num_checks)==false) lag_num_checks=1; + line.lookupValue("comment", comment); + line.lookupValue("domain_name", domain_name); + char *o1=strdup(comment.c_str()); + char *o=escape_string_single_quotes(o1, false); + char *p1=strdup(domain_name.c_str()); + char *p=escape_string_single_quotes(p1, false); + char *query=(char *)malloc(strlen(q)+strlen(o)+strlen(p)+256); // 128 vs sizeof(int)*8 + sprintf(query,q, writer_hostgroup, reader_hostgroup, active, aurora_port, p, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, o); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + if (o!=o1) free(o); + free(o1); + if (p!=p1) free(p); + free(p1); + free(query); + rows++; + } + } + admindb->execute("PRAGMA foreign_keys = ON"); + return rows; +} + +int ProxySQL_Config::Write_ProxySQL_Servers_to_configfile(std::string& data) { + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* sqlite_resultset = NULL; + + char *query=(char *)"SELECT * FROM proxysql_servers"; + admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); + if (error) { + proxy_error("Error on read from proxysql_servers: %s\n", error); + return -1; + } else { + if (sqlite_resultset) { + data += "proxysql_servers:\n(\n"; + bool isNext = false; + for (auto r : sqlite_resultset->rows) { + if (isNext) + data += ",\n"; + data += "\t{\n"; + addField(data, "hostname", r->fields[0]); + addField(data, "port", r->fields[1], ""); + addField(data, "weight", r->fields[2], ""); + addField(data, "comment", r->fields[3]); + + data += "\t}"; + isNext = true; + } + data += "\n)\n"; + } + } + + if (sqlite_resultset) + delete sqlite_resultset; + + return 0; +} + +int ProxySQL_Config::Read_ProxySQL_Servers_from_configfile() { + const Setting& root = GloVars.confFile->cfg.getRoot(); + int i; + int rows=0; + admindb->execute("PRAGMA foreign_keys = OFF"); + if (root.exists("proxysql_servers")==true) { + const Setting &mysql_servers = root["proxysql_servers"]; + int count = mysql_servers.getLength(); + //fprintf(stderr, "Found %d servers\n",count); + char *q=(char *)"INSERT OR REPLACE INTO proxysql_servers (hostname, port, weight, comment) VALUES (\"%s\", %d, %d, '%s')"; + for (i=0; i< count; i++) { + const Setting &server = mysql_servers[i]; + std::string address; + int port; + int weight=0; + std::string comment=""; + if (server.lookupValue("address", address)==false) { + if (server.lookupValue("hostname", address)==false) { + proxy_error("Admin: detected a proxysql_servers in config file without a mandatory hostname\n"); + continue; + } + } + if (server.lookupValue("port", port)==false) { + proxy_error("Admin: detected a proxysql_servers in config file without a mandatory port\n"); + continue; + } + server.lookupValue("weight", weight); + server.lookupValue("comment", comment); + char *o1=strdup(comment.c_str()); + char *o=escape_string_single_quotes(o1, false); + char *query=(char *)malloc(strlen(q)+strlen(address.c_str())+strlen(o)+128); + sprintf(query, q, address.c_str(), port, weight, o); + proxy_info("Cluster: Adding ProxySQL Servers %s:%d from config file\n", address.c_str(), port); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + if (o!=o1) free(o); + free(o1); + free(query); + rows++; + } + } + admindb->execute("PRAGMA foreign_keys = ON"); + return rows; +} + +int ProxySQL_Config::Write_Global_Variables_to_configfile(std::string& data) { + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* sqlite_resultset = NULL; + + char *query=(char *)"SELECT variable_name, variable_value FROM global_variables ORDER BY variable_name"; + admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); + if (error) { + proxy_error("Error on read from global_variables : %s\n", error); + return -1; + } else { + if (sqlite_resultset) { + std::string prefix; + + for (auto r : sqlite_resultset->rows) { + std::string input(r->fields[0]); + std::string p1 = input.substr(0, input.find("-")); + if (prefix.empty()) { + prefix = input.substr(0, input.find("-")); + data += prefix + "_variables =\n{\n"; + } else { + if (p1.compare(prefix)) { + prefix = p1; + data += "}\n\n" + prefix + "_variables = \n{\n"; + } + } + if (r->fields[1] && strlen(r->fields[1])) { + std::stringstream ss; + ss << "\t" << r->fields[0] + p1.size() + 1 << "=\"" << r->fields[1] << "\"\n"; + data += ss.str(); + } + } + + if (!prefix.empty()) + data += "}\n"; + } + } + + if (sqlite_resultset) + delete sqlite_resultset; + + return 0; +} diff --git a/test/tap/tap/command_line.cpp b/test/tap/tap/command_line.cpp index 2bef6630b..50c1ef507 100644 --- a/test/tap/tap/command_line.cpp +++ b/test/tap/tap/command_line.cpp @@ -13,7 +13,7 @@ using nlohmann::json; CommandLine::CommandLine() : - host(NULL), username(NULL), password(NULL) {} + host(NULL), username(NULL), password(NULL), admin_username(NULL), admin_password(NULL) {} CommandLine::~CommandLine() { if (host) @@ -22,6 +22,10 @@ CommandLine::~CommandLine() { free(username); if (password) free(password); + if (admin_username) + free(admin_username); + if (admin_password) + free(admin_password); } int CommandLine::parse(int argc, char** argv) { @@ -37,6 +41,9 @@ int CommandLine::parse(int argc, char** argv) { case 'p': password = strdup(optarg); break; + case 'A': + admin_port = atoi(optarg); + break; case 'h': host = strdup(optarg); break; @@ -49,8 +56,14 @@ int CommandLine::parse(int argc, char** argv) { case 'n': no_write = true; break; + case 'U': + admin_username = strdup(optarg); + break; + case 'S': + admin_password = strdup(optarg); + break; default: /* '?' */ - fprintf(stderr, "Usage: %s -u username -p password -h host [ -P port ] [ -c ] [ -s ] [ -n ]\n", argv[0]); + fprintf(stderr, "Usage: %s -u username -p password -h host [ -P port ] [ -A port ] [ -U admin_username ] [ -S admin_password ] [ -c ] [ -s ] [ -n ]\n", argv[0]); return 0; } } @@ -85,6 +98,7 @@ int CommandLine::read(const std::string& file) { host = strdup("127.0.0.1"); port = 6033; + admin_port = 6032; username = strdup("root"); password = strdup("a"); return 0; @@ -105,6 +119,18 @@ int CommandLine::getEnv() { if(!value) return -1; password=strdup(value); + value=getenv("TAP_ADMINUSERNAME"); + if(!value) + admin_username=strdup("admin"); + else + admin_username=strdup(value); + + value=getenv("TAP_ADMINPASSWORD"); + if(!value) + admin_password=strdup("admin"); + else + admin_password=strdup(value); + port=6033; checksum=true; @@ -113,8 +139,18 @@ int CommandLine::getEnv() { value=getenv("TAP_PORT"); if(value) env_port=strtol(value, &endstr, 10); + else + env_port=6033; if(env_port>0 && env_port<65536) port=env_port; + value=getenv("TAP_ADMINPORT"); + if(value) + env_port=strtol(value, &endstr, 10); + else + env_port=6032; + if(env_port>0 && env_port<65536) + admin_port=env_port; + return 0; } diff --git a/test/tap/tap/command_line.h b/test/tap/tap/command_line.h index ad1b1b35c..a8f93c029 100644 --- a/test/tap/tap/command_line.h +++ b/test/tap/tap/command_line.h @@ -13,7 +13,10 @@ class CommandLine { char* host; char* username; char* password; + char* admin_username; + char* admin_password; int port; + int admin_port; int read(const std::string& file); int getEnv(); diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index e990ee90c..f1017c027 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -14,13 +14,13 @@ all: tests .PHONY: clean clean: - rm -f basic-t set_character_set-t charset_unsigned_int-t || true + rm -f basic-t set_character_set-t charset_unsigned_int-t select_config_file-t || true OPT=-O2 -SRC=basic-t.cpp set_character_set-t.cpp charset_unsigned_int-t.cpp +SRC=basic-t.cpp set_character_set-t.cpp charset_unsigned_int-t.cpp select_config_file-t.cpp -tests: basic-t set_character_set-t charset_unsigned_int-t +tests: basic-t set_character_set-t charset_unsigned_int-t select_config_file-t basic-t: $(LIBDIR)/libtap.a g++ basic-t.cpp -I$(INCLUDEDIR) -L$(LIBDIR) $(OPT) -std=c++11 -ltap -o basic-t @@ -31,3 +31,6 @@ set_character_set-t: set_character_set-t.cpp $(LIBDIR)/libtap.a charset_unsigned_int-t: charset_unsigned_int-t.cpp $(LIBDIR)/libtap.a g++ charset_unsigned_int-t.cpp -I$(INCLUDEDIR) -I$(MARIADB_IDIR) -L$(LIBDIR) $(OPT) -std=c++11 $(STATIC_LIBS) -ltap -lssl -lcrypto -ldl -lpthread -o charset_unsigned_int-t +select_config_file-t: select_config_file-t.cpp $(LIBDIR)/libtap.a + g++ select_config_file-t.cpp -I$(INCLUDEDIR) -L$(LIBDIR) -std=c++11 `mysql_config --include --libs` -lmysqlclient -ltap -o select_config_file-t + diff --git a/test/tap/tests/proxysql_reference_select_config_file.cnf b/test/tap/tests/proxysql_reference_select_config_file.cnf new file mode 100644 index 000000000..cc2f68f79 --- /dev/null +++ b/test/tap/tests/proxysql_reference_select_config_file.cnf @@ -0,0 +1,369 @@ +######################################################################################## +# This config file is parsed using libconfig , and its grammar is described in: +# http://www.hyperrealm.com/libconfig/libconfig_manual.html#Configuration-File-Grammar +# Grammar is also copied at the end of this file +######################################################################################## + +######################################################################################## +# IMPORTANT INFORMATION REGARDING THIS CONFIGURATION FILE: +######################################################################################## +# On startup, ProxySQL reads its config file (if present) to determine its datadir. +# What happens next depends on if the database file (disk) is present in the defined +# datadir (i.e. "/var/lib/proxysql/proxysql.db"). +# +# If the database file is found, ProxySQL initializes its in-memory configuration from +# the persisted on-disk database. So, disk configuration gets loaded into memory and +# then propagated towards the runtime configuration. +# +# If the database file is not found and a config file exists, the config file is parsed +# and its content is loaded into the in-memory database, to then be both saved on-disk +# database and loaded at runtime. +# +# IMPORTANT: If a database file is found, the config file is NOT parsed. In this case +# ProxySQL initializes its in-memory configuration from the persisted on-disk +# database ONLY. In other words, the configuration found in the proxysql.cnf +# file is only used to initial the on-disk database read on the first startup. +# +# In order to FORCE a re-initialise of the on-disk database from the configuration file +# the ProxySQL service should be started with "service proxysql initial". +# +######################################################################################## +admin_variables = +{ + admin_credentials="admin" + checksum_mysql_query_rules="admin" + checksum_mysql_servers="admin" + checksum_mysql_users="admin" + cluster_check_interval_ms="admin" + cluster_check_status_frequency="admin" + cluster_mysql_query_rules_diffs_before_sync="admin" + cluster_mysql_query_rules_save_to_disk="admin" + cluster_mysql_servers_diffs_before_sync="admin" + cluster_mysql_servers_save_to_disk="admin" + cluster_mysql_users_diffs_before_sync="admin" + cluster_mysql_users_save_to_disk="admin" + cluster_password="admin" + cluster_proxysql_servers_diffs_before_sync="admin" + cluster_proxysql_servers_save_to_disk="admin" + cluster_username="admin" + hash_passwords="admin" + mysql_ifaces="admin" + read_only="admin" + refresh_interval="admin" + restapi_enabled="admin" + restapi_port="admin" + stats_credentials="admin" + stats_mysql_connection_pool="admin" + stats_mysql_connections="admin" + stats_mysql_query_cache="admin" + stats_mysql_query_digest_to_disk="admin" + stats_system_cpu="admin" + stats_system_memory="admin" + telnet_admin_ifaces="admin" + telnet_stats_ifaces="admin" + vacuum_stats="admin" + version="admin" + web_enabled="admin" + web_port="admin" +} + +mysql_variables = +{ + add_ldap_user_comment="mysql" + auditlog_filename="mysql" + auditlog_filesize="mysql" + aurora_max_lag_ms_only_read_from_replicas="mysql" + auto_increment_delay_multiplex="mysql" + autocommit_false_is_transaction="mysql" + autocommit_false_not_reusable="mysql" + automatic_detect_sqli="mysql" + binlog_reader_connect_retry_msec="mysql" + client_found_rows="mysql" + client_multi_statements="mysql" + client_session_track_gtid="mysql" + commands_stats="mysql" + connect_retries_delay="mysql" + connect_retries_on_failure="mysql" + connect_timeout_server="mysql" + connect_timeout_server_max="mysql" + connection_delay_multiplex_ms="mysql" + connection_max_age_ms="mysql" + connpoll_reset_queue_length="mysql" + default_character_set_results="mysql" + default_charset="mysql" + default_collation_connection="mysql" + default_isolation_level="mysql" + default_max_join_size="mysql" + default_max_latency_ms="mysql" + default_net_write_timeout="mysql" + default_query_delay="mysql" + default_query_timeout="mysql" + default_reconnect="mysql" + default_schema="mysql" + default_session_track_gtids="mysql" + default_sql_auto_is_null="mysql" + default_sql_mode="mysql" + default_sql_safe_updates="mysql" + default_sql_select_limit="mysql" + default_time_zone="mysql" + default_transaction_read="mysql" + default_tx_isolation="mysql" + enforce_autocommit_on_reads="mysql" + eventslog_default_log="mysql" + eventslog_filename="mysql" + eventslog_filesize="mysql" + eventslog_format="mysql" + firewall_whitelist_enabled="mysql" + firewall_whitelist_errormsg="mysql" + forward_autocommit="mysql" + free_connections_pct="mysql" + handle_unknown_charset="mysql" + have_compress="mysql" + have_ssl="mysql" + hostgroup_manager_verbose="mysql" + init_connect="mysql" + interfaces="mysql" + keep_multiplexing_variables="mysql" + kill_backend_connection_when_disconnect="mysql" + ldap_user_variable="mysql" + log_unhealthy_connections="mysql" + long_query_time="mysql" + max_allowed_packet="mysql" + max_connections="mysql" + max_stmts_cache="mysql" + max_stmts_per_connection="mysql" + max_transaction_time="mysql" + min_num_servers_lantency_awareness="mysql" + mirror_max_concurrency="mysql" + mirror_max_queue_length="mysql" + monitor_connect_interval="mysql" + monitor_connect_timeout="mysql" + monitor_enabled="mysql" + monitor_galera_healthcheck_interval="mysql" + monitor_galera_healthcheck_max_timeout_count="mysql" + monitor_galera_healthcheck_timeout="mysql" + monitor_groupreplication_healthcheck_interval="mysql" + monitor_groupreplication_healthcheck_max_timeout_count="mysql" + monitor_groupreplication_healthcheck_timeout="mysql" + monitor_groupreplication_max_transactions_behind_count="mysql" + monitor_history="mysql" + monitor_password="mysql" + monitor_ping_interval="mysql" + monitor_ping_max_failures="mysql" + monitor_ping_timeout="mysql" + monitor_query_interval="mysql" + monitor_query_timeout="mysql" + monitor_read_only_interval="mysql" + monitor_read_only_max_timeout_count="mysql" + monitor_read_only_timeout="mysql" + monitor_replication_lag_interval="mysql" + monitor_replication_lag_timeout="mysql" + monitor_replication_lag_use_percona_heartbeat="mysql" + monitor_slave_lag_when_null="mysql" + monitor_threads_max="mysql" + monitor_threads_min="mysql" + monitor_threads_queue_maxsize="mysql" + monitor_username="mysql" + monitor_wait_timeout="mysql" + monitor_writer_is_also_reader="mysql" + multiplexing="mysql" + ping_interval_server_msec="mysql" + ping_timeout_server="mysql" + poll_timeout="mysql" + poll_timeout_on_failure="mysql" + query_cache_size_MB="mysql" + query_cache_stores_empty_result="mysql" + query_digests="mysql" + query_digests_lowercase="mysql" + query_digests_max_digest_length="mysql" + query_digests_max_query_length="mysql" + query_digests_no_digits="mysql" + query_digests_normalize_digest_text="mysql" + query_digests_replace_null="mysql" + query_digests_track_hostname="mysql" + query_processor_iterations="mysql" + query_processor_regex="mysql" + query_retries_on_failure="mysql" + reset_connection_algorithm="mysql" + server_capabilities="mysql" + server_version="mysql" + servers_stats="mysql" + session_idle_ms="mysql" + session_idle_show_processlist="mysql" + sessions_sort="mysql" + set_query_lock_on_hostgroup="mysql" + show_processlist_extended="mysql" + shun_on_failures="mysql" + shun_recovery_time_sec="mysql" + ssl_p2s_ca="mysql" + ssl_p2s_cert="mysql" + ssl_p2s_cipher="mysql" + ssl_p2s_key="mysql" + stacksize="mysql" + stats_time_backend_query="mysql" + stats_time_query_processor="mysql" + tcp_keepalive_time="mysql" + threads="mysql" + threshold_query_length="mysql" + threshold_resultset_size="mysql" + throttle_connections_per_sec_to_hostgroup="mysql" + throttle_max_bytes_per_second_to_client="mysql" + throttle_ratio_server_to_client="mysql" + use_tcp_keepalive="mysql" + verbose_query_error="mysql" + wait_timeout="mysql" +} +mysql_users: +( + { + username="user" + password="password" + active=1 + use_ssl=0 + default_hostgroup=0 + default_schema="schema" + schema_locked=1 + transaction_persistent=0 + fast_forward=1 + backend=1 + frontend=1 + max_connections=10 + comment="comm1" + } +) +mysql_query_rules: +( + { + rule_id=1 + active=1 + username="user" + schemaname="schema" + flagIN=0 + client_addr=".domain.com" + proxy_addr=".proxy.com" + proxy_port=3333 + digest="ABC1" + match_digest="ABC" + match_pattern="^SELECT *" + negate_match_pattern=0 + re_modifiers="CASE" + flagOUT=0 + replace_pattern="1" + destination_hostgroup=1 + cache_ttl=1 + cache_empty_result=1 + cache_timeout=100 + reconnect=1 + timeout=1 + retries=100 + delay=100 + next_query_flagIN=0 + mirror_flagOUT=0 + mirror_hostgroup=1 + error_msg="Error" + OK_msg="OK" + sticky_conn=0 + multiplex=0 + gtid_from_hostgroup=0 + log=0 + apply=0 + comment="comm1" + } +) +mysql_servers: +( + { + hostgroup_id=0 + hostname="127.0.0.1" + port=3306 + gtid_port=0 + status="ONLINE" + weight=1 + compression=0 + max_connections=1000 + max_replication_lag=0 + use_ssl=0 + max_latency_ms=0 + comment="comment2" + } +) +mysql_replication_hostgroups: +( + { + writer_hostgroup=10 + reader_hostgroup=20 + check_type="read_only" + comment="Master / Slave App 1" + } +) +mysql_group_replication_hostgroups: +( + { + writer_hostgroup=1 + backup_writer_hostgroup=2 + reader_hostgroup=3 + offline_hostgroup=4 + active=1 + max_writers=23 + writer_is_also_reader=1 + max_transactions_behind=1 + comment="comment" + } +) +mysql_galera_hostgroups: +( + { + writer_hostgroup=1 + backup_writer_hostgroup=2 + reader_hostgroup=3 + offline_hostgroup=4 + active=1 + max_writers=23 + writer_is_also_reader=1 + max_transactions_behind=1 + comment="comment" + } +) +mysql_aws_aurora_hostgroups: +( + { + writer_hostgroup=1 + reader_hostgroup=2 + active=1 + aurora_port=3 + domain_name=".domain.net" + max_lag_ms=20 + check_interval_ms=106 + check_timeout_ms=107 + writer_is_also_reader=1 + new_reader_weight=9 + add_lag_ms=10 + min_lag_ms=20 + lag_num_checks=1 + comment="comment" + } +) +scheduler: +( + { + id=1 + active=1 + interval_ms=1000 + filename="filename" + arg1="a1" + arg2="a2" + arg3="a3" + arg4="a4" + arg5="a5" + comment="comment" + } +) +proxysql_servers: +( + { + hostname="hostname" + port=3333 + weight=12 + comment="comment" + } +) diff --git a/test/tap/tests/select_config_file-t.cpp b/test/tap/tests/select_config_file-t.cpp new file mode 100644 index 000000000..6ec977a73 --- /dev/null +++ b/test/tap/tests/select_config_file-t.cpp @@ -0,0 +1,151 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "tap.h" +#include "command_line.h" + +#define MYSQL_QUERY(mysql, query) \ + do { \ + if (mysql_query(mysql, query)) { \ + fprintf(stderr, "File %s, line %d, Error: %s\n", \ + __FILE__, __LINE__, mysql_error(mysql)); \ + return exit_status(); \ + } \ + } while(0) + + +int show_variable(MYSQL *mysql, const std::string& var_name, std::string& var_value) { + char query[128]; + + snprintf(query, sizeof(query),"show variables like '%s'", var_name.c_str()); + if (mysql_query(mysql, query)) { + fprintf(stderr, "Failed to execute SHOW VARIABLES LIKE : no %d, %s\n", + mysql_errno(mysql), mysql_error(mysql)); + return -1; + } + + MYSQL_RES *result; + MYSQL_ROW row; + result = mysql_store_result(mysql); + + row = mysql_fetch_row(result); + var_value = row[1]; + mysql_free_result(result); +} + +int select_config_file(MYSQL* mysql, std::string& resultset) { + if (mysql_query(mysql, "select config file")) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysql)); + return exit_status(); + } + + MYSQL_RES *result; + MYSQL_ROW row; + result = mysql_store_result(mysql); + if (result) { + row = mysql_fetch_row(result); + resultset = row[0]; + mysql_free_result(result); + } else { + fprintf(stderr, "error\n"); + } + +} + + +int main(int argc, char** argv) { + CommandLine cl; + + if(cl.getEnv()) + return exit_status(); + + plan(1); + diag("Testing SELECT CONFIG FILE"); + + MYSQL* mysql = mysql_init(NULL); + if (!mysql) + return exit_status(); + + if (!mysql_real_connect(mysql, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysql)); + return exit_status(); + } + + MYSQL_QUERY(mysql, "delete from global_variables"); + MYSQL_QUERY(mysql, "delete from mysql_users"); + MYSQL_QUERY(mysql, "delete from mysql_servers"); + MYSQL_QUERY(mysql, "delete from mysql_query_rules"); + MYSQL_QUERY(mysql, "delete from mysql_replication_hostgroups"); + MYSQL_QUERY(mysql, "delete from mysql_group_replication_hostgroups"); + MYSQL_QUERY(mysql, "delete from mysql_galera_hostgroups"); + MYSQL_QUERY(mysql, "delete from mysql_aws_aurora_hostgroups"); + MYSQL_QUERY(mysql, "delete from scheduler"); + MYSQL_QUERY(mysql, "delete from proxysql_servers"); + + MYSQL_QUERY(mysql, "insert into proxysql_servers (hostname, port, weight, comment) values ('hostname', 3333, 12, 'comment');"); + MYSQL_QUERY(mysql, "insert into scheduler (id, active, interval_ms, filename, arg1, arg2, arg3, arg4, arg5, comment) values " + " (1,1,1000,'filename','a1','a2','a3','a4','a5','comment');"); + MYSQL_QUERY(mysql, "insert into mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, aurora_port, " + " domain_name, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, " + " add_lag_ms, min_lag_ms, lag_num_checks, comment) " + " values (1,2,1,3,'.domain.net',20,106,107,1,9,10,20,1,'comment');"); + MYSQL_QUERY(mysql, "insert into mysql_galera_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, " + " active, max_writers, writer_is_also_reader, max_transactions_behind, comment) values (1,2,3,4,1,23,1,1,'comment');"); + MYSQL_QUERY(mysql, "insert into mysql_group_replication_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, " + " active, max_writers, writer_is_also_reader, max_transactions_behind, comment) values (1,2,3,4,1,23,1,1,'comment');"); + MYSQL_QUERY(mysql, "insert into mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type, comment) " + " values (10,20,'read_only','Master / Slave App 1');"); + MYSQL_QUERY(mysql, "insert into mysql_servers (hostgroup_id, hostname, port, gtid_port, status, weight, compression, max_connections, " + " max_replication_lag, use_ssl, max_latency_ms, comment) values (0,'127.0.0.1',3306,0,'ONLINE',1,0,1000,0,0,0,'comment2');"); + MYSQL_QUERY(mysql, "insert into mysql_query_rules (rule_id, active, username, schemaname, flagIN, client_addr, proxy_addr, proxy_port, digest, " + " match_digest, match_pattern, negate_match_pattern, re_modifiers, flagOUT, replace_pattern, destination_hostgroup, cache_ttl, cache_empty_result, " + " cache_timeout, reconnect, timeout, retries, delay, next_query_flagIN, mirror_flagOUT, mirror_hostgroup, " + " error_msg, OK_msg, sticky_conn, multiplex, gtid_from_hostgroup, log, apply, comment) values " + " (1, 1, 'user', 'schema', 0, '.domain.com', '.proxy.com', 3333, 'ABC1', 'ABC', '^SELECT *', 0, 'CASE', 0, 1, 1, " + " 1, 1, 100, 1, 1, 100, 100, 0, 0, 1, 'Error', 'OK', 0, 0, 0, 0, 0, 'comm1')"); + MYSQL_QUERY(mysql, "insert into mysql_users (username, password, active, use_ssl, default_hostgroup, default_schema, schema_locked, " + " transaction_persistent, fast_forward, backend, frontend, max_connections, comment) values " + " ('user', 'password', 1, 0, 0, 'schema', 1, 0, 1, 1, 1, 10, 'comm1')"); + + MYSQL_QUERY(mysql, "update global_variables set variable_value='admin' where variable_name like 'admin-%'"); + MYSQL_QUERY(mysql, "update global_variables set variable_value='mysql' where variable_name like 'mysql-%'"); + + MYSQL_QUERY(mysql, "load mysql servers to runtime"); + + std::string resultset; + resultset.reserve(100000); + select_config_file(mysql, resultset); + + { + std::ifstream inFile; + inFile.open("./tests/proxysql_reference_select_config_file.cnf"); //open the input file + + std::stringstream strStream; + strStream << inFile.rdbuf(); //read the file + std::string str = strStream.str(); //str holds the content of the file + + ok(!str.compare(resultset), "Files are equal"); + } + + MYSQL_QUERY(mysql, "load mysql variables from disk"); + MYSQL_QUERY(mysql, "load admin variables from disk"); + MYSQL_QUERY(mysql, "load mysql users from disk"); + MYSQL_QUERY(mysql, "load mysql servers from disk"); + MYSQL_QUERY(mysql, "load scheduler from disk"); + MYSQL_QUERY(mysql, "load proxysql servers from disk"); + + mysql_close(mysql); + + return exit_status(); +} +