mirror of https://github.com/sysown/proxysql
Merge pull request #3888 from sysown/v2.4-clickhouse
Upgraded clickhouse-cpp to 2.1.0pull/3929/head
commit
2a5f364fd5
@ -0,0 +1 @@
|
||||
clickhouse-cpp-2.1.0
|
||||
Binary file not shown.
@ -1,9 +0,0 @@
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "../base/coded.h"
|
||||
#include "../types/types.h"
|
||||
|
||||
+#include <stdexcept>
|
||||
+
|
||||
namespace clickhouse {
|
||||
|
||||
using ColumnRef = std::shared_ptr<class Column>;
|
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,274 @@
|
||||
#!/usr/bin/env php
|
||||
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Test written in a TAP alike format checking that PHP connector is able to connect to ClickHouse through
|
||||
* ProxySQL and that it receives the correct types for the supported types. The test operations are:
|
||||
*
|
||||
* 1. Create a connection to ClickHouse through ProxySQL using PHP connector.
|
||||
* 2. Creates a table holding the supported types: [EventDate,DateTime,TINTYINT(Int8),SMALLINT(Int16),INT(Int32),BIGINT(Int64),FLOAT(Float32),DOUBLE(Float64)]
|
||||
* 3. Insert data in the table through: INSERT * SELECT.
|
||||
* 4. Query the table data checking:
|
||||
* 4.1 - The types correctly matches the expected ones (not mangled into 'string').
|
||||
* 4.2 - NULL types are supported and are properly represented and retrieved.
|
||||
* 4.3 - Values are properly received, being able to be casted and compared with inserting values.
|
||||
*/
|
||||
|
||||
error_reporting(E_ALL ^ E_NOTICE);
|
||||
|
||||
$username="cuser";
|
||||
$password="cpass";
|
||||
$port=6090;
|
||||
|
||||
#$username="sbtest1";
|
||||
#$password="sbtest1";
|
||||
#$port=13306;
|
||||
|
||||
$admin_user = getenv("TAP_ADMINUSERNAME");
|
||||
$admin_user = $admin_user == false ? "admin" : $admin_user;
|
||||
|
||||
$admin_pass = getenv("TAP_ADMINPASSWORD");
|
||||
$admin_pass = $admin_pass == false ? "admin" : $admin_pass;
|
||||
|
||||
$admin_port = getenv("TAP_ADMINPORT");
|
||||
$admin_port = $admin_port == false ? 6032 : $admin_port;
|
||||
|
||||
echo ":: Creating ProxySQL Admin connection...".PHP_EOL;
|
||||
$proxy_admin = new mysqli("127.0.0.1", $admin_user, $admin_pass, "", $admin_port);
|
||||
if ($proxy_admin->connect_errno) {
|
||||
die("PorxySQL connect failed: " . $proxy->connect_error);
|
||||
}
|
||||
echo ":: ProxySQL ProxySQL Admin connection completed".PHP_EOL;
|
||||
|
||||
echo ":: Creating required users for test...".PHP_EOL;
|
||||
$proxy_admin->query("INSERT OR REPLACE INTO clickhouse_users (username,password,active,max_connections) VALUES ('{$username}','{$password}',1,100)");
|
||||
$proxy_admin->query("LOAD CLICKHOUSE USERS TO RUNTIME");
|
||||
echo ":: Finished creating users".PHP_EOL;
|
||||
|
||||
echo ":: Creating ProxySQL connection...".PHP_EOL;
|
||||
$proxy = new mysqli("127.0.0.1", $username, $password, "", $port);
|
||||
if ($proxy->connect_errno) {
|
||||
die("PorxySQL connect failed: " . $proxy->connect_error);
|
||||
}
|
||||
echo ":: ProxySQL connection completed".PHP_EOL;
|
||||
|
||||
|
||||
echo ":: Starting schema and table creation...".PHP_EOL;
|
||||
if ($port !== 6090) {
|
||||
$proxy->query("CREATE DATABASE IF NOT EXISTS test_clickhouse_types_php");
|
||||
$proxy->query("USE test_clickhouse_types_php");
|
||||
$proxy->query("DROP TABLE IF EXISTS types_table");
|
||||
|
||||
$proxy->query("CREATE TABLE IF NOT EXISTS types_table (EventDate DATE, DateTime DATETIME, col1 TINYINT, col2 SMALLINT, col3 INT, col4 BIGINT, col5 FLOAT, col6 DOUBLE)");
|
||||
|
||||
echo ":: Inserting data directly to MySQL".PHP_EOL;
|
||||
$proxy->query("INSERT INTO types_table SELECT NOW(),NOW(),127,-32768,2147483647,9223372036854775807,340282346638528859811704183484516925440.0,340282346638528859811704183484516925440.0");
|
||||
|
||||
echo ":: Fetching inserted data".PHP_EOL;
|
||||
$result = $proxy->query("SELECT EventDate,DateTime,col1,col2,col3,col4,col5,round(col6,0) FROM types_table");
|
||||
while ($row = mysqli_fetch_row($result)) {
|
||||
echo " * ROW: [";
|
||||
|
||||
foreach ($row as $val) {
|
||||
echo $val.",";
|
||||
}
|
||||
|
||||
echo "]".PHP_EOL;
|
||||
}
|
||||
|
||||
echo ":: Finished operations on MySQL conn".PHP_EOL;
|
||||
exit(0);
|
||||
} else {
|
||||
$proxy->query("CREATE DATABASE IF NOT EXISTS test_clickhouse_types_php");
|
||||
$proxy->query("USE test_clickhouse_types_php");
|
||||
$proxy->query("DROP TABLE IF EXISTS types_table");
|
||||
$proxy->query("CREATE TABLE IF NOT EXISTS types_table (EventDate DATE, DateTime DATETIME, col1 UInt8, col2 Int16, col3 Int32, col4 Int64, col5 Nullable(Float32), col6 Float64) ENGINE=MergeTree(EventDate, (EventDate), 8192)");
|
||||
}
|
||||
|
||||
$shortName = exec('date +%Z');
|
||||
$longName = timezone_name_from_abbr($shortName);
|
||||
$timezone = timezone_open($longName);
|
||||
$datetime_db = date_create("now", timezone_open("UTC"));
|
||||
$timezone_off = timezone_offset_get($timezone, $datetime_db);
|
||||
|
||||
$cur_date = date("Y-m-d");
|
||||
$cur_datetime = date("Y-m-d H:i:s");
|
||||
|
||||
echo ":: Schema and table creation completed".PHP_EOL;
|
||||
|
||||
$exp_rows = [
|
||||
[
|
||||
"insert" => "INSERT INTO types_table SELECT '{$cur_date}','{$cur_datetime}',127,-32768,2147483647,9223372036854775807,340282346638528859811704183484516925440,340282346638528859811704183484516925440.0",
|
||||
"select" => "SELECT EventDate,DateTime,col1,col2,col3,col4,col5,round(col6,0) FROM types_table",
|
||||
"types" => [10, 12, 1, 2, 3, 8, 4, 5],
|
||||
"vals" => [$cur_date, $cur_datetime, 127, -32768, 2147483647, 9223372036854775807, 340282346638528859811704183484516925440, 340282346638528859811704183484516925440.0]
|
||||
],
|
||||
[
|
||||
"insert" => "INSERT INTO types_table SELECT '{$cur_date}','{$cur_datetime}',127,-32768,2147483647,9223372036854775807,1.2,340282346638528859811704183484516925440.0",
|
||||
"select" => "SELECT EventDate,DateTime,col1,col2,col3,col4,ROUND(col5,20),round(col6,0) FROM types_table",
|
||||
"types" => [10, 12, 1, 2, 3, 8, 4, 5],
|
||||
"vals" => [$cur_date, $cur_datetime, 127, -32768, 2147483647, 9223372036854775807, 1.2, 340282346638528859811704183484516925440.0]
|
||||
],
|
||||
[
|
||||
"insert" => "INSERT INTO types_table SELECT '{$cur_date}','{$cur_datetime}',127,-32768,2147483647,9223372036854775807,NULL,340282346638528859811704183484516925440.0",
|
||||
"select" => "SELECT EventDate,DateTime,col1,col2,col3,col4,col5,round(col6,0) FROM types_table",
|
||||
"types" => [10, 12, 1, 2, 3, 8, 4, 5],
|
||||
"vals" => [$cur_date, $cur_datetime, 127, -32768, 2147483647, 9223372036854775807, NULL, 340282346638528859811704183484516925440.0]
|
||||
]
|
||||
];
|
||||
|
||||
echo ":: Checking expected data definition...".PHP_EOL;
|
||||
|
||||
foreach ($exp_rows as $row) {
|
||||
$c_row_types = count($row["types"]);
|
||||
$c_row_vals = count($row["vals"]);
|
||||
|
||||
if ($c_row_types !== $c_row_vals) {
|
||||
echo " * Invalid exp row definition for query '{$row["select"]}'. Expected type count '{$c_row_types}' != '{$c_row_vals}'".PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
echo ":: Checking expected data completed".PHP_EOL;
|
||||
|
||||
$exit_code = 0;
|
||||
|
||||
$count = 0;
|
||||
foreach ($exp_rows as $exp_row) {
|
||||
echo ":: Performing operation for payload num '$count'".PHP_EOL;
|
||||
echo " * Issuing INSERT query '{$exp_row["insert"]}'".PHP_EOL;
|
||||
$proxy->query($exp_row["insert"]);
|
||||
|
||||
echo " * Issuing SELECT query '{$exp_row["select"]}'".PHP_EOL;
|
||||
$result = $proxy->query($exp_row["select"]);
|
||||
|
||||
/* Get field information for all columns */
|
||||
$finfo = $result->fetch_fields();
|
||||
|
||||
echo " * START: Received columns info".PHP_EOL;
|
||||
|
||||
$act_col_defs = array();
|
||||
|
||||
foreach ($finfo as $val) {
|
||||
printf(" - Name: %s\n", $val->name);
|
||||
printf(" - Table: %s\n", $val->table);
|
||||
printf(" - Max. Len: %d\n", $val->max_length);
|
||||
printf(" - Length: %d\n", $val->length);
|
||||
printf(" - charsetnr: %d\n", $val->charsetnr);
|
||||
printf(" - Flags: %d\n", $val->flags);
|
||||
printf(" - Type: %d\n\n", $val->type);
|
||||
|
||||
array_push($act_col_defs, $val);
|
||||
}
|
||||
|
||||
echo " * END: Received columns info".PHP_EOL;
|
||||
|
||||
echo ":: Checking fetched data...".PHP_EOL;
|
||||
|
||||
$fetch_rows = array();
|
||||
while ($row = mysqli_fetch_row($result)) {
|
||||
array_push($fetch_rows, $row);
|
||||
}
|
||||
|
||||
$c_exp_rows = 1;
|
||||
$c_fetch_rows = count($fetch_rows);
|
||||
|
||||
if ($c_exp_rows !== $c_fetch_rows) {
|
||||
echo "Expected received row number doesn't match actual received rows - exp: {$c_exp_rows}, act: {$c_fetch_rows}".PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$types_match = true;
|
||||
$type_count = 0;
|
||||
|
||||
foreach ($act_col_defs as $act_col_def) {
|
||||
$act_type = $act_col_def->type;
|
||||
$exp_type = $exp_row["types"][$type_count];
|
||||
|
||||
$type_match = $act_type === $exp_type;
|
||||
|
||||
if ($type_match) {
|
||||
$ok_msg = "ok";
|
||||
} else {
|
||||
$ok_msg = "not ok";
|
||||
}
|
||||
|
||||
echo " {$ok_msg} - Type match result for column '{$act_col_def->name}' was '{$type_match}'.";
|
||||
echo " ExpType: '{$exp_type}'. ActType '{$act_type}'".PHP_EOL;
|
||||
|
||||
$type_count += 1;
|
||||
$types_match &= $type_match;
|
||||
}
|
||||
echo PHP_EOL;
|
||||
|
||||
$vals_match = true;
|
||||
$val_count = 0;
|
||||
foreach ($fetch_rows as $row) {
|
||||
foreach ($row as $val) {
|
||||
$col_name = $act_col_defs[$val_count]->name;
|
||||
|
||||
$exp_val = $exp_row["vals"][$val_count];
|
||||
$exp_type = $exp_row["types"][$val_count];
|
||||
|
||||
if (is_null($exp_val) == false) {
|
||||
if ($exp_type == 1 || $exp_type == 2 || $exp_type == 3 || $exp_type == 8) {
|
||||
$cast_val = (int)$val;
|
||||
} else if ($exp_type == 4 | $exp_type == 5) {
|
||||
$cast_val = (float)$val;
|
||||
} else if ($exp_type == 12) {
|
||||
$timestamp = strtotime($val) - $timezone_off;
|
||||
$cast_val = date("Y-m-d H:i:s", $timestamp);
|
||||
} else {
|
||||
$cast_val = $val;
|
||||
}
|
||||
} else {
|
||||
$cast_val = $val;
|
||||
}
|
||||
|
||||
$val_match = $exp_val === $cast_val;
|
||||
|
||||
if ($val_match) {
|
||||
$ok_msg = "ok";
|
||||
} else {
|
||||
$ok_msg = "not ok";
|
||||
}
|
||||
|
||||
echo " {$ok_msg} - Value result match for column '{$col_name}' was '{$val_match}'.";
|
||||
echo " ExpVal: '{$exp_val}'. ActVal '{$cast_val}'".PHP_EOL;
|
||||
|
||||
$vals_match &= $val_match;
|
||||
$val_count += 1;
|
||||
}
|
||||
$val_count = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
$count += 1;
|
||||
|
||||
echo ":: Cleaning up table before next queries...".PHP_EOL;
|
||||
$proxy->query("ALTER TABLE types_table DELETE WHERE 1=1");
|
||||
|
||||
$result = $proxy->query("SELECT COUNT(*) FROM types_table");
|
||||
$row = mysqli_fetch_row($result);
|
||||
echo ":: Waiting for table cleaning";
|
||||
|
||||
while ($row[0] != 0) {
|
||||
echo ".";
|
||||
sleep(1);
|
||||
|
||||
$result = $proxy->query("SELECT COUNT(*) FROM types_table");
|
||||
$row = mysqli_fetch_row($result);
|
||||
}
|
||||
|
||||
echo PHP_EOL;
|
||||
echo ":: Table cleaning completed".PHP_EOL;
|
||||
|
||||
$exit_code |= !($types_match & $vals_match);
|
||||
}
|
||||
|
||||
$proxy->query("DROP DATABASE IF EXISTS test_clickhouse_types_php");
|
||||
$result->free();
|
||||
|
||||
exit($exit_code);
|
||||
|
||||
?>
|
||||
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @brief Extract the current 'MODULE-mysql_ifaces' from ProxySQL config.
|
||||
* @param proxysql_admin An already opened connection to ProxySQL Admin.
|
||||
* @return EXIT_SUCCESS, or one of the following error codes:
|
||||
* - EINVAL if supplied 'proxysql_admin' is NULL.
|
||||
* - EXIT_FAILURE in case other operation failed.
|
||||
*/
|
||||
int get_module_ifaces(MYSQL* proxysql_admin, const std::string varname, std::string& module_ifaces) {
|
||||
if (proxysql_admin == NULL) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int res = EXIT_FAILURE;
|
||||
|
||||
std::string query = "SELECT * FROM global_variables WHERE Variable_name='" + varname + "'";
|
||||
diag("Running query: %s", query.c_str());
|
||||
MYSQL_QUERY(proxysql_admin, query.c_str());
|
||||
|
||||
MYSQL_RES* admin_res = mysql_store_result(proxysql_admin);
|
||||
if (!admin_res) {
|
||||
diag("'mysql_store_result' at line %d failed: %s", __LINE__, mysql_error(proxysql_admin));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(admin_res);
|
||||
if (!row || row[0] == nullptr || row[1] == nullptr) {
|
||||
diag("'mysql_fetch_row' at line %d returned 'NULL'", __LINE__);
|
||||
res = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
std::string _module_ifaces { row[1] };
|
||||
module_ifaces = _module_ifaces;
|
||||
res = EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int extract_module_host_port(MYSQL* proxysql_admin, const std::string varname, std::pair<std::string, int>& host_port) {
|
||||
if (proxysql_admin == nullptr) { return EINVAL; }
|
||||
int res = EXIT_SUCCESS;
|
||||
|
||||
std::string module_ifaces {};
|
||||
int ifaces_err = get_module_ifaces(proxysql_admin, varname, module_ifaces);
|
||||
|
||||
// ProxySQL is likely to have been launched without "--MODULE-server" flag
|
||||
if (ifaces_err == -1) {
|
||||
if (varname=="sqliteserver-mysql_ifaces") {
|
||||
diag("ProxySQL was launched without '--sqlite3-server' flag");
|
||||
} else if (varname=="clickhouse-mysql_ifaces") {
|
||||
diag("ProxySQL was launched without '--clickhouse-server' flag");
|
||||
} else {
|
||||
diag("Unknown variable %s", varname.c_str());
|
||||
}
|
||||
res = EXIT_FAILURE;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Extract the correct port to connect to MODULE server
|
||||
std::string::size_type colon_pos = module_ifaces.find(":");
|
||||
if (colon_pos == std::string::npos) {
|
||||
diag("ProxySQL returned a malformed '%s': %s", varname.c_str(), module_ifaces.c_str());
|
||||
res = EXIT_FAILURE;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string module_host { module_ifaces.substr(0, colon_pos) };
|
||||
std::string module_port { module_ifaces.substr(colon_pos + 1) };
|
||||
|
||||
// Check that port has valid conversion
|
||||
char* end_pos = nullptr;
|
||||
int i_module_port = std::strtol(module_port.c_str(), &end_pos, 10);
|
||||
|
||||
if (errno == ERANGE || (end_pos != &module_port.back() + 1)) {
|
||||
diag(
|
||||
"ProxySQL returned a invalid port number within '%s': %s",
|
||||
varname.c_str(), module_ifaces.c_str()
|
||||
);
|
||||
res = EXIT_FAILURE;
|
||||
return res;
|
||||
}
|
||||
|
||||
if (res == EXIT_SUCCESS) {
|
||||
host_port = { module_host, i_module_port };
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -1,91 +0,0 @@
|
||||
/**
|
||||
* @brief Extract the current 'sqliteserver-mysql_ifaces' from ProxySQL config.
|
||||
* @param proxysql_admin An already opened connection to ProxySQL Admin.
|
||||
* @return EXIT_SUCCESS, or one of the following error codes:
|
||||
* - EINVAL if supplied 'proxysql_admin' is NULL.
|
||||
* - '-1' in case of ProxySQL returns an 'NULL' row for the query selecting
|
||||
* the variable 'sqliteserver-read_only'.
|
||||
* - EXIT_FAILURE in case other operation failed.
|
||||
*/
|
||||
int get_sqlite3_ifaces(MYSQL* proxysql_admin, std::string& sqlite3_ifaces) {
|
||||
if (proxysql_admin == NULL) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int res = EXIT_FAILURE;
|
||||
|
||||
MYSQL_QUERY(
|
||||
proxysql_admin,
|
||||
"SELECT * FROM global_variables WHERE Variable_name='sqliteserver-mysql_ifaces'"
|
||||
);
|
||||
|
||||
MYSQL_RES* admin_res = mysql_store_result(proxysql_admin);
|
||||
if (!admin_res) {
|
||||
diag("'mysql_store_result' at line %d failed: %s", __LINE__, mysql_error(proxysql_admin));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(admin_res);
|
||||
if (!row || row[0] == nullptr || row[1] == nullptr) {
|
||||
diag("'mysql_fetch_row' at line %d returned 'NULL'", __LINE__);
|
||||
res = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
std::string _sqlite3_ifaces { row[1] };
|
||||
sqlite3_ifaces = _sqlite3_ifaces;
|
||||
res = EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int extract_sqlite3_host_port(MYSQL* proxysql_admin, std::pair<std::string, int>& host_port) {
|
||||
if (proxysql_admin == nullptr) { return EINVAL; }
|
||||
int res = EXIT_SUCCESS;
|
||||
|
||||
std::string sqlite3_ifaces {};
|
||||
int ifaces_err = get_sqlite3_ifaces(proxysql_admin, sqlite3_ifaces);
|
||||
|
||||
// ProxySQL is likely to have been launched without "--sqlite3-server" flag
|
||||
if (ifaces_err == -1) {
|
||||
diag("ProxySQL was launched without '--sqlite3-server' flag");
|
||||
res = EXIT_FAILURE;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Extract the correct port to connect to SQLite server
|
||||
std::string::size_type colon_pos = sqlite3_ifaces.find(":");
|
||||
if (colon_pos == std::string::npos) {
|
||||
diag("ProxySQL returned a malformed 'sqliteserver-mysql_ifaces': %s", sqlite3_ifaces.c_str());
|
||||
res = EXIT_FAILURE;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string sqlite3_host { sqlite3_ifaces.substr(0, colon_pos) };
|
||||
std::string sqlite3_port { sqlite3_ifaces.substr(colon_pos + 1) };
|
||||
|
||||
// Check that port has valid conversion
|
||||
char* end_pos = nullptr;
|
||||
int i_sqlite3_port = std::strtol(sqlite3_port.c_str(), &end_pos, 10);
|
||||
|
||||
if (errno == ERANGE || (end_pos != &sqlite3_port.back() + 1)) {
|
||||
diag(
|
||||
"ProxySQL returned a invalid port number within 'sqliteserver-mysql_ifaces': %s",
|
||||
sqlite3_ifaces.c_str()
|
||||
);
|
||||
res = EXIT_FAILURE;
|
||||
return res;
|
||||
}
|
||||
|
||||
if (res == EXIT_SUCCESS) {
|
||||
host_port = { sqlite3_host, i_sqlite3_port };
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,546 @@
|
||||
/**
|
||||
* @file test_clickhouse_server-t.cpp
|
||||
* @brief Test to perform multiple operations over ProxySQL Clickhouse server.
|
||||
* @details It performs the following operations:
|
||||
* - Connects to clickhouse with a wrong username.
|
||||
* - Connects to clickhouse with a right username but wrong password.
|
||||
* - Successfully connects to clickhouse and runs several queries.
|
||||
* + SHOW SCHEMAS
|
||||
* + SHOW DATABASES
|
||||
* + SELECT DATABASE()
|
||||
* - Successfully connects to clickhouse and runs a variety of queries:
|
||||
* + CREATE TABLE, SHOW CREATE TABLE, INSERT, SELECT, DROP TABLE...
|
||||
* + Queries that induce errors: syntax error, duplicate keys, etc...
|
||||
* - Changes 'clickhouse-mysql_ifaces' and tries to connect to the new interface.
|
||||
* - Connects to ProxySQL Admin and performs the following operations:
|
||||
* + LOAD|SAVE SQLITESERVER TO|FROM RUNTIME|MEMORY|DISK
|
||||
* - This test is also compiled against 'libmysqlclient' resulting in the binary
|
||||
* 'test_clickhouse_server_libmysql-t'. This duplicate test exists for testing 'deprecate_eof' support
|
||||
* against ProxySQL ClickHouse server.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <mysql.h>
|
||||
#include <mysql/mysqld_error.h>
|
||||
|
||||
#include "tap.h"
|
||||
#include "command_line.h"
|
||||
#include "utils.h"
|
||||
|
||||
using query_spec = std::tuple<std::string, int, int>;
|
||||
|
||||
const int proxysql_clickhouse_port = 6090;
|
||||
const int crash_loops = 4;
|
||||
|
||||
#include "modules_server_test.h"
|
||||
|
||||
int fetch_and_discard_results(MYSQL_RES* result, bool verbose=false);
|
||||
|
||||
std::vector<std::pair<std::string,std::string>> credentials = {
|
||||
{"cliuser1", "clipass1"},
|
||||
{"cliuser2", "clipass2"},
|
||||
{"cliuser3", "clipass3"},
|
||||
{"cliuser4", "clipass4"}
|
||||
};
|
||||
|
||||
int set_clickhouse_port(MYSQL *pa, int p) {
|
||||
std::string query = "SET clickhouse-port=" + std::to_string(p);
|
||||
diag("Line: %d . Setting clickhouse-port to %d", __LINE__ , p);
|
||||
MYSQL_QUERY(pa, query.c_str());
|
||||
MYSQL_QUERY(pa, "LOAD CLICKHOUSE VARIABLES TO RUNTIME");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_crash(const char *host, int port) {
|
||||
// try to connect and run queries while there is no backend
|
||||
for (int i=0; i<crash_loops; i++) {
|
||||
MYSQL * proxysql_clickhouse = mysql_init(NULL);
|
||||
diag("Line: %d . Create connection %d in test_cash()", __LINE__ , i);
|
||||
// Correctly connect to Clickhouse server
|
||||
if (
|
||||
!mysql_real_connect(
|
||||
proxysql_clickhouse, host, credentials[2].first.c_str(), credentials[2].second.c_str(),
|
||||
NULL, port, NULL, 0
|
||||
)
|
||||
) {
|
||||
diag("File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_clickhouse));
|
||||
return exit_status();
|
||||
}
|
||||
char * q = (char *)"SELECT 1";
|
||||
int query_err = mysql_query(proxysql_clickhouse, q);
|
||||
MYSQL_RES * result = mysql_store_result(proxysql_clickhouse);
|
||||
if (result) {
|
||||
fetch_and_discard_results(result, false);
|
||||
mysql_free_result(result);
|
||||
} else {
|
||||
diag("Line %d : Query failed: %s . Error: %s", __LINE__, q, mysql_error(proxysql_clickhouse));
|
||||
}
|
||||
ok(query_err!=0, "Query should fail when the backend is not connected");
|
||||
mysql_close(proxysql_clickhouse);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int create_users(MYSQL *pa) {
|
||||
diag("Emptying clickhouse_users table");
|
||||
MYSQL_QUERY(pa, "DELETE FROM clickhouse_users");
|
||||
diag("Emptying runtime_clickhouse_users table");
|
||||
MYSQL_QUERY(pa, "LOAD CLICKHOUSE USERS TO RUNTIME");
|
||||
int query_err;
|
||||
MYSQL_RES *result;
|
||||
char *q;
|
||||
q = (char *)"SELECT * FROM clickhouse_users";
|
||||
query_err = mysql_query(pa, q);
|
||||
result = mysql_store_result(pa);
|
||||
if (result) {
|
||||
int j = fetch_and_discard_results(result, true);
|
||||
mysql_free_result(result);
|
||||
ok(j==0, "Line %d : Rows in clickhouse_users should be 0. Actual: %d" , __LINE__, j);
|
||||
} else {
|
||||
ok(false,"Line %d : Query failed: %s . Error: %s", __LINE__, q, mysql_error(pa));
|
||||
return exit_status();
|
||||
}
|
||||
q = (char *)"SELECT * FROM runtime_clickhouse_users";
|
||||
query_err = mysql_query(pa, q);
|
||||
result = mysql_store_result(pa);
|
||||
if (result) {
|
||||
int j = fetch_and_discard_results(result, true);
|
||||
mysql_free_result(result);
|
||||
ok(j==0, "Line %d : Rows in clickhouse_users should be 0. Actual: %d" , __LINE__, j);
|
||||
} else {
|
||||
ok(false,"Line %d : Query failed: %s . Error: %s", __LINE__, q, mysql_error(pa));
|
||||
return exit_status();
|
||||
}
|
||||
for(std::vector<std::pair<std::string,std::string>>::iterator it = credentials.begin(); it!=credentials.end(); it++) {
|
||||
std::string query = "INSERT INTO clickhouse_users VALUES ('" + it->first + "', '" + it->second + "', 1, 100)";
|
||||
diag("Adding user %s : %s", it->first.c_str(), query.c_str());
|
||||
MYSQL_QUERY(pa, query.c_str());
|
||||
}
|
||||
q = (char *)"SELECT * FROM clickhouse_users";
|
||||
query_err = mysql_query(pa, q);
|
||||
result = mysql_store_result(pa);
|
||||
if (result) {
|
||||
int j = fetch_and_discard_results(result, true);
|
||||
mysql_free_result(result);
|
||||
ok(j==4, "Line %d : Rows in clickhouse_users should be 4. Actual: %d" , __LINE__, j);
|
||||
} else {
|
||||
ok(false,"Line %d : Query failed: %s . Error: %s", __LINE__, q, mysql_error(pa));
|
||||
return exit_status();
|
||||
}
|
||||
diag("Loading clickhouse_users to runtime");
|
||||
MYSQL_QUERY(pa, "LOAD CLICKHOUSE USERS TO RUNTIME");
|
||||
q = (char *)"SELECT * FROM runtime_clickhouse_users";
|
||||
query_err = mysql_query(pa, q);
|
||||
result = mysql_store_result(pa);
|
||||
if (result) {
|
||||
int j = fetch_and_discard_results(result, true);
|
||||
mysql_free_result(result);
|
||||
ok(j==4, "Line %d : Rows in clickhouse_users should be 4. Actual: %d" , __LINE__, j);
|
||||
} else {
|
||||
ok(false,"Line %d : Query failed: %s . Error: %s", __LINE__, q, mysql_error(pa));
|
||||
return exit_status();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fetch_and_discard_results(MYSQL_RES* result, bool verbose) {
|
||||
MYSQL_ROW row = nullptr;
|
||||
unsigned int num_fields = 0;
|
||||
unsigned int i = 0;
|
||||
unsigned int j = 0;
|
||||
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result))) {
|
||||
unsigned long *lengths = mysql_fetch_lengths(result);
|
||||
|
||||
if (verbose) {
|
||||
printf("# RowNum_%d: ", j);
|
||||
}
|
||||
|
||||
for(i = 0; i < num_fields; i++) {
|
||||
if (verbose) {
|
||||
printf("[%.*s] ", (int) lengths[i], row[i] ? row[i] : "NULL");
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Execute the supplied queries and check that the return codes are the
|
||||
* ones specified.
|
||||
*
|
||||
* @param proxysql_clickhouse An already opened MYSQL connection to ProxySQL
|
||||
* Clickhouse server.
|
||||
* @param queries The queries to be performed and check.
|
||||
*/
|
||||
int execute_and_check_queries(MYSQL* proxysql_clickhouse, const std::vector<query_spec>& queries) {
|
||||
for (const auto& supp_query : queries) {
|
||||
const std::string query = std::get<0>(supp_query);
|
||||
const int exp_err_code = std::get<1>(supp_query);
|
||||
const int exp_rows = std::get<2>(supp_query); // if >= 0 , it is a select and expects data
|
||||
|
||||
diag("Line: %d . Running query: %s" , __LINE__ , query.c_str());
|
||||
int query_err = mysql_query(proxysql_clickhouse, query.c_str());
|
||||
MYSQL_RES* result = nullptr;
|
||||
// NOTE: For test compatibility with 'libmysqlclient', 'mysql_store_result' should only be called
|
||||
// in case no error is present. Otherwise would modify the error itself, thus making test fail.
|
||||
if (!query_err) {
|
||||
result = mysql_store_result(proxysql_clickhouse);
|
||||
}
|
||||
if (exp_rows >= 0 && result == NULL) {
|
||||
diag ("We were expecting %d rows, but we didn't receive a resultset", exp_rows);
|
||||
return exit_status();
|
||||
}
|
||||
if (exp_rows < 0 && result != NULL) {
|
||||
diag ("We were expecting no result, but we received a resultset");
|
||||
return exit_status();
|
||||
}
|
||||
if (result) {
|
||||
int j = fetch_and_discard_results(result, true);
|
||||
mysql_free_result(result);
|
||||
if (j != exp_rows) {
|
||||
diag ("We were expecting a result of %d rows, but we received a resultset of %d rows", exp_rows, j);
|
||||
return exit_status();
|
||||
}
|
||||
}
|
||||
|
||||
int m_errno = mysql_errno(proxysql_clickhouse);
|
||||
const char* m_error = mysql_error(proxysql_clickhouse);
|
||||
|
||||
if (exp_err_code == 0) {
|
||||
ok(
|
||||
exp_err_code == m_errno,
|
||||
"Line: %d . Query '%s' should succeed. Error code: (Expected:'%d' == Actual:'%d'), Err: '%s'",
|
||||
__LINE__, query.c_str(), exp_err_code, m_errno, m_error
|
||||
);
|
||||
} else {
|
||||
ok(
|
||||
exp_err_code == m_errno,
|
||||
"Line: %d . Query '%s' should fail. Error code: (Expected:'%d' == Actual:'%d'), Err: '%s'",
|
||||
__LINE__, query.c_str(), exp_err_code, m_errno, m_error
|
||||
);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<query_spec> queries_set1 {
|
||||
std::make_tuple<std::string, int>("SHOW SCHEMAS", 0, 4),
|
||||
std::make_tuple<std::string, int>("SHOW DATABASES", 0, 4),
|
||||
std::make_tuple<std::string, int>("SELECT DATABASE()", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT USER()", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT CURRENT_USER()", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT VERSION()", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT CONCAT(version(),'')", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT 1", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT 1+1", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT CONCAT('AAA','BBB')", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT NULL", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT NULL AS a", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT NULL+2 AS a, 'hello', NULL+1, 'world', NULL AS b", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT CONCAT('AAA',NULL)", 0, 1),
|
||||
std::make_tuple<std::string, int>("DROP TABLE IF EXISTS table1", 0, -1),
|
||||
std::make_tuple<std::string, int>("CREATE TABLE table1 (CounterID INT, EventDate DATE, col1 INT) ENGINE=MergeTree(EventDate, (CounterID, EventDate), 8192)", 0, -1),
|
||||
std::make_tuple<std::string, int>("CREATE TABLE table1 (CounterID INT, EventDate DATE, col1 INT) ENGINE=MergeTree(EventDate, (CounterID, EventDate), 8192)", 1148, -1), // the second time it must fails
|
||||
std::make_tuple<std::string, int>("INSERT INTO table1 VALUES (1,NOW(),1)", 1148, -1),
|
||||
std::make_tuple<std::string, int>("INSERT INTO table1 SELECT 1,NOW(),1", 0, -1),
|
||||
std::make_tuple<std::string, int>("SELECT * FROM table1", 0, 1),
|
||||
std::make_tuple<std::string, int>("INSERT INTO table1 SELECT * FROM table1", 0, -1),
|
||||
std::make_tuple<std::string, int>("SELECT * FROM table1", 0, 2),
|
||||
std::make_tuple<std::string, int>("TRUNCATE TABLE table1", 1148, -1),
|
||||
std::make_tuple<std::string, int>("DROP TABLE IF EXISTS table1", 0, -1),
|
||||
std::make_tuple<std::string, int>("CREATE TABLE table1 (CounterID INT, EventDate DATE, col1 INT) ENGINE=MergeTree(EventDate, (CounterID, EventDate), 8192)", 0, -1),
|
||||
std::make_tuple<std::string, int>("SELECT * FROM table1", 0, 0),
|
||||
std::make_tuple<std::string, int>("INSERT INTO table1 SELECT 1,'2022-06-23',1", 0, -1),
|
||||
std::make_tuple<std::string, int>("INSERT INTO table1 SELECT 2,'2022-06-23',1", 0, -1),
|
||||
std::make_tuple<std::string, int>("INSERT INTO table1 SELECT CounterID+2, '2022-06-23', 1 FROM table1", 0, -1),
|
||||
std::make_tuple<std::string, int>("SELECT * FROM table1 ORDER BY CounterID", 0, 4),
|
||||
std::make_tuple<std::string, int>("INSERT INTO table1 SELECT * FROM table1", 0, -1),
|
||||
std::make_tuple<std::string, int>("INSERT INTO table1 SELECT * FROM table1", 0, -1),
|
||||
std::make_tuple<std::string, int>("SELECT CounterID, EventDate, SUM(col1) s FROM table1 GROUP BY CounterID,EventDate ORDER BY CounterID", 0, 4),
|
||||
std::make_tuple<std::string, int>("SELECT * FROM table1 t1 JOIN table1 t2 ON t1.CounterID==t2.CounterID ORDER BY t1.CounterID", 0, 64),
|
||||
std::make_tuple<std::string, int>("DESC table1", 0, 3),
|
||||
std::make_tuple<std::string, int>("SHOW COLUMNS FROM table1", 0, 3),
|
||||
std::make_tuple<std::string, int>("LOCK TABLE table1", 0, -1),
|
||||
std::make_tuple<std::string, int>("UNLOCK TABLE table1", 0, -1),
|
||||
};
|
||||
std::vector<query_spec> queries_set2 {
|
||||
std::make_tuple<std::string, int>("DROP TABLE IF EXISTS table2", 0, -1),
|
||||
std::make_tuple<std::string, int>("CREATE TABLE table2 (CounterID INT, EventDate DATE, col0 INT, col1 Nullable(INT), col2 Nullable(UInt8), col3 Nullable(UInt16), col4 Nullable(UInt32), col5 Nullable(UInt64), col6 Nullable(Float32), col7 Nullable(Float64), col8 Nullable(Enum8('hello' = 1, 'world' = 2)) , col9 Nullable(Enum16('hello' = 1, 'world' = 2))) ENGINE=MergeTree(EventDate, (CounterID, EventDate), 8192)", 0, -1),
|
||||
std::make_tuple<std::string, int>("INSERT INTO table2 SELECT 1,'2022-06-23', 0, 1, 2, 3, 4, 5, 6, 7, 'hello', 'world'", 0, -1),
|
||||
std::make_tuple<std::string, int>("INSERT INTO table2 SELECT 1,'2022-06-23', 1, 2, 3, 4, 5, 6, 7, 'hello', 'world'", 1148, -1), // incorrect number of values
|
||||
std::make_tuple<std::string, int>("INSERT INTO table2 SELECT 1,'2022-06-23', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL", 1148, -1), // col0 can't be null
|
||||
std::make_tuple<std::string, int>("INSERT INTO table2 SELECT 1,'2022-06-23', 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL", 0, -1),
|
||||
std::make_tuple<std::string, int>("SELECT * FROM table2 ORDER BY CounterID", 0, 2),
|
||||
std::make_tuple<std::string, int>("DESC table2", 0, 12),
|
||||
std::make_tuple<std::string, int>("SHOW COLUMNS FROM table2", 0, 12),
|
||||
};
|
||||
|
||||
std::vector<query_spec> queries_set3 {
|
||||
std::make_tuple<std::string, int>("SHOW FULL TABLES FROM `default`", 0, 2), // table1 and table2
|
||||
std::make_tuple<std::string, int>("SHOW CHARSET", 0, 42),
|
||||
std::make_tuple<std::string, int>("SET AUTOCOMMIT=0", 0, -1),
|
||||
std::make_tuple<std::string, int>("SET foreign_key_checks=0", 0, -1),
|
||||
std::make_tuple<std::string, int>("/*!40101 SET whatever", 0, -1),
|
||||
std::make_tuple<std::string, int>("SET NAMES utf8", 0, -1),
|
||||
std::make_tuple<std::string, int>("SET WAIT_TIMEOUT=10", 0, -1),
|
||||
std::make_tuple<std::string, int>("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED", 0, -1),
|
||||
std::make_tuple<std::string, int>("SET SQL_SAFE_UPDATES = OFF", 0, -1),
|
||||
std::make_tuple<std::string, int>("SET SQL_AUTO_IS_NULL = OFF", 0, -1),
|
||||
std::make_tuple<std::string, int>("SHOW GLOBAL VARIABLES", 0, 6),
|
||||
std::make_tuple<std::string, int>("SHOW ALL VARIABLES", 0, 6),
|
||||
std::make_tuple<std::string, int>("SHOW GLOBAL STATUS", 0, 6),
|
||||
std::make_tuple<std::string, int>("SHOW ENGINES", 0, 1),
|
||||
std::make_tuple<std::string, int>("SHOW VARIABLES LIKE 'lower_case_table_names'", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT * FROM INFORMATION_SCHEMA.COLLATIONS", 0, 375),
|
||||
std::make_tuple<std::string, int>("SHOW COLLATION", 0, 375),
|
||||
std::make_tuple<std::string, int>("SHOW CHARSET", 0, 42),
|
||||
std::make_tuple<std::string, int>("SELECT * FROM INFORMATION_SCHEMA.CHARACTER_SETS", 0, 42),
|
||||
std::make_tuple<std::string, int>("SELECT @@collation_server", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT @@character_set_results", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT @@have_profiling", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT @@lower_case_table_names", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT @@version, @@version_comment", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT @@storage_engine", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT `SCHEMA_NAME` FROM `INFORMATION_SCHEMA`.`SCHEMATA`", 0, 4),
|
||||
std::make_tuple<std::string, int>("select name, type FROM mysql.proc where db='default'", 0, 0),
|
||||
std::make_tuple<std::string, int>("SELECT logfile_group_name FROM information_schema.FILES", 0, 0),
|
||||
std::make_tuple<std::string, int>("SELECT tablespace_name FROM information_schema.FILES", 0, 0),
|
||||
std::make_tuple<std::string, int>("SELECT CONNECTION_ID()", 0, 1),
|
||||
std::make_tuple<std::string, int>("select @@version_comment limit 1", 0, 1),
|
||||
std::make_tuple<std::string, int>("select DATABASE(), USER() limit 1", 0, 1),
|
||||
std::make_tuple<std::string, int>("select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1", 0, 1),
|
||||
std::make_tuple<std::string, int>("SELECT @@version", 0, 1),
|
||||
std::make_tuple<std::string, int>("SHOW TABLES FROM default", 0, 2),
|
||||
std::make_tuple<std::string, int>("SELECT DATABASE() AS name", 0, 1),
|
||||
std::make_tuple<std::string, int>("SHOW MASTER STATUS", 1045, -1),
|
||||
std::make_tuple<std::string, int>("SHOW SLAVE STATUS", 1045, -1),
|
||||
std::make_tuple<std::string, int>("SHOW MASTER LOGS", 1045, -1),
|
||||
std::make_tuple<std::string, int>("LOCK TABLE table1", 0, -1),
|
||||
std::make_tuple<std::string, int>("UNLOCK TABLE table1", 0, -1),
|
||||
};
|
||||
/**
|
||||
* @brief Perform several admin queries to exercise more paths.
|
||||
*/
|
||||
std::vector<std::string> admin_queries {
|
||||
"LOAD CLICKHOUSE VARIABLES FROM DISK",
|
||||
"LOAD CLICKHOUSE VARIABLES TO RUNTIME",
|
||||
"SAVE CLICKHOUSE VARIABLES FROM RUNTIME",
|
||||
"SAVE CLICKHOUSE VARIABLES TO DISK"
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Perform several admin queries to exercise more paths.
|
||||
*/
|
||||
std::vector<std::string> ch_intf_queries {
|
||||
"SET clickhouse-mysql_ifaces='127.0.0.1:6091'",
|
||||
"LOAD CLICKHOUSE VARIABLES TO RUNTIME"
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
CommandLine cl;
|
||||
|
||||
// plan as many tests as queries
|
||||
plan(
|
||||
crash_loops
|
||||
+ 2 /* Fail to connect with wrong username and password */
|
||||
+ 4 // during LOAD USERS TO RUNTIME
|
||||
+ 4 // during LOAD USERS TO RUNTIME , second time
|
||||
+ queries_set1.size()
|
||||
+ queries_set2.size()
|
||||
+ queries_set3.size()
|
||||
+ admin_queries.size() + ch_intf_queries.size()
|
||||
+ 1 /* Connect to new setup interface */
|
||||
);
|
||||
|
||||
if (cl.getEnv()) {
|
||||
diag("Failed to get the required environmental variables.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
MYSQL* proxysql_admin = mysql_init(NULL);
|
||||
|
||||
// Connect to ProxySQL Admin and check current clickhouse configuration
|
||||
if (
|
||||
!mysql_real_connect(
|
||||
proxysql_admin, cl.host, cl.admin_username, cl.admin_password,
|
||||
NULL, cl.admin_port, NULL, 0
|
||||
)
|
||||
) {
|
||||
fprintf(
|
||||
stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__,
|
||||
mysql_error(proxysql_admin)
|
||||
);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
create_users(proxysql_admin);
|
||||
create_users(proxysql_admin); // to trigger more code coverage
|
||||
|
||||
|
||||
{
|
||||
std::pair<std::string, int> host_port {};
|
||||
int host_port_err = extract_module_host_port(proxysql_admin, "clickhouse-mysql_ifaces", host_port);
|
||||
if (host_port_err) {
|
||||
diag("Failed to get and parse 'clickhouse-mysql_ifaces' at line '%d'", __LINE__);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
set_clickhouse_port(proxysql_admin,8000);
|
||||
test_crash(host_port.first.c_str(), host_port.second);
|
||||
set_clickhouse_port(proxysql_admin,19000);
|
||||
|
||||
MYSQL* proxysql_clickhouse = mysql_init(NULL);
|
||||
|
||||
// Connect with invalid username
|
||||
std::string inv_user_err {};
|
||||
bool failed_to_connect = false;
|
||||
if (
|
||||
!mysql_real_connect(
|
||||
proxysql_clickhouse, host_port.first.c_str(), "foobar_user", cl.password,
|
||||
NULL, host_port.second, NULL, 0
|
||||
)
|
||||
) {
|
||||
inv_user_err = mysql_error(proxysql_clickhouse);
|
||||
failed_to_connect = true;
|
||||
}
|
||||
|
||||
ok(
|
||||
failed_to_connect,
|
||||
"An invalid user should fail to connect to Clickhouse server, error was: %s",
|
||||
inv_user_err.c_str()
|
||||
);
|
||||
|
||||
// Reinitialize MYSQL handle
|
||||
mysql_close(proxysql_clickhouse);
|
||||
proxysql_clickhouse = mysql_init(NULL);
|
||||
|
||||
// Connect with invalid password
|
||||
std::string inv_pass_err {};
|
||||
failed_to_connect = false;
|
||||
if (
|
||||
!mysql_real_connect(
|
||||
proxysql_clickhouse, host_port.first.c_str(), credentials[0].first.c_str(), "foobar_pass",
|
||||
NULL, host_port.second, NULL, 0
|
||||
)
|
||||
) {
|
||||
inv_pass_err = mysql_error(proxysql_clickhouse);
|
||||
failed_to_connect = true;
|
||||
}
|
||||
|
||||
ok(
|
||||
failed_to_connect,
|
||||
"An invalid pass should fail to connect to Clickhouse server, error was: %s",
|
||||
inv_pass_err.c_str()
|
||||
);
|
||||
|
||||
// Reinitialize MYSQL handle
|
||||
mysql_close(proxysql_clickhouse);
|
||||
proxysql_clickhouse = mysql_init(NULL);
|
||||
|
||||
// Correctly connect to Clickhouse server
|
||||
if (
|
||||
!mysql_real_connect(
|
||||
proxysql_clickhouse, host_port.first.c_str(), credentials[0].first.c_str(), credentials[0].second.c_str(),
|
||||
NULL, host_port.second, NULL, 0
|
||||
)
|
||||
) {
|
||||
fprintf(
|
||||
stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__,
|
||||
mysql_error(proxysql_clickhouse)
|
||||
);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
diag("Started performing queries set 1");
|
||||
if (execute_and_check_queries(proxysql_clickhouse, queries_set1)) {
|
||||
return exit_status();
|
||||
}
|
||||
|
||||
diag("Started performing queries set 2");
|
||||
if (execute_and_check_queries(proxysql_clickhouse, queries_set2)) {
|
||||
return exit_status();
|
||||
}
|
||||
|
||||
diag("Started performing queries set 3");
|
||||
if (execute_and_check_queries(proxysql_clickhouse, queries_set3)) {
|
||||
return exit_status();
|
||||
}
|
||||
|
||||
// Reinitialize MYSQL handle
|
||||
mysql_close(proxysql_clickhouse);
|
||||
proxysql_clickhouse = mysql_init(NULL);
|
||||
|
||||
// Change Clickhouse interface and connect to new port
|
||||
for (const auto& admin_query : ch_intf_queries) {
|
||||
int query_err = mysql_query(proxysql_admin, admin_query.c_str());
|
||||
ok(
|
||||
query_err == 0, "Admin query '%s' should succeed. Line: %d, Err: '%s'",
|
||||
admin_query.c_str(), __LINE__, mysql_error(proxysql_admin)
|
||||
);
|
||||
}
|
||||
// NOTE: Wait for ProxySQL to reconfigure, changing Clickhous interface.
|
||||
// Trying to perform a connection immediately after changing the
|
||||
// interface could lead to 'EADDRINUSE' in ProxySQL side.
|
||||
sleep(1);
|
||||
|
||||
// Connect to the new interface
|
||||
std::pair<std::string, int> new_host_port {};
|
||||
int ext_intf_err = extract_module_host_port(proxysql_admin, "clickhouse-mysql_ifaces", new_host_port);
|
||||
if (ext_intf_err) {
|
||||
diag("Failed to get and parse 'clickhouse-mysql_ifaces' at line '%d'", __LINE__);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Connect with invalid username
|
||||
bool success_to_connect = true;
|
||||
std::string new_intf_conn_err {};
|
||||
if (
|
||||
!mysql_real_connect(
|
||||
proxysql_clickhouse, new_host_port.first.c_str(), credentials[1].first.c_str(), credentials[1].second.c_str(),
|
||||
NULL, new_host_port.second, NULL, 0
|
||||
)
|
||||
) {
|
||||
new_intf_conn_err = mysql_error(proxysql_clickhouse);
|
||||
success_to_connect = false;
|
||||
}
|
||||
|
||||
ok(
|
||||
success_to_connect,
|
||||
"A connection to the new selected interface should success, error was: '%s'",
|
||||
new_intf_conn_err.c_str()
|
||||
);
|
||||
|
||||
mysql_close(proxysql_clickhouse);
|
||||
|
||||
// Perform the final Admin queries
|
||||
for (const auto& admin_query : admin_queries) {
|
||||
int query_err = mysql_query(proxysql_admin, admin_query.c_str());
|
||||
ok(
|
||||
query_err == 0, "Admin query '%s' should succeed. Line: %d, Err: '%s'",
|
||||
admin_query.c_str(), __LINE__, mysql_error(proxysql_admin)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
mysql_close(proxysql_admin);
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
Loading…
Reference in new issue