diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index 10208d17b..fbb3b8e39 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -124,6 +124,7 @@ class ProxySQL_Admin { int Read_Global_Variables_from_configfile(const char *prefix); int Read_MySQL_Users_from_configfile(); + int Read_MySQL_Query_Rules_from_configfile(); int Read_MySQL_Servers_from_configfile(); }; #endif /* __CLASS_PROXYSQL_ADMIN_H */ diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 08fd79868..b03a0fc61 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -671,6 +671,34 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query return false; } + if ( + (query_no_space_length==strlen("LOAD MYSQL QUERY RULES FROM CONFIG") && !strncasecmp("LOAD MYSQL QUERY RULES FROM CONFIG",query_no_space, query_no_space_length)) + ) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + int rows=0; + rows=SPA->Read_MySQL_Query_Rules_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql query rules from CONFIG\n"); + SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL, rows); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_MySQL_ERR(&sess->client_myds->myprot, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_MySQL_ERR(&sess->client_myds->myprot, (char *)"Config file unknown"); + } + return false; + } + if ( (query_no_space_length==strlen("SAVE MYSQL QUERY RULES FROM MEMORY") && !strncasecmp("SAVE MYSQL QUERY RULES FROM MEMORY",query_no_space, query_no_space_length)) || @@ -1398,11 +1426,13 @@ bool ProxySQL_Admin::init() { Read_Global_Variables_from_configfile("admin"); Read_Global_Variables_from_configfile("mysql"); Read_MySQL_Users_from_configfile(); + Read_MySQL_Query_Rules_from_configfile(); __insert_or_replace_disktable_select_maintable(); } else { if (GloVars.confFile->OpenFile(GloVars.config_file)==true) { Read_MySQL_Servers_from_configfile(); Read_MySQL_Users_from_configfile(); + Read_MySQL_Query_Rules_from_configfile(); Read_Global_Variables_from_configfile("admin"); Read_Global_Variables_from_configfile("mysql"); __insert_or_replace_disktable_select_maintable(); @@ -1937,15 +1967,34 @@ void ProxySQL_Admin::save_mysql_query_rules_from_runtime() { SQLite3_result * resultset=GloQPro->get_current_query_rules(); if (resultset==NULL) return; admindb->execute("DELETE FROM mysql_query_rules"); - char *a=(char *)"INSERT INTO mysql_query_rules VALUES (\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\")"; + //char *a=(char *)"INSERT INTO mysql_query_rules VALUES (\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\")"; + char *a=(char *)"INSERT INTO mysql_query_rules (rule_id, active, username, schemaname, flagIN, match_pattern, negate_match_pattern, flagOUT, replace_pattern, destination_hostgroup, cache_ttl, reconnect, timeout, delay, apply) VALUES (%s, %s, \"%s\", \"%s\", %s, \"%s\", %s, %s, %s, %s, %s, %s, %s, %s, %s)"; for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { SQLite3_row *r=*it; int arg_len=0; - for (int i=0; i<12; i++) { - arg_len+=strlen(r->fields[i]); + for (int i=0; i<15; i++) { + arg_len+=strlen(r->fields[i]+4); } char *query=(char *)malloc(strlen(a)+arg_len+32); - sprintf(query,a,r->fields[0],r->fields[1],r->fields[2],r->fields[3],r->fields[4],r->fields[5],r->fields[6],r->fields[7],r->fields[8],r->fields[9],r->fields[10],r->fields[11]); + char *buf1=(char *)strlen(r->fields[8])+8; + if (r->fields[8]) { + sprintf(buf1,"\"%s\"",r->fields[8]); + } else { + sprintf(buf1,"NULL"); + } + sprintf(query,a,r->fields[0],r->fields[1],r->fields[2],r->fields[3], + ( strcmp(r->fields[4],"-1")==0 ? "NULL" : r->fields[4] ), // flagIN + r->fields[5], // match_pattern + r->fields[6], // negate + ( strcmp(r->fields[7],"-1")==0 ? "NULL" : r->fields[7] ), // flagOUT + buf1, // replace_pattern + ( strcmp(r->fields[9],"-1")==0 ? "NULL" : r->fields[9] ), // destination_hostgroup + ( strcmp(r->fields[10],"-1")==0 ? "NULL" : r->fields[10] ), // cache_ttl + ( strcmp(r->fields[11],"-1")==0 ? "NULL" : r->fields[11] ), // reconnect + ( strcmp(r->fields[12],"-1")==0 ? "NULL" : r->fields[12] ), // timeout + ( strcmp(r->fields[13],"-1")==0 ? "NULL" : r->fields[13] ), // delay + ( strcmp(r->fields[14],"-1")==0 ? "NULL" : r->fields[14] ) // apply + ); //fprintf(stderr,"%s\n",query); admindb->execute(query); free(query); @@ -2462,6 +2511,113 @@ int ProxySQL_Admin::Read_MySQL_Users_from_configfile() { return rows; } +int ProxySQL_Admin::Read_MySQL_Query_Rules_from_configfile() { + const Setting& root = GloVars.confFile->cfg->getRoot(); + if (root.exists("mysql_query_rules")==false) return 0; + const Setting &mysql_query_rules = root["mysql_query_rules"]; + int count = mysql_query_rules.getLength(); + //fprintf(stderr, "Found %d users\n",count); + int i; + int rows=0; + admindb->execute("PRAGMA foreign_keys = OFF"); + char *q=(char *)"INSERT OR REPLACE INTO mysql_query_rules (rule_id, active, username, schemaname, flagIN, match_pattern, negate_match_pattern, flagOUT, replace_pattern, destination_hostgroup, cache_ttl, reconnect, timeout, delay, apply) VALUES (%d, %d, %s, %s, %s, %s, %d, %s, %s, %s, %s, %s, %s, %s, %d)"; + 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; + bool match_pattern_exists=false; + std::string match_pattern; + int negate_match_pattern=0; + int flagOUT=-1; + bool replace_pattern_exists=false; + std::string replace_pattern; + int destination_hostgroup=-1; + int cache_ttl=-1; + int reconnect=-1; + int timeout=-1; + int delay=-1; + int apply=0; + if (rule.lookupValue("rule_id", rule_id)==false) 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("match_pattern", match_pattern)) match_pattern_exists=true; + rule.lookupValue("negate_match_pattern", negate_match_pattern); + rule.lookupValue("flagOUT", flagOUT); + if (rule.lookupValue("replace_pattern", replace_pattern)) replace_pattern_exists=true; + rule.lookupValue("destination_hostgroup", destination_hostgroup); + rule.lookupValue("cache_ttl", cache_ttl); + rule.lookupValue("reconnect", reconnect); + rule.lookupValue("timeout", timeout); + rule.lookupValue("delay", delay); + rule.lookupValue("apply", apply); + //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 + + ( match_pattern_exists ? strlen(match_pattern.c_str()) : 0 ) + 4 + + strlen(std::to_string(negate_match_pattern).c_str()) + 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(reconnect).c_str()) + 4 + + strlen(std::to_string(timeout).c_str()) + 4 + + strlen(std::to_string(delay).c_str()) + 4 + + strlen(std::to_string(apply).c_str()) + 4 + + 40; + char *query=(char *)malloc(query_len); + if (username_exists) + username="\"" + username + "\""; + else + username = "NULL"; + if (schemaname_exists) + schemaname="\"" + schemaname + "\""; + else + schemaname = "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"; + sprintf(query, q, + rule_id, active, + username.c_str(), + schemaname.c_str(), + ( flagIN >= 0 ? std::to_string(flagIN).c_str() : "NULL") , + match_pattern.c_str(), + ( negate_match_pattern == 0 ? 0 : 1) , + ( 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") , + ( reconnect >= 0 ? std::to_string(reconnect).c_str() : "NULL") , + ( timeout >= 0 ? std::to_string(timeout).c_str() : "NULL") , + ( delay >= 0 ? std::to_string(delay).c_str() : "NULL") , + ( apply == 0 ? 0 : 1) + ); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + free(query); + rows++; + } + admindb->execute("PRAGMA foreign_keys = ON"); + return rows; +} + int ProxySQL_Admin::Read_MySQL_Servers_from_configfile() { const Setting& root = GloVars.confFile->cfg->getRoot(); if (root.exists("mysql_servers")==false) return 0; diff --git a/lib/Query_Processor.cpp b/lib/Query_Processor.cpp index 49281ad7f..452c79711 100644 --- a/lib/Query_Processor.cpp +++ b/lib/Query_Processor.cpp @@ -395,7 +395,7 @@ SQLite3_result * Query_Processor::get_stats_query_rules() { SQLite3_result * Query_Processor::get_current_query_rules() { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "Dumping current query rules, using Global version %d\n", version); - SQLite3_result *result=new SQLite3_result(13); + SQLite3_result *result=new SQLite3_result(16); spin_rdlock(&rwlock); QP_rule_t *qr1; result->add_column_definition(SQLITE_TEXT,"rule_id"); @@ -464,13 +464,13 @@ QP_out_t * Query_Processor::process_mysql_query(MySQL_Session *sess, void *ptr, proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 6, "query rule %d has no matching flagIN\n", qr->rule_id); continue; } - if (qr->username) { + if (qr->username && strlen(qr->username)) { if (strcmp(qr->username,sess->client_myds->myconn->userinfo->username)!=0) { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "query rule %d has no matching username\n", qr->rule_id); continue; } } - if (qr->schemaname) { + if (qr->schemaname && strlen(qr->schemaname)) { if (strcmp(qr->schemaname,sess->client_myds->myconn->userinfo->schemaname)!=0) { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "query rule %d has no matching schemaname\n", qr->rule_id); continue; diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index f38e7586b..73c9dacb2 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -149,6 +149,8 @@ MySQL_Connection::MySQL_Connection() { options.server_version=NULL; compression_pkt_id=0; mysql_result=NULL; + query.ptr=NULL; + query.length=0; proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "Creating new MySQL_Connection %p\n", this); }; diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index feaa6d988..8fb32e78e 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -96,6 +96,7 @@ MySQL_Data_Stream::MySQL_Data_Stream() { pkts_sent=0; client_addr=NULL; + sess=NULL; mysql_real_query.ptr=NULL; mysql_real_query.size=0; diff --git a/src/proxysql.cfg b/src/proxysql.cfg index 9f55afe78..5fa79b7d4 100644 --- a/src/proxysql.cfg +++ b/src/proxysql.cfg @@ -35,6 +35,7 @@ mysql_variables= monitor_connect_interval=200000 monitor_ping_interval=200000 ping_interval_server=1000 + commands_stats=true } @@ -56,16 +57,17 @@ mysql_servers = # }, { address="127.0.0.1" - port=3306 + port=21891 hostgroup=0 max_connections=200 }, # { address="127.0.0.2" , port=3306 , hostgroup=0, max_connections=5 }, - { address="127.0.0.1" , port=3306 , hostgroup=1 }, - { address="127.0.0.2" , port=3306 , hostgroup=1 }, - { address="127.0.0.3" , port=3306 , hostgroup=1 }, - { address="127.0.0.4" , port=3306 , hostgroup=1 }, - { address="/var/lib/mysql/mysql.sock" , port=0 , hostgroup=1 } + { address="127.0.0.1" , port=21892 , hostgroup=1 }, + { address="127.0.0.1" , port=21893 , hostgroup=1 } +# { address="127.0.0.2" , port=3306 , hostgroup=1 }, +# { address="127.0.0.3" , port=3306 , hostgroup=1 }, +# { address="127.0.0.4" , port=3306 , hostgroup=1 }, +# { address="/var/lib/mysql/mysql.sock" , port=0 , hostgroup=1 } ) @@ -91,6 +93,27 @@ mysql_users: ) + +#defines MySQL Query Rules +mysql_query_rules: +( + { + rule_id=1 + active=1 + match_pattern="^SELECT .* FOR UPDATE$" + destination_hostgroup=0 + apply=1 + }, + { + rule_id=2 + active=1 + match_pattern="^SELECT" + destination_hostgroup=1 + apply=1 + } +) + + # http://www.hyperrealm.com/libconfig/libconfig_manual.html#Configuration-File-Grammar # # Below is the BNF grammar for configuration files. Comments and include directives are not part of the grammar, so they are not included here.