mirror of https://github.com/sysown/proxysql
- SQL_SAFE_UPDATES - SQL_SELECT_LIMIT - SQL_SQL_MODE - SQL_TIME_ZONE - CHARACTER_SET_RESULTS - SQL_TRANSACTION_ISOLATION - SQL_TRANSACTION_READ - SQL_SESSION_TRACK_GTIDS - SQL_SQL_AUTO_IS_NULLpull/2582/head
parent
70b9f25937
commit
1fb5c9cf06
@ -0,0 +1,52 @@
|
||||
#ifndef MYSQL_VARIABLES_H
|
||||
#define MYSQL_VARIABLES_H
|
||||
|
||||
#include "proxysql.h"
|
||||
#include "cpp.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
class MySQL_Session;
|
||||
|
||||
class Updater {
|
||||
public:
|
||||
virtual bool verify_variables(MySQL_Session* session, int idx) = 0;
|
||||
virtual bool update_server_variable(MySQL_Session* session, int idx, int &_rc) = 0;
|
||||
};
|
||||
|
||||
class Generic_Updater : public Updater {
|
||||
public:
|
||||
bool verify_variables(MySQL_Session* session, int idx);
|
||||
bool update_server_variable(MySQL_Session* session, int idx, int &_rc);
|
||||
};
|
||||
|
||||
class MySQL_Variables {
|
||||
MySQL_Session* session;
|
||||
public:
|
||||
bool verify_generic_variable(uint32_t *be_int, char **be_var, char *def, uint32_t *fe_int, char *fe_var, enum session_status next_sess_status);
|
||||
static int session_by_var[SQL_NAME_LAST];
|
||||
static int var_by_session[NONE];
|
||||
static bool quotes[SQL_NAME_LAST];
|
||||
static bool set_transaction[SQL_NAME_LAST];
|
||||
Updater* updaters[SQL_NAME_LAST];
|
||||
|
||||
MySQL_Variables(MySQL_Session* session);
|
||||
virtual ~MySQL_Variables();
|
||||
|
||||
void client_set_value(int idx, const char* value);
|
||||
const char* client_get_value(int idx);
|
||||
uint32_t client_get_hash(int idx);
|
||||
|
||||
void server_set_value(int idx, const char* value);
|
||||
const char* server_get_value(int idx);
|
||||
uint32_t server_get_hash(int idx);
|
||||
|
||||
bool verify_variable(int idx);
|
||||
bool update_variable(int &_rc);
|
||||
|
||||
};
|
||||
|
||||
#endif // #ifndef MYSQL_VARIABLES_H
|
||||
|
||||
@ -0,0 +1,230 @@
|
||||
#include "MySQL_Variables.h"
|
||||
#include "proxysql.h"
|
||||
|
||||
#include "MySQL_Session.h"
|
||||
#include "MySQL_Data_Stream.h"
|
||||
#include "SpookyV2.h"
|
||||
|
||||
int MySQL_Variables::session_by_var[SQL_NAME_LAST] = {
|
||||
SETTING_SQL_SAFE_UPDATES,
|
||||
SETTING_SQL_SELECT_LIMIT,
|
||||
SETTING_SQL_MODE,
|
||||
SETTING_TIME_ZONE,
|
||||
SETTING_CHARACTER_SET_RESULTS,
|
||||
SETTING_ISOLATION_LEVEL,
|
||||
SETTING_TRANSACTION_READ,
|
||||
SETTING_SESSION_TRACK_GTIDS,
|
||||
SETTING_SQL_AUTO_IS_NULL
|
||||
/* SETTING_COLLATION_CONNECTION,
|
||||
SETTING_NET_WRITE_TIMEOUT,
|
||||
SETTING_MAX_JOIN_SIZE*/
|
||||
};
|
||||
|
||||
bool MySQL_Variables::quotes[SQL_NAME_LAST] = {
|
||||
true, // SQL_SAFE_UPDATES
|
||||
true, // SQL_SELECT_LIMIT
|
||||
false, // SQL_MODE
|
||||
false, // SQL_TIME_ZONE
|
||||
true, // CHARACTER_SET_RESULTS
|
||||
false, // ISOLATION_LEVEL
|
||||
false, // TRANSACTION_READ
|
||||
true, // SESSION_TRACK_GTIDS
|
||||
true // SQL_AUTO_IS_NULL
|
||||
/* false, // COLLATION_CONNECTION
|
||||
true, // NET_WRITE_TIMEOUT
|
||||
true // MAX_JOIN_SIZE*/
|
||||
};
|
||||
|
||||
bool MySQL_Variables::set_transaction[SQL_NAME_LAST] = {
|
||||
false, // SQL_SAFE_UPDATES
|
||||
false, // SQL_SELECT_LIMIT
|
||||
false, // SQL_MODE
|
||||
false, // SQL_TIME_ZONE
|
||||
false, // CHARACTER_SET_RESULTS
|
||||
true, // ISOLATION_LEVEL
|
||||
true, // TRANSACTION_READ
|
||||
false, // SESSION_TRACK_GTIDS
|
||||
false // SQL_AUTO_IS_NULL
|
||||
/* false, // COLLATION_CONNECTION
|
||||
false, // NET_WRITE_TIMEOUT
|
||||
false // MAX_JOIN_SIZE */
|
||||
};
|
||||
|
||||
int MySQL_Variables::var_by_session[NONE] = {
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_SQL_MODE,
|
||||
SQL_TIME_ZONE,
|
||||
SQL_ISOLATION_LEVEL,
|
||||
SQL_TRANSACTION_READ,
|
||||
SQL_CHARACTER_SET_RESULTS,
|
||||
SQL_SESSION_TRACK_GTIDS,
|
||||
SQL_SQL_AUTO_IS_NULL,
|
||||
SQL_SELECT_LIMIT,
|
||||
SQL_SAFE_UPDATES,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST,
|
||||
SQL_NAME_LAST
|
||||
};
|
||||
|
||||
MySQL_Variables::MySQL_Variables(MySQL_Session* _session) {
|
||||
assert(_session);
|
||||
session = _session;
|
||||
|
||||
for (auto i = 0; i < SQL_NAME_LAST; i++) {
|
||||
switch(i) {
|
||||
case SQL_SAFE_UPDATES:
|
||||
case SQL_SELECT_LIMIT:
|
||||
case SQL_SQL_MODE:
|
||||
case SQL_TIME_ZONE:
|
||||
case SQL_CHARACTER_SET_RESULTS:
|
||||
case SQL_ISOLATION_LEVEL:
|
||||
case SQL_TRANSACTION_READ:
|
||||
case SQL_SESSION_TRACK_GTIDS:
|
||||
case SQL_SQL_AUTO_IS_NULL:
|
||||
updaters[i] = new Generic_Updater();
|
||||
break;
|
||||
default:
|
||||
proxy_error("Wrong variable index\n");
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MySQL_Variables::~MySQL_Variables() {
|
||||
for (auto u : updaters)
|
||||
delete u;
|
||||
}
|
||||
|
||||
void MySQL_Variables::client_set_value(int idx, const char* value) {
|
||||
session->client_myds->myconn->variables[idx].hash = SpookyHash::Hash32(value,strlen(value),10);
|
||||
|
||||
if (session->client_myds->myconn->variables[idx].value) {
|
||||
free(session->client_myds->myconn->variables[idx].value);
|
||||
}
|
||||
session->client_myds->myconn->variables[idx].value = strdup(value);
|
||||
}
|
||||
|
||||
const char* MySQL_Variables::client_get_value(int idx) {
|
||||
return session->client_myds->myconn->variables[idx].value;
|
||||
}
|
||||
|
||||
uint32_t MySQL_Variables::client_get_hash(int idx) {
|
||||
return session->client_myds->myconn->variables[idx].hash;
|
||||
}
|
||||
|
||||
void MySQL_Variables::server_set_value(int idx, const char* value) {
|
||||
session->mybe->server_myds->myconn->variables[idx].hash = SpookyHash::Hash32(value,strlen(value),10);
|
||||
|
||||
if (session->mybe->server_myds->myconn->variables[idx].value) {
|
||||
free(session->mybe->server_myds->myconn->variables[idx].value);
|
||||
}
|
||||
session->mybe->server_myds->myconn->variables[idx].value = strdup(value);
|
||||
}
|
||||
|
||||
const char* MySQL_Variables::server_get_value(int idx) {
|
||||
return session->mybe->server_myds->myconn->variables[idx].value;
|
||||
}
|
||||
|
||||
uint32_t MySQL_Variables::server_get_hash(int idx) {
|
||||
return session->mybe->server_myds->myconn->variables[idx].hash;
|
||||
}
|
||||
|
||||
bool MySQL_Variables::verify_generic_variable(uint32_t *be_int, char **be_var, char *def, uint32_t *fe_int, char *fe_var, enum session_status next_sess_status) {
|
||||
// be_int = backend int (hash)
|
||||
// be_var = backend value
|
||||
// def = default
|
||||
// fe_int = frontend int (has)
|
||||
// fe_var = frontend value
|
||||
if (*be_int == 0) {
|
||||
// it is the first time we use this backend. Set value to default
|
||||
if (*be_var) {
|
||||
free(*be_var);
|
||||
*be_var = NULL;
|
||||
}
|
||||
*be_var = strdup(def);
|
||||
uint32_t tmp_int = SpookyHash::Hash32(*be_var, strlen(*be_var), 10);
|
||||
*be_int = tmp_int;
|
||||
}
|
||||
if (*fe_int) {
|
||||
if (*fe_int != *be_int) {
|
||||
{
|
||||
*be_int = *fe_int;
|
||||
if (*be_var) {
|
||||
free(*be_var);
|
||||
*be_var = NULL;
|
||||
}
|
||||
if (fe_var) {
|
||||
*be_var = strdup(fe_var);
|
||||
}
|
||||
}
|
||||
switch(session->status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility
|
||||
case PROCESSING_QUERY:
|
||||
session->previous_status.push(PROCESSING_QUERY);
|
||||
break;
|
||||
case PROCESSING_STMT_PREPARE:
|
||||
session->previous_status.push(PROCESSING_STMT_PREPARE);
|
||||
break;
|
||||
case PROCESSING_STMT_EXECUTE:
|
||||
session->previous_status.push(PROCESSING_STMT_EXECUTE);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
session->set_status(next_sess_status);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MySQL_Variables::update_variable(int &_rc) {
|
||||
auto idx = MySQL_Variables::var_by_session[session->status];
|
||||
updaters[idx]->update_server_variable(session, idx, _rc);
|
||||
}
|
||||
|
||||
bool MySQL_Variables::verify_variable(int idx) {
|
||||
int rc = 0;
|
||||
auto ret = updaters[idx]->verify_variables(session, idx);
|
||||
if (ret)
|
||||
update_variable(rc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Generic_Updater::verify_variables(MySQL_Session* session, int idx) {
|
||||
auto ret = session->mysql_variables->verify_generic_variable(
|
||||
&session->mybe->server_myds->myconn->variables[idx].hash,
|
||||
&session->mybe->server_myds->myconn->variables[idx].value,
|
||||
mysql_thread___default_sql_safe_updates,
|
||||
&session->client_myds->myconn->variables[idx].hash,
|
||||
session->client_myds->myconn->variables[idx].value,
|
||||
static_cast<session_status>(MySQL_Variables::session_by_var[idx])
|
||||
);
|
||||
}
|
||||
|
||||
bool Generic_Updater::update_server_variable(MySQL_Session* session, int idx, int &_rc) {
|
||||
bool q = MySQL_Variables::quotes[idx];
|
||||
bool st = MySQL_Variables::set_transaction[idx];
|
||||
auto ret = session->handler_again___status_SETTING_GENERIC_VARIABLE(&_rc, Variable::set_name[idx], session->mysql_variables->server_get_value(idx), q, st);
|
||||
return ret;
|
||||
}
|
||||
@ -0,0 +1,442 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <mysql.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <pthread.h>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include "json.hpp"
|
||||
|
||||
#include "tap.h"
|
||||
#include "command_line.h"
|
||||
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
struct TestCase {
|
||||
std::string command;
|
||||
json expected_vars;
|
||||
};
|
||||
|
||||
std::vector<TestCase> testCases;
|
||||
|
||||
#define MAX_LINE 1024
|
||||
|
||||
int readTestCases(const std::string& fileName) {
|
||||
FILE* fp = fopen(fileName.c_str(), "r");
|
||||
if (!fp) return 0;
|
||||
|
||||
char buf[MAX_LINE], col1[MAX_LINE], col2[MAX_LINE];
|
||||
int n = 0;
|
||||
for(;;) {
|
||||
if (fgets(buf, sizeof(buf), fp) == NULL) break;
|
||||
n = sscanf(buf, " \"%[^\"]\", \"%[^\"]\"", col1, col2);
|
||||
if (n == 0) break;
|
||||
|
||||
char *p = col2;
|
||||
while(*p++) if(*p == '\'') *p = '\"';
|
||||
|
||||
json vars = json::parse(col2);
|
||||
testCases.push_back({col1, vars});
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned long long monotonic_time() {
|
||||
struct timespec ts;
|
||||
//clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); // this is faster, but not precise
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (((unsigned long long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000);
|
||||
}
|
||||
|
||||
struct cpu_timer
|
||||
{
|
||||
cpu_timer() {
|
||||
begin = monotonic_time();
|
||||
}
|
||||
~cpu_timer()
|
||||
{
|
||||
unsigned long long end = monotonic_time();
|
||||
std::cerr << double( end - begin ) / 1000000 << " secs.\n" ;
|
||||
begin=end-begin;
|
||||
};
|
||||
unsigned long long begin;
|
||||
};
|
||||
|
||||
int queries_per_connections=1;
|
||||
unsigned int num_threads=1;
|
||||
int count=0;
|
||||
char *username=NULL;
|
||||
char *password=NULL;
|
||||
char *host=(char *)"localhost";
|
||||
int port=3306;
|
||||
int multiport=1;
|
||||
char *schema=(char *)"information_schema";
|
||||
int silent = 0;
|
||||
int sysbench = 0;
|
||||
int local=0;
|
||||
int queries=0;
|
||||
int uniquequeries=0;
|
||||
int histograms=-1;
|
||||
unsigned int g_connect_OK=0;
|
||||
unsigned int g_connect_ERR=0;
|
||||
unsigned int g_select_OK=0;
|
||||
unsigned int g_select_ERR=0;
|
||||
|
||||
unsigned int g_passed=0;
|
||||
unsigned int g_failed=0;
|
||||
|
||||
unsigned int status_connections = 0;
|
||||
unsigned int connect_phase_completed = 0;
|
||||
unsigned int query_phase_completed = 0;
|
||||
|
||||
__thread int g_seed;
|
||||
|
||||
inline int fastrand() {
|
||||
g_seed = (214013*g_seed+2531011);
|
||||
return (g_seed>>16)&0x7FFF;
|
||||
}
|
||||
|
||||
void parseResultJsonColumn(MYSQL_RES *result, json& j) {
|
||||
if(!result) return;
|
||||
MYSQL_ROW row;
|
||||
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
j = json::parse(row[0]);
|
||||
}
|
||||
|
||||
void parseResult(MYSQL_RES *result, json& j) {
|
||||
if(!result) return;
|
||||
MYSQL_ROW row;
|
||||
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
j[row[0]] = row[1];
|
||||
}
|
||||
|
||||
void dumpResult(MYSQL_RES *result) {
|
||||
if(!result) return;
|
||||
MYSQL_ROW row;
|
||||
|
||||
int num_fields = mysql_num_fields(result);
|
||||
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
for(int i = 0; i < num_fields; i++)
|
||||
{
|
||||
printf("%s ", row[i] ? row[i] : "NULL");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void queryVariables(MYSQL *mysql, json& j) {
|
||||
char *query = (char*)"SELECT * FROM performance_schema.session_variables WHERE variable_name IN "
|
||||
" ('hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'autocommit', 'sql_auto_is_null', "
|
||||
" 'sql_safe_updates', 'session_track_gtids', 'max_join_size', 'net_write_timeout', 'sql_select_limit', "
|
||||
" 'sql_select_limit', 'character_set_results', 'transaction_isolation', 'transaction_read_only', 'session_track_gtids', "
|
||||
" 'sql_auto_is_null');";
|
||||
if (mysql_query(mysql, query)) {
|
||||
if (silent==0) {
|
||||
fprintf(stderr,"%s\n", mysql_error(mysql));
|
||||
}
|
||||
} else {
|
||||
MYSQL_RES *result = mysql_store_result(mysql);
|
||||
parseResult(result, j);
|
||||
|
||||
mysql_free_result(result);
|
||||
__sync_fetch_and_add(&g_select_OK,1);
|
||||
}
|
||||
}
|
||||
|
||||
void queryInternalStatus(MYSQL *mysql, json& j) {
|
||||
char *query = (char*)"PROXYSQL INTERNAL SESSION";
|
||||
|
||||
if (mysql_query(mysql, query)) {
|
||||
if (silent==0) {
|
||||
fprintf(stderr,"%s\n", mysql_error(mysql));
|
||||
}
|
||||
} else {
|
||||
MYSQL_RES *result = mysql_store_result(mysql);
|
||||
parseResultJsonColumn(result, j);
|
||||
|
||||
mysql_free_result(result);
|
||||
__sync_fetch_and_add(&g_select_OK,1);
|
||||
}
|
||||
|
||||
// value types in mysql and in proxysql are different
|
||||
// we should convert proxysql values to mysql format to compare
|
||||
for (auto& el : j.items()) {
|
||||
if (el.key() == "conn") {
|
||||
std::string sql_log_bin_value;
|
||||
|
||||
// sql_log_bin {0|1}
|
||||
if (el.value()["sql_log_bin"] == 1) {
|
||||
el.value().erase("sql_log_bin");
|
||||
j["conn"]["sql_log_bin"] = "ON";
|
||||
}
|
||||
else if (el.value()["sql_log_bin"] == 0) {
|
||||
el.value().erase("sql_log_bin");
|
||||
j["conn"]["sql_log_bin"] = "OFF";
|
||||
}
|
||||
|
||||
// autocommit {true|false}
|
||||
if (!el.value()["sql_auto_is_null"].dump().compare("ON") ||
|
||||
!el.value()["sql_auto_is_null"].dump().compare("1") ||
|
||||
!el.value()["sql_auto_is_null"].dump().compare("on") ||
|
||||
el.value()["sql_auto_is_null"] == 1) {
|
||||
el.value().erase("sql_auto_is_null");
|
||||
j["conn"]["sql_auto_is_null"] = "ON";
|
||||
}
|
||||
else if (!el.value()["sql_auto_is_null"].dump().compare("OFF") ||
|
||||
!el.value()["sql_auto_is_null"].dump().compare("0") ||
|
||||
!el.value()["sql_auto_is_null"].dump().compare("off") ||
|
||||
el.value()["sql_auto_is_null"] == 0) {
|
||||
el.value().erase("sql_auto_is_null");
|
||||
j["conn"]["sql_auto_is_null"] = "OFF";
|
||||
}
|
||||
|
||||
// autocommit {true|false}
|
||||
if (!el.value()["autocommit"].dump().compare("ON") ||
|
||||
!el.value()["autocommit"].dump().compare("1") ||
|
||||
!el.value()["autocommit"].dump().compare("on") ||
|
||||
el.value()["autocommit"] == 1) {
|
||||
el.value().erase("autocommit");
|
||||
j["conn"]["autocommit"] = "ON";
|
||||
}
|
||||
else if (!el.value()["autocommit"].dump().compare("OFF") ||
|
||||
!el.value()["autocommit"].dump().compare("0") ||
|
||||
!el.value()["autocommit"].dump().compare("off") ||
|
||||
el.value()["autocommit"] == 0) {
|
||||
el.value().erase("autocommit");
|
||||
j["conn"]["autocommit"] = "OFF";
|
||||
}
|
||||
|
||||
// sql_safe_updates
|
||||
if (!el.value()["sql_safe_updates"].dump().compare("\"ON\"") ||
|
||||
!el.value()["sql_safe_updates"].dump().compare("\"1\"") ||
|
||||
!el.value()["sql_safe_updates"].dump().compare("\"on\"") ||
|
||||
el.value()["sql_safe_updates"] == 1) {
|
||||
el.value().erase("sql_safe_updates");
|
||||
j["conn"]["sql_safe_updates"] = "ON";
|
||||
}
|
||||
else if (!el.value()["sql_safe_updates"].dump().compare("\"OFF\"") ||
|
||||
!el.value()["sql_safe_updates"].dump().compare("\"0\"") ||
|
||||
!el.value()["sql_safe_updates"].dump().compare("\"off\"") ||
|
||||
el.value()["sql_safe_updates"] == 0) {
|
||||
el.value().erase("sql_safe_updates");
|
||||
j["conn"]["sql_safe_updates"] = "OFF";
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << 0xFFFFFFFFFFFFFFFF;
|
||||
// sql_select_limit
|
||||
if (!el.value()["sql_select_limit"].dump().compare("\"DEFAULT\"")) {
|
||||
el.value().erase("sql_select_limit");
|
||||
j["conn"]["sql_select_limit"] = strdup(ss.str().c_str());
|
||||
}
|
||||
|
||||
// transaction_isolation (level)
|
||||
if (!el.value()["isolation_level"].dump().compare("\"REPEATABLE READ\"")) {
|
||||
el.value().erase("isolation_level");
|
||||
j["conn"]["transaction_isolation"] = "REPEATABLE-READ";
|
||||
}
|
||||
else if (!el.value()["isolation_level"].dump().compare("\"READ COMMITTED\"")) {
|
||||
el.value().erase("isolation_level");
|
||||
j["conn"]["transaction_isolation"] = "READ-COMMITTED";
|
||||
}
|
||||
else if (!el.value()["isolation_level"].dump().compare("\"READ UNCOMMITTED\"")) {
|
||||
el.value().erase("isolation_level");
|
||||
j["conn"]["transaction_isolation"] = "READ-UNCOMMITTED";
|
||||
}
|
||||
else if (!el.value()["isolation_level"].dump().compare("\"SERIALIZABLE\"")) {
|
||||
el.value().erase("isolation_level");
|
||||
j["conn"]["transaction_isolation"] = "SERIALIZABLE";
|
||||
}
|
||||
|
||||
// transaction_read (write|only)
|
||||
if (!el.value()["transaction_read"].dump().compare("\"ONLY\"")) {
|
||||
el.value().erase("transaction_read");
|
||||
j["conn"]["transaction_read_only"] = "ON";
|
||||
}
|
||||
else if (!el.value()["transaction_read"].dump().compare("\"WRITE\"")) {
|
||||
el.value().erase("transaction_read");
|
||||
j["conn"]["transaction_read_only"] = "OFF";
|
||||
}
|
||||
|
||||
// session_track_gtids
|
||||
if (!el.value()["session_track_gtids"].dump().compare("\"OFF\"")) {
|
||||
el.value().erase("session_track_gtids");
|
||||
j["conn"]["session_track_gtids"] = "OFF";
|
||||
}
|
||||
else if (!el.value()["session_track_gtids"].dump().compare("\"OWN_GTID\"")) {
|
||||
el.value().erase("session_track_gtids");
|
||||
j["conn"]["session_track_gtids"] = "OWN_GTID";
|
||||
}
|
||||
else if (!el.value()["session_track_gtids"].dump().compare("\"ALL_GTIDS\"")) {
|
||||
el.value().erase("session_track_gtids");
|
||||
j["conn"]["session_track_gtids"] = "ALL_GTIDS";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void * my_conn_thread(void *arg) {
|
||||
g_seed = time(NULL) ^ getpid() ^ pthread_self();
|
||||
unsigned int select_OK=0;
|
||||
unsigned int select_ERR=0;
|
||||
int i, j;
|
||||
MYSQL **mysqlconns=(MYSQL **)malloc(sizeof(MYSQL *)*count);
|
||||
std::vector<json> varsperconn(count);
|
||||
|
||||
if (mysqlconns==NULL) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
for (i=0; i<count; i++) {
|
||||
MYSQL *mysql=mysql_init(NULL);
|
||||
if (mysql==NULL) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
MYSQL *rc=mysql_real_connect(mysql, host, username, password, schema, (local ? 0 : ( port + rand()%multiport ) ), NULL, 0);
|
||||
if (rc==NULL) {
|
||||
if (silent==0) {
|
||||
fprintf(stderr,"%s\n", mysql_error(mysql));
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
mysqlconns[i]=mysql;
|
||||
__sync_add_and_fetch(&status_connections,1);
|
||||
}
|
||||
__sync_fetch_and_add(&connect_phase_completed,1);
|
||||
|
||||
while(__sync_fetch_and_add(&connect_phase_completed,0) != num_threads) {
|
||||
}
|
||||
MYSQL *mysql;
|
||||
json vars;
|
||||
for (j=0; j<queries; j++) {
|
||||
int fr = fastrand();
|
||||
int r1=fr%count;
|
||||
int r2=fastrand()%testCases.size();
|
||||
|
||||
if (j%queries_per_connections==0) {
|
||||
mysql=mysqlconns[r1];
|
||||
vars = varsperconn[r1];
|
||||
}
|
||||
|
||||
if (mysql_query(mysql, testCases[r2].command.c_str())) {
|
||||
if (silent==0) {
|
||||
fprintf(stderr,"%s\n", mysql_error(mysql));
|
||||
}
|
||||
} else {
|
||||
MYSQL_RES *result = mysql_store_result(mysql);
|
||||
mysql_free_result(result);
|
||||
select_OK++;
|
||||
__sync_fetch_and_add(&g_select_OK,1);
|
||||
}
|
||||
for (auto& el : testCases[r2].expected_vars.items()) {
|
||||
vars[el.key()] = el.value();
|
||||
}
|
||||
|
||||
int sleepDelay = fastrand()%100;
|
||||
usleep(sleepDelay * 1000);
|
||||
|
||||
char query[128];
|
||||
sprintf(query, "SELECT %d;", sleepDelay);
|
||||
if (mysql_query(mysql,query)) {
|
||||
select_ERR++;
|
||||
__sync_fetch_and_add(&g_select_ERR,1);
|
||||
} else {
|
||||
MYSQL_RES *result = mysql_store_result(mysql);
|
||||
mysql_free_result(result);
|
||||
select_OK++;
|
||||
__sync_fetch_and_add(&g_select_OK,1);
|
||||
}
|
||||
|
||||
|
||||
json mysql_vars;
|
||||
queryVariables(mysql, mysql_vars);
|
||||
|
||||
json proxysql_vars;
|
||||
queryInternalStatus(mysql, proxysql_vars);
|
||||
|
||||
bool testPassed = true;
|
||||
for (auto& el : vars.items()) {
|
||||
auto k = mysql_vars.find(el.key());
|
||||
auto s = proxysql_vars["conn"].find(el.key());
|
||||
|
||||
if (k == mysql_vars.end())
|
||||
fprintf(stderr, "Variable %s->%s in mysql resultset was not found.\nmysql data : %s\nproxysql data: %s\ncsv data %s\n",
|
||||
el.value().dump().c_str(), el.key().c_str(), mysql_vars.dump().c_str(), proxysql_vars.dump().c_str(), vars.dump().c_str());
|
||||
|
||||
if (s == proxysql_vars["conn"].end())
|
||||
fprintf(stderr, "Variable %s->%s in proxysql resultset was not found.\nmysql data : %s\nproxysql data: %s\ncsv data %s\n",
|
||||
el.value().dump().c_str(), el.key().c_str(), mysql_vars.dump().c_str(), proxysql_vars.dump().c_str(), vars.dump().c_str());
|
||||
|
||||
if (k.value() != el.value() || s.value() != el.value()) {
|
||||
__sync_fetch_and_add(&g_failed, 1);
|
||||
testPassed = false;
|
||||
fprintf(stderr, "Test failed for this case %s->%s.\n\nmysql data %s\n\n proxysql data %s\n\n csv data %s\n\n\n",
|
||||
el.value().dump().c_str(), el.key().c_str(), mysql_vars.dump().c_str(), proxysql_vars.dump().c_str(), vars.dump().c_str());
|
||||
}
|
||||
}
|
||||
ok(testPassed, "Test passed");
|
||||
}
|
||||
__sync_fetch_and_add(&query_phase_completed,1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
CommandLine cl;
|
||||
std::string fileName("./tests/set_testing-t.csv");
|
||||
|
||||
if(cl.getEnv())
|
||||
return exit_status();
|
||||
|
||||
num_threads = 10;
|
||||
queries = 1000;
|
||||
queries_per_connections = 10;
|
||||
count = 10;
|
||||
username = cl.username;
|
||||
password = cl.password;
|
||||
host = cl.host;
|
||||
port = cl.port;
|
||||
|
||||
plan(queries * num_threads);
|
||||
if (!readTestCases(fileName)) {
|
||||
fprintf(stderr, "Cannot read %s\n", fileName.c_str());
|
||||
return exit_status();
|
||||
}
|
||||
|
||||
if (strcmp(host,"localhost")==0) {
|
||||
local = 1;
|
||||
}
|
||||
if (uniquequeries == 0) {
|
||||
if (queries) uniquequeries=queries;
|
||||
}
|
||||
if (uniquequeries) {
|
||||
uniquequeries=(int)sqrt(uniquequeries);
|
||||
}
|
||||
mysql_library_init(0, NULL, NULL);
|
||||
|
||||
pthread_t *thi=(pthread_t *)malloc(sizeof(pthread_t)*num_threads);
|
||||
if (thi==NULL)
|
||||
return exit_status();
|
||||
|
||||
for (unsigned int i=0; i<num_threads; i++) {
|
||||
if ( pthread_create(&thi[i], NULL, my_conn_thread , NULL) != 0 )
|
||||
perror("Thread creation");
|
||||
}
|
||||
for (unsigned int i=0; i<num_threads; i++) {
|
||||
pthread_join(thi[i], NULL);
|
||||
}
|
||||
return exit_status();
|
||||
}
|
||||
|
Loading…
Reference in new issue