You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
proxysql/lib/ProxySQL_Config.cpp

2339 lines
85 KiB

#include "proxysql_config.h"
#include "re2/re2.h"
#include "proxysql.h"
#include "cpp.h"
#include "sqlite3db.h"
#include "proxysql_debug.h"
#include <sstream>
#include <set>
#include <tuple>
#include <cstring>
#include <memory>
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 `<prefix>_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<std::tuple<std::string, int>> 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<std::tuple<int, std::string, int>> 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<std::tuple<std::string, int>> 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;
}