#include "proxysql_config.h" #include "re2/re2.h" #include "proxysql.h" #include "cpp.h" #include "sqlite3db.h" #include "proxysql_debug.h" #include #include #include #include #include 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"; 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; // Escape the double quotes in all the fields contents std::string esc_value { value }; RE2::GlobalReplace(&esc_value, "\"", "\\\\\""); ss << "\t\t" << name << "=" << dq << esc_value.c_str() << dq << "\n"; data += ss.str(); } /** * @brief Reads global variables from configuration file for a given module prefix. * * This function parses the configuration file section named `_variables` * (e.g., "mysql_variables", "pgsql_variables", "admin_variables") and inserts * the variables into the global_variables table in the format "prefix-variable_name". * * The function automatically strips duplicate module prefixes from variable names. * For example, if a user writes "mysql-log_unhealthy_connections" in the mysql_variables * section, the prefix "mysql-" is automatically removed, and the variable is stored * as "mysql-log_unhealthy_connections" (with a single prefix). * * @param prefix The module prefix: "mysql", "pgsql", or "admin". * @return Number of variables processed (successfully read and stored). * @retval 0 if the prefix_variables section does not exist in the config file. * * @note The function temporarily disables foreign keys for performance. * @note Variable values are converted to strings: booleans become "true"/"false", * integers are converted via std::to_string, and strings are used as-is. * * @par Example: * For prefix="mysql", the function reads the "mysql_variables" section from config. * If the config contains "mysql-log_unhealthy_connections=false", it's stored as * "mysql-log_unhealthy_connections" with value "false". * * @see ProxySQL_Config::Write_Global_Variables_to_configfile() */ 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; size_t prefix_len = strlen(prefix); admindb->execute("PRAGMA foreign_keys = OFF"); // Prepare statement once for all inserts auto [rc, stmt] = admindb->prepare_v2("INSERT OR REPLACE INTO global_variables VALUES (?1, ?2)"); if (rc != SQLITE_OK) { proxy_error("Failed to prepare statement for global_variables insert: %d\n", rc); admindb->execute("PRAGMA foreign_keys = ON"); free(groupname); return 0; } 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()); // Automatic prefix stripping: if variable already starts with "prefix-", remove it if (strncmp(n, prefix, prefix_len) == 0 && n[prefix_len] == '-') { n += prefix_len + 1; // Skip "prefix-" } // Construct full variable name std::string full_var_name = std::string(prefix) + "-" + n; rc = (*proxy_sqlite3_bind_text)(stmt.get(), 1, full_var_name.c_str(), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, admindb); rc = (*proxy_sqlite3_bind_text)(stmt.get(), 2, value_string.c_str(), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, admindb); SAFE_SQLITE3_STEP2(stmt.get()); rc = (*proxy_sqlite3_clear_bindings)(stmt.get()); ASSERT_SQLITE_OK(rc, admindb); rc = (*proxy_sqlite3_reset)(stmt.get()); ASSERT_SQLITE_OK(rc, admindb); } // Statement automatically finalized when stmt goes out of scope 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, "attributes", 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_Users_from_configfile(std::string& error) { 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(); if (!validate_backend_users(PROXYSQL_CONFIG_MYSQL_USERS, mysql_users, error)) { proxy_error("Admin: %s\n", error.c_str()); return -1; } 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, attributes, comment) VALUES ('%s', '%s', %d, %d, %d, '%s', %d, %d, %d, %d, '%s','%s')"; for (int 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=""; std::string attributes=""; user.lookupValue("username", username); 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("attributes", attributes); 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)+strlen(attributes.c_str())+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, attributes.c_str(), 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_Restapi_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 restapi_routes"; admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); if (error) { proxy_error("Error on read from restapi_router: %s\n", error); return -1; } else { if (sqlite_resultset) { data += "restapi:\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, "timeout_ms", r->fields[2], ""); addField(data, "method", r->fields[3], ""); addField(data, "uri", r->fields[4]); addField(data, "script", r->fields[5]); addField(data, "comment", r->fields[6]); data += "\t}"; isNext = true; } data += "\n)\n"; } } if (sqlite_resultset) delete sqlite_resultset; return 0; } int ProxySQL_Config::Read_Restapi_from_configfile() { const Setting& root = GloVars.confFile->cfg.getRoot(); if (root.exists("restapi")==false) return 0; const Setting &routes = root["restapi"]; int count = routes.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 restapi_routes VALUES (%d, %d, %d, '%s', '%s', '%s', '%s')"; for (i=0; i< count; i++) { const Setting &route = routes[i]; int id; int active=1; // variable for parsing timeout_ms int timeout_ms=0; std::string method; std::string uri; std::string script; std::string comment=""; // validate arguments if (route.lookupValue("id", id)==false) { proxy_error("Admin: detected a restapi route in config file without a mandatory id\n"); continue; } route.lookupValue("active", active); if (route.lookupValue("interval_ms", timeout_ms) == false) { route.lookupValue("timeout_ms", timeout_ms); } if (route.lookupValue("method", method)==false) { proxy_error("Admin: detected a restapi route in config file without a mandatory method\n"); continue; } if (route.lookupValue("uri", uri)==false) { proxy_error("Admin: detected a restapi route in config file without a mandatory uri\n"); continue; } if (route.lookupValue("script", script)==false) { proxy_error("Admin: detected a restapi route in config file without a mandatory script\n"); continue; } route.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(timeout_ms).c_str()) + strlen(method.c_str()) + strlen(uri.c_str()) + strlen(script.c_str()) + strlen(comment.c_str()) + 40; char *query=(char *)malloc(query_len); sprintf(query, q, id, active, timeout_ms, method.c_str(), uri.c_str(), script.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, "attributes", r->fields[33]); addField(data, "comment", r->fields[34]); 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, attributes, 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, %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; // attributes bool attributes_exists=false; std::string attributes {}; 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 (rule.lookupValue("attributes", attributes)) attributes_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 + ( attributes_exists ? strlen(attributes.c_str()) : 0 ) + 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 (attributes_exists) attributes="'" + attributes + "'"; else attributes = "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) , attributes.c_str(), 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; query = (char *)"SELECT * FROM mysql_hostgroup_attributes"; admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); if (error) { proxy_error("Error on read from mysql_hostgroup_attributes: %s\n", error); return -1; } else { if (sqlite_resultset) { data += "mysql_hostgroup_attributes:\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, "max_num_online_servers", r->fields[1], ""); addField(data, "autocommit", r->fields[2], ""); addField(data, "free_connections_pct", r->fields[3], ""); addField(data, "init_connect", r->fields[4]); addField(data, "multiplex", r->fields[5], ""); addField(data, "connection_warming", r->fields[6], ""); addField(data, "throttle_connections_per_sec", r->fields[7], ""); addField(data, "ignore_session_variables", r->fields[8]); addField(data, "hostgroup_settings", r->fields[9]); addField(data, "servers_defaults", r->fields[10]); addField(data, "comment", r->fields[11]); data += "\t}"; isNext = true; } data += "\n)\n"; } } if (sqlite_resultset) delete sqlite_resultset; return 0; } int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { 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(); if (!validate_backend_servers(PROXYSQL_CONFIG_MYSQL_SERVERS, mysql_servers, error)) { proxy_error("Admin: %s\n", error.c_str()); return -1; } 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)) { server.lookupValue("hostname", address); } if (!server.lookupValue("hostgroup", hostgroup)) { server.lookupValue("hostgroup_id", hostgroup); } server.lookupValue("port", port); server.lookupValue("gtid_port", gtid_port); 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++; } } // TODO: Add validation to mysql_replication_hostgroups similar to mysql_servers 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_servers_ssl_params")==true) { // mysql_servers_ssl_params const Setting &mysql_servers_ssl_params = root["mysql_servers_ssl_params"]; int count = mysql_servers_ssl_params.getLength(); char *q=(char *)"INSERT OR REPLACE INTO mysql_servers_ssl_params (hostname, port, username, ssl_ca, ssl_cert, ssl_key, ssl_capath, ssl_crl, ssl_crlpath, ssl_cipher, tls_version, comment) VALUES ('%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')"; for (i=0; i< count; i++) { const Setting &line = mysql_servers_ssl_params[i]; string hostname = ""; int port = 3306; string username = ""; string ssl_ca = ""; string ssl_cert = ""; string ssl_key = ""; string ssl_capath = ""; string ssl_crl = ""; string ssl_crlpath = ""; string ssl_cipher = ""; string tls_version = ""; std::string comment=""; if (line.lookupValue("hostname", hostname)==false) { proxy_error("Admin: detected a mysql_servers_ssl_params in config file without a mandatory hostname\n"); continue; } line.lookupValue("port", port); line.lookupValue("username", username); line.lookupValue("ssl_ca", ssl_ca); line.lookupValue("ssl_cert", ssl_cert); line.lookupValue("ssl_key", ssl_key); line.lookupValue("ssl_capath", ssl_capath); line.lookupValue("ssl_crl", ssl_crl); line.lookupValue("ssl_crlpath", ssl_crlpath); line.lookupValue("ssl_cipher", ssl_cipher); line.lookupValue("tls_version", tls_version); line.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); char *query=(char *)malloc( strlen(q) + hostname.length() + username.length() + ssl_ca.length() + ssl_cert.length() + ssl_key.length() + ssl_capath.length() + ssl_crl.length() + ssl_crlpath.length() + ssl_cipher.length() + tls_version.length() + strlen(o) + 32); sprintf(query, q, hostname.c_str() , port , username.c_str() , ssl_ca.c_str() , ssl_cert.c_str() , ssl_key.c_str() , ssl_capath.c_str() , ssl_crl.c_str() , ssl_crlpath.c_str() , ssl_cipher.c_str() , tls_version.c_str() , o); admindb->execute(query); if (o!=o1) free(o); free(o1); 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++; } } if (root.exists("mysql_hostgroup_attributes") == true) { const Setting &mysql_hostgroup_attributes = root["mysql_hostgroup_attributes"]; int count = mysql_hostgroup_attributes.getLength(); for (i = 0; i < count; i++) { const Setting &hostgroup_attributes = mysql_hostgroup_attributes[i]; bool is_first_field = true; int integer_val = 0; std::string string_val = ""; std::string fields = ""; std::string values = ""; auto process_field = [&](const std::string &field_name, const std::string &field_value, int is_int) { if (!is_first_field) { fields += ", "; values += ", "; } else { is_first_field = false; } fields += field_name; if (is_int) { values += field_value; } else { char *cs = strdup(field_value.c_str()); char *ecs = escape_string_single_quotes(cs, false); values += std::string("'") + ecs + "'"; if (cs != ecs) free(cs); free(ecs); } }; // Only inserting/updating fields which are in configuration file. // Fields default will be from table schema. // Parsing integer field if (hostgroup_attributes.lookupValue("hostgroup_id", integer_val) ) { process_field("hostgroup_id", to_string(integer_val), true); } else { proxy_error("Admin: detected a mysql_hostgroup_attributes in config file without a mandatory hostgroup_id.\n"); continue; } if (hostgroup_attributes.lookupValue("max_num_online_servers", integer_val)) { process_field("max_num_online_servers", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("autocommit", integer_val)) { process_field("autocommit", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("free_connections_pct", integer_val)) { process_field("free_connections_pct", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("multiplex", integer_val)) { process_field("multiplex", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("connection_warming", integer_val)) { process_field("connection_warming", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("throttle_connections_per_sec", integer_val)) { process_field("throttle_connections_per_sec", to_string(integer_val), true); } // Parsing string field if (hostgroup_attributes.lookupValue("init_connect", string_val)) { process_field("init_connect", string_val, false); } if (hostgroup_attributes.lookupValue("ignore_session_variables", string_val)) { process_field("ignore_session_variables", string_val, false); } if (hostgroup_attributes.lookupValue("hostgroup_settings", string_val)) { process_field("hostgroup_settings", string_val, false); } if (hostgroup_attributes.lookupValue("servers_defaults", string_val)) { process_field("servers_defaults", string_val, false); } if (hostgroup_attributes.lookupValue("comment", string_val)) { process_field("comment", string_val, false); } std::string s_query = "INSERT OR REPLACE INTO mysql_hostgroup_attributes ("; s_query += fields + ") VALUES (" + values + ")"; //fprintf(stderr, "%s\n", s_query.c_str()); if (admindb->execute(s_query.c_str()) == false) { proxy_error("Admin: detected a mysql_hostgroup_attributes invalid value. Failed to insert in the table.\n"); continue; } 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(std::string& error) { 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 & proxysql_servers = root["proxysql_servers"]; int count = proxysql_servers.getLength(); if (!validate_proxysql_servers(proxysql_servers, error)) { proxy_error("Admin: %s\n", error.c_str()); return -1; } 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 = proxysql_servers[i]; std::string address; int port; int weight=0; std::string comment=""; if (!server.lookupValue("address", address)) { server.lookupValue("hostname", address); } server.lookupValue("port", port); 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; } int ProxySQL_Config::Write_PgSQL_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 pgsql_servers"; admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); if (error) { proxy_error("Error on read from pgsql_servers : %s\n", error); return -1; } else { if (sqlite_resultset) { data += "pgsql_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, "status", r->fields[3]); addField(data, "weight", r->fields[4], ""); addField(data, "compression", r->fields[5], ""); addField(data, "max_connections", r->fields[6], ""); addField(data, "max_replication_lag", r->fields[7], ""); addField(data, "use_ssl", r->fields[8], ""); addField(data, "max_latency_ms", r->fields[9], ""); addField(data, "comment", r->fields[10]); data += "\t}"; isNext = true; } data += "\n)\n"; } } if (sqlite_resultset) delete sqlite_resultset; query = (char*)"SELECT * FROM pgsql_replication_hostgroups"; admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); if (error) { proxy_error("Error on read from pgsql_replication_hostgroups : %s\n", error); return -1; } else { if (sqlite_resultset) { data += "pgsql_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; return 0; } int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { const Setting& root = GloVars.confFile->cfg.getRoot(); int i; int rows = 0; admindb->execute("PRAGMA foreign_keys = OFF"); if (root.exists("pgsql_servers") == true) { const Setting& pgsql_servers = root["pgsql_servers"]; int count = pgsql_servers.getLength(); if (!validate_backend_servers(PROXYSQL_CONFIG_PGSQL_SERVERS, pgsql_servers, error)) { proxy_error("Admin: %s\n", error.c_str()); return -1; } char* q = (char*)"INSERT OR REPLACE INTO pgsql_servers (hostname, port, hostgroup_id, compression, weight, status, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment) VALUES (\"%s\", %d, %d, %d, %d, \"%s\", %d, %d, %d, %d, '%s')"; for (i = 0; i < count; i++) { const Setting& server = pgsql_servers[i]; std::string address; std::string status = "ONLINE"; int port = 5432; 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)) { server.lookupValue("hostname", address); } if (!server.lookupValue("hostgroup", hostgroup)) { server.lookupValue("hostgroup_id", hostgroup); } server.lookupValue("port", port); 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, 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++; } } // TODO: Add validation to pgsql_replication_hostgroups similar to pgsql_servers if (root.exists("pgsql_replication_hostgroups") == true) { const Setting& pgsql_replication_hostgroups = root["pgsql_replication_hostgroups"]; int count = pgsql_replication_hostgroups.getLength(); char* q = (char*)"INSERT OR REPLACE INTO pgsql_replication_hostgroups (writer_hostgroup, reader_hostgroup, comment, check_type) VALUES (%d, %d, '%s', '%s')"; for (i = 0; i < count; i++) { const Setting& line = pgsql_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 pgsql_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 pgsql_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++; } } admindb->execute("PRAGMA foreign_keys = ON"); return rows; } int ProxySQL_Config::Write_PgSQL_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 pgsql_users"; admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); if (error) { proxy_error("Error on read from pgsql_users: %s\n", error); return -1; } else { if (sqlite_resultset) { data += "pgsql_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, "transaction_persistent", r->fields[5], ""); addField(data, "fast_forward", r->fields[6], ""); addField(data, "backend", r->fields[7], ""); addField(data, "frontend", r->fields[8], ""); addField(data, "max_connections", r->fields[9], ""); addField(data, "attributes", r->fields[10]); addField(data, "comment", r->fields[11]); data += "\t}"; isNext = true; } data += "\n)\n"; } } if (sqlite_resultset) delete sqlite_resultset; return 0; } int ProxySQL_Config::Read_PgSQL_Users_from_configfile(std::string& error) { const Setting& root = GloVars.confFile->cfg.getRoot(); if (root.exists("pgsql_users") == false) return 0; const Setting& pgsql_users = root["pgsql_users"]; int count = pgsql_users.getLength(); if (!validate_backend_users(PROXYSQL_CONFIG_PGSQL_USERS, pgsql_users, error)) { proxy_error("Admin: %s\n", error.c_str()); return -1; } int rows = 0; admindb->execute("PRAGMA foreign_keys = OFF"); char* q = (char*)"INSERT OR REPLACE INTO pgsql_users (username, password, active, use_ssl, default_hostgroup, transaction_persistent, fast_forward, max_connections, attributes, comment) VALUES ('%s', '%s', %d, %d, %d, %d, %d, %d, '%s','%s')"; for (int i = 0; i < count; i++) { const Setting& user = pgsql_users[i]; std::string username; std::string password = ""; int active = 1; int use_ssl = 0; int default_hostgroup = 0; int transaction_persistent = 1; int fast_forward = 0; int max_connections = 10000; std::string comment = ""; std::string attributes = ""; user.lookupValue("username", username); 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("transaction_persistent", transaction_persistent); user.lookupValue("fast_forward", fast_forward); user.lookupValue("max_connections", max_connections); user.lookupValue("attributes", attributes); 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) + strlen(attributes.c_str()) + 128); sprintf(query, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, transaction_persistent, fast_forward, max_connections, attributes.c_str(), 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_PgSQL_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 pgsql_query_rules"; admindb->execute_statement(query, &error, &cols, &affected_rows, &sqlite_resultset); if (error) { proxy_error("Error on read from pgsql_query_rules : %s\n", error); return -1; } else { if (sqlite_resultset) { std::string prefix; data += "pgsql_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, "database", 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, "log", r->fields[30], ""); addField(data, "apply", r->fields[31], ""); addField(data, "attributes", 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_PgSQL_Query_Rules_from_configfile() { const Setting& root = GloVars.confFile->cfg.getRoot(); if (root.exists("pgsql_query_rules") == false) return 0; const Setting& pgsql_query_rules = root["pgsql_query_rules"]; int count = pgsql_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 pgsql_query_rules (rule_id, active, username, database, 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, log, apply, attributes, 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, %d, %s, %s)"; for (i = 0; i < count; i++) { const Setting& rule = pgsql_query_rules[i]; int rule_id; int active = 1; bool username_exists = false; std::string username; bool database_exists = false; std::string database; 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; // variable for parsing log int log = -1; int apply = 0; // attributes bool attributes_exists = false; std::string attributes{}; bool comment_exists = false; std::string comment; // validate arguments if (rule.lookupValue("rule_id", rule_id) == false) { proxy_error("Admin: detected a pgsql_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("database", database)) database_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("log", log); rule.lookupValue("apply", apply); if (rule.lookupValue("comment", comment)) comment_exists = true; if (rule.lookupValue("attributes", attributes)) attributes_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 + (database_exists ? strlen(database.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(log).c_str()) + 4 + strlen(std::to_string(apply).c_str()) + 4 + (attributes_exists ? strlen(attributes.c_str()) : 0) + 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 (database_exists) database = "\"" + database + "\""; else database = "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 (attributes_exists) attributes = "'" + attributes + "'"; else attributes = "NULL"; if (comment_exists) comment = "'" + comment + "'"; else comment = "NULL"; sprintf(query, q, rule_id, active, username.c_str(), database.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"), (log >= 0 ? std::to_string(log).c_str() : "NULL"), (apply == 0 ? 0 : 1), attributes.c_str(), comment.c_str() ); //fprintf(stderr, "%s\n", query); admindb->execute(query); free(query); rows++; } admindb->execute("PRAGMA foreign_keys = ON"); return rows; } bool ProxySQL_Config::validate_backend_users(proxysql_config_type type, const Setting& config, std::string& error) { std::set> pk_set; int count = config.getLength(); for (int i = 0; i < count; i++) { const Setting& user = config[i]; std::string config; std::string username; int backend = 1; // default backend value config = (type == PROXYSQL_CONFIG_MYSQL_USERS) ? "mysql_users" : "pgsql_users"; if (user.lookupValue("username", username) == false) { error = "mandatory field username missing from a " + config + " entry in config file"; return false; } user.lookupValue("backend", backend); auto key = std::make_tuple(username, backend); if (pk_set.find(key) != pk_set.end()) { error = "duplicate entries found in " + config + " in config file"; return false; } pk_set.insert(key); } return true; } bool ProxySQL_Config::validate_backend_servers(proxysql_config_type type, const Setting& config, std::string& error) { std::set> pk_set; int count = config.getLength(); for (int i = 0; i < count; i++) { const Setting& server = config[i]; std::string config; std::string address; int port; int hostgroup; config = (type == PROXYSQL_CONFIG_MYSQL_SERVERS) ? "mysql_servers" : "pgsql_servers"; port = (type == PROXYSQL_CONFIG_MYSQL_SERVERS) ? 3306 : 5432; if (server.lookupValue("hostgroup", hostgroup) == false) { if (server.lookupValue("hostgroup_id", hostgroup) == false) { error = "mandatory field hostgroup_id missing from a " + config + " entry in config file"; return false; } } if (server.lookupValue("address", address) == false) { if (server.lookupValue("hostname", address) == false) { error = "mandatory field hostname missing from a " + config + " entry in config file"; return false; } } server.lookupValue("port", port); auto key = std::make_tuple(hostgroup, address, port); if (pk_set.find(key) != pk_set.end()) { error = "duplicate entries found in " + config + " in config file"; return false; } pk_set.insert(key); } return true; } bool ProxySQL_Config::validate_proxysql_servers(const Setting& config, std::string& error) { std::set> pk_set; int count = config.getLength(); for (int i = 0; i < count; i++) { const Setting& server = config[i]; std::string address; int port; if (server.lookupValue("address", address) == false) { if (server.lookupValue("hostname", address) == false) { error = "mandatory field hostname missing from a proxysql_servers entry in config file"; return false; } } if (server.lookupValue("port", port) == false) { error = "mandatory field port missing from a proxysql_servers entry in config file"; return false; } auto key = std::make_tuple(address, port); if (pk_set.find(key) != pk_set.end()) { error = "duplicate entries found in proxysql_servers in config file"; return false; } pk_set.insert(key); } return true; }