@ -22,9 +22,6 @@
# include "tap.h"
# include "utils.h"
using std : : string ;
using std : : vector ;
/**
* @ brief List of the pairs holding the unsupported queries to be executed by ProxySQL
* together with the error code that they should return .
@ -68,13 +65,22 @@ using query_test_info =
std : : function < void ( const CommandLine & , MYSQL * , int , bool ) >
> ;
// "SET mysql-enable_load_data_local_infile='true'",
/**
* @ brief Enable the query based using the information supplied in the
* ' query_info ' parameter , and verifies that the value of the query has properly
* change at runtime .
*
* @ param proxysql_admin An already oppened connection to ProxySQL Admin .
* @ param query_info Information about the query to be enabled .
*
* @ return True if the query was properly enabled , false if not .
*/
bool enable_query ( MYSQL * proxysql_admin , const query_test_info & query_info , bool enable = true ) {
std : : string exp_var_value { } ;
// In case of false, we choose the value for disabling the variable
if ( enable = = true ) {
exp_var_value = std : : get < 2 > ( query_info ) ;
} else {
@ -86,17 +92,59 @@ bool enable_query(MYSQL* proxysql_admin, const query_test_info& query_info, bool
" LOAD MYSQL VARIABLES TO RUNTIME "
} ;
bool success = true ;
bool query_enabling_succeed = true ;
for ( const auto & query : enabling_queries ) {
diag ( " Admin Query: %s " , query . c_str ( ) ) ;
if ( mysql_query ( proxysql_admin , query . c_str ( ) ) ) {
diag ( " ERROR: ProxySQL Admin Error: %s " , mysql_error ( proxysql_admin ) ) ;
success = false ;
break ;
int query_res = mysql_query ( proxysql_admin , query . c_str ( ) ) ;
if ( query_res ) {
diag (
" Query '%s' for enabling query '%s' enabling at line '%d', with error: '%s' " ,
query . c_str ( ) , std : : get < 0 > ( query_info ) . c_str ( ) , __LINE__ ,
mysql_error ( proxysql_admin )
) ;
query_enabling_succeed = false ;
goto exit ;
}
}
{
std : : string variable_value { } ;
int var_err = get_variable_value (
proxysql_admin , std : : get < 1 > ( query_info ) , variable_value , true
) ;
if ( var_err ) {
diag (
" Getting value for variable '%s', failed with error: '%d' " ,
std : : get < 1 > ( query_info ) . c_str ( ) , var_err
) ;
query_enabling_succeed = false ;
goto exit ;
}
// perform a final conversion in case it's required for the exp value
std : : string f_exp_var_value { } ;
if ( exp_var_value = = " 'true' " ) {
f_exp_var_value = " true " ;
} else if ( exp_var_value = = " 'false' " ) {
f_exp_var_value = " false " ;
} else {
f_exp_var_value = exp_var_value ;
}
if ( variable_value ! = f_exp_var_value ) {
query_enabling_succeed = false ;
diag (
" Variable value doesn't match expected: (Exp: '%s', Act: '%s') " ,
exp_var_value . c_str ( ) , variable_value . c_str ( )
) ;
goto exit ;
}
}
ok ( success , " %s query '%s' should succeed. " , ( enable ? " Enabling " : " Disabling " ) , std : : get < 0 > ( query_info ) . c_str ( ) ) ;
return success ;
exit :
return query_enabling_succeed ;
}
// ******************* QUERIES TESTING FUNCTIONS ******************** //
@ -112,188 +160,464 @@ using mysql_res_row = std::vector<std::string>;
/**
* @ brief Helper function that performs the actual check for ' test_load_data_local_infile ' .
*
* @ param cl CommandLine parameters required for the test .
* @ param proxysql An already oppened connection to ProxySQL .
* @ param exp_err The expected error code in case we are testing for failure ,
* ' 0 ' by default .
* @ param test_for_success Select the operation mode of the test , ' true ' for
* testing for success , ' false ' for failure . It ' s ' true ' by default .
*/
void helper_test_load_data_local_infile (
const CommandLine & cl , MYSQL * proxysql , int exp_err = 0 , bool test_for_success = true
) {
// PROVISIONING: Create shared directory and data file in /var/lib/proxysql
// This ensures the server container can see the local file we want to load.
string script_dst = " /var/lib/proxysql/load_data_local_datadir " ;
string datafile = script_dst + " /insert_data.txt " ;
diag ( " Step: Provisioning LOAD DATA test files... " ) ;
diag ( " - Target directory: %s " , script_dst . c_str ( ) ) ;
string cmd = " mkdir -p " + script_dst + " && printf '1, \" a string \" ,100.20 \n 2, \" a string containing a , comma \" ,102.20 \n 3, \" a string containing a \\ \" quote \" ,102.20 \n 4, \" a string containing a \\ \" , quote and comma \" ,102.20 \n ' > " + datafile + " && chmod -R 777 " + script_dst ;
if ( system ( cmd . c_str ( ) ) ! = 0 ) {
diag ( " WARNING: Failed to provision test files to %s. Test may fail. " , script_dst . c_str ( ) ) ;
}
// Provision test data to shared volume
std : : string script_dst = " /var/lib/proxysql/load_data_local_datadir " ;
std : : string datafile_ci = script_dst + " /insert_data.txt " ;
std : : string cmd = " mkdir -p " + script_dst + " && printf '1, \" a string \" ,100.20 \n 2, \" a string containing a , comma \" ,102.20 \n 3, \" a string containing a \\ \" quote \" ,102.20 \n 4, \" a string containing a \\ \" , quote and comma \" ,102.20 \n ' > " + datafile_ci + " && chmod -R 777 " + script_dst ;
system ( cmd . c_str ( ) ) ;
const char * d_env = getenv ( " REGULAR_INFRA_DATADIR " ) ;
std : : string datafile = ( d_env ? std : : string ( d_env ) + " /load_data_local_datadir/insert_data.txt "
: std : : string ( cl . workdir ) + " load_data_local_datadir/insert_data.txt " ) ;
diag ( " Data file path: %s " , datafile . c_str ( ) ) ;
bool table_prep_success = true ;
diag ( " Step: Preparing test table... " ) ;
for ( const auto & query : prepare_table_queries ) {
diag ( " - Query: %s " , query . c_str ( ) ) ;
if ( mysql_query ( proxysql , query . c_str ( ) ) ) {
diag ( " - ERROR: %s " , mysql_error ( proxysql ) ) ;
int query_res = mysql_query ( proxysql , query . c_str ( ) ) ;
if ( query_res ) {
diag (
" Query '%s' for table preparation failed at line '%d', with error: '%s' " ,
query . c_str ( ) , __LINE__ , mysql_error ( proxysql )
) ;
table_prep_success = false ;
break ;
}
}
std : : string t_load_data_command {
" LOAD DATA LOCAL INFILE \" %s \" INTO TABLE test.load_data_local "
" FIELDS TERMINATED BY ',' ENCLOSED BY ' \" ' LINES TERMINATED BY ' \\ n' "
} ;
std : : string load_data_command { } ;
string_format ( t_load_data_command , load_data_command , datafile . c_str ( ) ) ;
if ( table_prep_success ) {
std : : string t_load_data_command {
" LOAD DATA LOCAL INFILE \" %s \" INTO TABLE test.load_data_local "
" FIELDS TERMINATED BY ',' ENCLOSED BY ' \" ' LINES TERMINATED BY ' \\ n' "
} ;
std : : string load_data_command { } ;
string_format ( t_load_data_command , load_data_command , datafile . c_str ( ) ) ;
diag ( " Step: Executing LOAD DATA LOCAL INFILE... " ) ;
diag ( " - Query: %s " , load_data_command . c_str ( ) ) ;
int load_data_res = mysql_query ( proxysql , load_data_command . c_str ( ) ) ;
int load_data_res =
mysql_query ( proxysql , load_data_command . c_str ( ) ) ;
if ( test_for_success ) {
if ( load_data_res = = EXIT_SUCCESS ) {
diag ( " - SUCCESS: Query returned no error. " ) ;
} else {
diag ( " - FAILURE: Query failed. Error: %s " , mysql_error ( proxysql ) ) ;
}
if ( test_for_success ) {
if ( load_data_res ) {
diag (
load_data_command . c_str ( ) , __LINE__ , mysql_error ( proxysql )
) ;
}
diag ( " Step: Verifying data integrity via SELECT... " ) ;
mysql_query ( proxysql , " SELECT * /* ;hostgroup=0 */ FROM test.load_data_local " ) ;
MYSQL_RES * result = mysql_store_result ( proxysql ) ;
std : : vector < mysql_res_row > rows_res { extract_mysql_rows ( result ) } ;
std : : vector < mysql_res_row > exp_rows {
{ " 1 " , " a string " , " 100.20 " } ,
{ " 2 " , " a string containing a , comma " , " 102.20 " } ,
{ " 3 " , " a string containing a \" quote " , " 102.20 " } ,
{ " 4 " , " a string containing a \" , quote and comma " , " 102.20 " }
} ;
if ( load_data_res = = EXIT_SUCCESS ) {
diag (
" Supplied query '%s' succeeded, performing check on data... " ,
load_data_command . c_str ( )
) ;
} else {
diag (
" Supplied query '%s' failed, check not going to be performed. Error was: '%s'. " ,
load_data_command . c_str ( ) , mysql_error ( proxysql )
) ;
}
// Detailed row comparison output
diag ( " - Rows found: %zu, Expected: %zu " , rows_res . size ( ) , exp_rows . size ( ) ) ;
if ( rows_res . size ( ) ! = exp_rows . size ( ) ) {
diag ( " - ERROR: Row count mismatch! " ) ;
}
// Check that the data has actually been loaded to the database
// NOTE: Specifically target 'hostgroup=0' to avoid replication lag.
int myerr = mysql_query ( proxysql , " SELECT * /* ;hostgroup=0 */ FROM test.load_data_local " ) ;
if ( myerr ) {
diag (
" Query 'SELECT * FROM test.load_data_local' for table preparation failed "
" at line '%d', with error: '%s' " , __LINE__ , mysql_error ( proxysql )
) ;
} else {
MYSQL_RES * result = mysql_store_result ( proxysql ) ;
std : : vector < mysql_res_row > rows_res { extract_mysql_rows ( result ) } ;
std : : vector < mysql_res_row > exp_rows {
{ " 1 " , " a string " , " 100.20 " } ,
{ " 2 " , " a string containing a , comma " , " 102.20 " } ,
{ " 3 " , " a string containing a \" quote " , " 102.20 " } ,
{ " 4 " , " a string containing a \" , quote and comma " , " 102.20 " }
} ;
std : : string exp_rows_str { " { \n " } ;
for ( const auto & exp_row : exp_rows ) {
std : : string exp_row_str { nlohmann : : json ( exp_row ) . dump ( ) } ;
exp_rows_str + = " " + exp_row_str + " , \n " ;
}
exp_rows_str + = " } \n " ;
bool equal = ( rows_res . size ( ) = = exp_rows . size ( ) ) ;
if ( equal ) equal = std : : equal ( exp_rows . begin ( ) , exp_rows . end ( ) , rows_res . begin ( ) ) ;
ok ( equal , " The selected ROWS were equal to the expected ones " ) ;
if ( result ) mysql_free_result ( result ) ;
} else {
int my_errno = mysql_errno ( proxysql ) ;
diag ( " - Expected error: %d, Actual error: %d " , exp_err , my_errno ) ;
ok ( my_errno = = exp_err , " Query should fail with error %d. Actual: %d (%s) " , exp_err , my_errno , mysql_error ( proxysql ) ) ;
diag ( " Expected values for rows were: \n %s " , exp_rows_str . c_str ( ) ) ;
std : : string act_rows_str { " { \n " } ;
for ( const auto & act_row : rows_res ) {
std : : string act_row_str { nlohmann : : json ( act_row ) . dump ( ) } ;
act_rows_str + = " " + act_row_str + " , \n " ;
}
act_rows_str + = " } \n " ;
diag ( " Actual values for found rows were: \n %s " , act_rows_str . c_str ( ) ) ;
bool equal = false ;
if ( ! rows_res . empty ( ) ) {
equal = std : : equal ( exp_rows . begin ( ) , exp_rows . end ( ) , rows_res . begin ( ) ) ;
}
ok ( equal , " The selected ROWS were equal to the expected ones " ) ;
}
} else {
if ( load_data_res ) {
diag (
load_data_command . c_str ( ) , __LINE__ , mysql_error ( proxysql )
) ;
}
int my_errno = mysql_errno ( proxysql ) ;
ok (
my_errno = = exp_err ,
" Query '%s' should fail. ErrCode: '%d', and error: '%s' " ,
load_data_command . c_str ( ) , my_errno , mysql_error ( proxysql )
) ;
}
}
}
/**
* @ brief Perform the same test as ' test_load_data_local_infile ' , but with
* ' mysql - verbose_query_error ' set to ' true ' . This test only purpose is
* to exercise the code performing the additional extra logging .
*/
void test_verbose_error_load_data_local_infile (
const CommandLine & cl , MYSQL * proxysql , int exp_err = 0 , bool test_for_success = true
) {
diag ( " Sub-Test: Testing with mysql-verbose_query_error=true " ) ;
MYSQL * admin = mysql_init ( NULL ) ;
if ( mysql_real_connect ( admin , cl . host , cl . admin_username , cl . admin_password , NULL , cl . admin_port , NULL , 0 ) ) {
mysql_query ( admin , " SET mysql-verbose_query_error='true' " ) ;
mysql_query ( admin , " LOAD MYSQL VARIABLES TO RUNTIME " ) ;
helper_test_load_data_local_infile ( cl , proxysql , exp_err , test_for_success ) ;
mysql_query ( admin , " SET mysql-verbose_query_error='false' " ) ;
mysql_query ( admin , " LOAD MYSQL VARIABLES TO RUNTIME " ) ;
mysql_close ( admin ) ;
MYSQL * proxysql_admin = mysql_init ( NULL ) ;
if (
! mysql_real_connect (
proxysql_admin , cl . host , cl . admin_username , cl . admin_password , NULL , cl . admin_port , NULL , 0
)
) {
diag ( " File %s, line %d, Error: %s \n " , __FILE__ , __LINE__ , mysql_error ( proxysql_admin ) ) ;
return ;
}
std : : vector < std : : string > verbose_query_error_true {
" SET mysql-verbose_query_error='true' " ,
" LOAD MYSQL VARIABLES TO RUNTIME "
} ;
for ( const auto & query : verbose_query_error_true ) {
int query_err = mysql_query ( proxysql_admin , query . c_str ( ) ) ;
if ( query_err ) {
diag ( " File %s, line %d, Error: %s \n " , __FILE__ , __LINE__ , mysql_error ( proxysql_admin ) ) ;
return ;
}
}
helper_test_load_data_local_infile ( cl , proxysql , exp_err , test_for_success ) ;
std : : vector < std : : string > verbose_query_error_false {
" SET mysql-verbose_query_error='false' " ,
" LOAD MYSQL VARIABLES TO RUNTIME "
} ;
for ( const auto & query : verbose_query_error_false ) {
int query_err = mysql_query ( proxysql_admin , query . c_str ( ) ) ;
if ( query_err ) {
diag ( " File %s, line %d, Error: %s \n " , __FILE__ , __LINE__ , mysql_error ( proxysql_admin ) ) ;
return ;
}
}
mysql_close ( proxysql_admin ) ;
}
/**
* @ brief Test that the query ' LOAD DATA LOCAL INFILE ' performs correctly when
* enabled , and returns the proper error code when disabled . Performs one
* ' ok ( ) ' call in case everything went as expected , and several ' diag ( ) ' call
* in case of errors .
*
* @ param cl CommandLine parameters required for the test .
* @ param proxysql An already oppened connection to ProxySQL .
* @ param exp_err The expected error code in case we are testing for failure ,
* ' 0 ' by default .
* @ param test_for_success Select the operation mode of the test , ' true ' for
* testing for success , ' false ' for failure . It ' s ' true ' by default .
*/
void test_load_data_local_infile (
const CommandLine & cl , MYSQL * proxysql , int exp_err = 0 , bool test_for_success = true
) {
diag ( " Sub-Test: Standard LOAD DATA LOCAL INFILE test " ) ;
helper_test_load_data_local_infile ( cl , proxysql , exp_err , test_for_success ) ;
}
/**
* @ brief Analogous function to ' test_load_data_local_infile ' but it
* deliberately provides a non - existing file as an argument to make the query
* fail .
*
* @ details This way we make sure that ProxySQL is exhibiting proper behavior
* for this unsupported query that can be misused .
* @ param cl CommandLine parameters required for the test .
* @ param proxysql An already oppened connection to ProxySQL .
* @ param exp_err The expected error code in case we are testing for failure ,
* ' 0 ' by default .
* @ param test_for_success Select the operation mode of the test , ' true ' for
* testing for success , ' false ' for failure . It ' s ' true ' by default .
*/
void test_failing_load_data_local_infile (
const CommandLine & cl , MYSQL * proxysql , int exp_err = 0 , bool test_for_success = true
) {
diag ( " Sub-Test: Testing failure with non-existing file " ) ;
std : : string datafile = " /var/lib/proxysql/load_data_local_datadir/non_existing_file.txt " ;
diag ( " - Using invalid path: %s " , datafile . c_str ( ) ) ;
// Supply an invalid file
const char * d_env = getenv ( " REGULAR_INFRA_DATADIR " ) ;
std : : string datafile = ( d_env ? std : : string ( d_env ) + " /load_data_local_datadir/non_existing_file.txt "
: std : : string ( cl . workdir ) + " load_data_local_datadir/non_existing_file.txt " ) ;
diag ( " Data file path: %s " , datafile . c_str ( ) ) ;
bool table_prep_success = true ;
for ( const auto & query : prepare_table_queries ) {
mysql_query ( proxysql , query . c_str ( ) ) ;
int query_res = mysql_query ( proxysql , query . c_str ( ) ) ;
if ( query_res ) {
diag (
" Query '%s' for table preparation failed at line '%d', with error: '%s' " ,
query . c_str ( ) , __LINE__ , mysql_error ( proxysql )
) ;
table_prep_success = false ;
break ;
}
}
std : : string load_data_command = " LOAD DATA LOCAL INFILE \" " + datafile + " \" INTO TABLE test.load_data_local " ;
int load_data_res = mysql_query ( proxysql , load_data_command . c_str ( ) ) ;
if ( table_prep_success ) {
std : : string t_load_data_command {
" LOAD DATA LOCAL INFILE \" %s \" INTO TABLE test.load_data_local "
} ;
std : : string load_data_command { } ;
string_format ( t_load_data_command , load_data_command , datafile . c_str ( ) ) ;
if ( test_for_success ) {
int my_errno = mysql_errno ( proxysql ) ;
diag ( " - Actual error returned: %d (%s) " , my_errno , mysql_error ( proxysql ) ) ;
ok ( ( load_data_res ! = EXIT_SUCCESS ) & & my_errno = = 2 , " Query should fail with Errcode 2 (File not found) " ) ;
} else {
int my_errno = mysql_errno ( proxysql ) ;
diag ( " - Expected error: %d, Actual: %d " , exp_err , my_errno ) ;
ok ( my_errno = = exp_err , " Query should fail with error %d. Actual: %d " , exp_err , my_errno ) ;
int load_data_res =
mysql_query ( proxysql , load_data_command . c_str ( ) ) ;
if ( test_for_success ) {
if ( load_data_res ) {
diag (
load_data_command . c_str ( ) , __LINE__ , mysql_error ( proxysql )
) ;
}
int my_errno = mysql_errno ( proxysql ) ;
ok (
( load_data_res ! = EXIT_SUCCESS ) & & my_errno = = 2 ,
" Query '%s' should fail. ErrCode: '%d', and error: '%s' " ,
load_data_command . c_str ( ) , mysql_errno ( proxysql ) , mysql_error ( proxysql )
) ;
} else {
if ( load_data_res ) {
diag (
load_data_command . c_str ( ) , __LINE__ , mysql_error ( proxysql )
) ;
}
int my_errno = mysql_errno ( proxysql ) ;
ok (
my_errno = = exp_err ,
" Query '%s' should fail. ErrCode: '%d', and error: '%s' " ,
load_data_command . c_str ( ) , my_errno , mysql_error ( proxysql )
) ;
}
}
}
// ****************************************************************** //
// ********************* QUERIES TESTS INFO ************************ //
/**
* @ brief List of queries which need to be check before performing the
* ' unsupported ' checks .
*/
std : : vector < query_test_info > queries_tests_info {
std : : make_tuple < string , string , string , string , int , std : : function < void ( const CommandLine & , MYSQL * , int , bool ) > > ( " LOAD DATA LOCAL INFILE " , " mysql-enable_load_data_local_infile " , " 'true' " , " 'false' " , 1047 , test_load_data_local_infile ) ,
std : : make_tuple < string , string , string , string , int , std : : function < void ( const CommandLine & , MYSQL * , int , bool ) > > ( " LOAD DATA LOCAL INFILE " , " mysql-enable_load_data_local_infile " , " 'true' " , " 'false' " , 1047 , test_failing_load_data_local_infile ) ,
std : : make_tuple < string , string , string , string , int , std : : function < void ( const CommandLine & , MYSQL * , int , bool ) > > ( " LOAD DATA LOCAL INFILE " , " mysql-enable_load_data_local_infile " , " 'true' " , " 'false' " , 1047 , test_verbose_error_load_data_local_infile ) ,
std : : make_tuple <
std : : string , std : : string , std : : string , std : : string , int ,
std : : function < void ( const CommandLine & , MYSQL * , int , bool ) >
> (
// Query to be tested
" LOAD DATA LOCAL INFILE " ,
// Variable name enabling / disabling the query
" mysql-enable_load_data_local_infile " ,
// Value for enabling the query
" 'true' " ,
// Value for diabling the query
" 'false' " ,
// Expected error code in case of failure
1047 ,
// Function performing an internal 'ok' test checking that the
// enabled / disabled query responds as expected
test_load_data_local_infile
) ,
std : : make_tuple <
std : : string , std : : string , std : : string , std : : string , int ,
std : : function < void ( const CommandLine & , MYSQL * , int , bool ) >
> (
// Query to be tested
" LOAD DATA LOCAL INFILE " ,
// Variable name enabling / disabling the query
" mysql-enable_load_data_local_infile " ,
// Value for enabling the query
" 'true' " ,
// Value for diabling the query
" 'false' " ,
// Expected error code in case of failure
1047 ,
// Function performing an internal 'ok' test checking that the
// enabled / disabled query responds as expected
test_failing_load_data_local_infile
) ,
std : : make_tuple <
std : : string , std : : string , std : : string , std : : string , int ,
std : : function < void ( const CommandLine & , MYSQL * , int , bool ) >
> (
// Query to be tested
" LOAD DATA LOCAL INFILE " ,
// Variable name enabling / disabling the query
" mysql-enable_load_data_local_infile " ,
// Value for enabling the query
" 'true' " ,
// Value for diabling the query
" 'false' " ,
// Expected error code in case of failure
1047 ,
// Function performing an internal 'ok' test checking that the
// enabled / disabled query responds as expected
test_verbose_error_load_data_local_infile
) ,
} ;
// ****************************************************************** //
int main ( int argc , char * * argv ) {
CommandLine cl ;
if ( cl . getEnv ( ) ) return - 1 ;
diag ( " ================================================================================ " ) ;
diag ( " TEST DESCRIPTION: ProxySQL Unsupported and Conditional Query Validation " ) ;
diag ( " This test verifies that ProxySQL handles unsupported commands (like LOAD DATA LOCAL) " ) ;
diag ( " correctly by returning error 1047, and that these commands can be enabled " ) ;
diag ( " conditionally via global variables. " ) ;
diag ( " ================================================================================ " ) ;
// plan as many tests as queries
plan ( unsupported_queries . size ( ) + 4 * queries_tests_info . size ( ) ) ;
if ( cl . getEnv ( ) ) {
diag ( " Failed to get the required environmental variables. " ) ;
return - 1 ;
}
// Verbose test header
diag ( " ================================================================================ " ) ;
diag ( " Test: test_unsupported_queries-t " ) ;
diag ( " ================================================================================ " ) ;
diag ( " This test verifies that unsupported queries return the expected error codes, " ) ;
diag ( " and that conditionally-enabled queries work correctly when enabled/disabled. " ) ;
diag ( " " ) ;
diag ( " Test scenarios: " ) ;
diag ( " - Unsupported queries (LOAD DATA LOCAL INFILE) should fail with error 1047 " ) ;
diag ( " - Conditionally enabled queries should work when enabled " ) ;
diag ( " - Conditionally enabled queries should fail when disabled " ) ;
diag ( " " ) ;
diag ( " Connection Context: " ) ;
diag ( " Connection parameters :" ) ;
diag ( " - Host: %s " , cl . host ) ;
diag ( " - Port: %d " , cl . port ) ;
diag ( " - Admin Port: %d " , cl . admin_port ) ;
diag ( " ================================================================================ " ) ;
diag ( " " ) ;
plan ( unsupported_queries . size ( ) + 4 * queries_tests_info . size ( ) ) ;
diag ( " Phase 1: Testing naturally unsupported queries (Expecting 1047) " ) ;
// perform a different connection per query
for ( const auto & unsupported_query : unsupported_queries ) {
MYSQL * mysql = mysql_init ( NULL ) ;
if ( mysql_real_connect ( mysql , cl . host , cl . username , cl . password , NULL , cl . port , NULL , 0 ) ) {
string q = std : : get < 0 > ( unsupported_query ) ;
diag ( " - Executing: %s " , q . c_str ( ) ) ;
int query_err = mysql_query ( mysql , q . c_str ( ) ) ;
int m_errno = mysql_errno ( mysql ) ;
ok ( query_err & & ( m_errno = = std : : get < 1 > ( unsupported_query ) ) , " Unsupported query failed as expected " ) ;
mysql_close ( mysql ) ;
MYSQL * proxysql_mysql = mysql_init ( NULL ) ;
// extract the tuple elements
const std : : string query = std : : get < 0 > ( unsupported_query ) ;
const int exp_err_code = std : : get < 1 > ( unsupported_query ) ;
const std : : string exp_err_msg = std : : get < 2 > ( unsupported_query ) ;
if ( ! mysql_real_connect ( proxysql_mysql , cl . host , cl . username , cl . password , NULL , cl . port , NULL , 0 ) ) {
fprintf ( stderr , " File %s, line %d, Error: %s \n " , __FILE__ , __LINE__ , mysql_error ( proxysql_mysql ) ) ;
return EXIT_FAILURE ;
}
int query_err = mysql_query ( proxysql_mysql , query . c_str ( ) ) ;
int m_errno = mysql_errno ( proxysql_mysql ) ;
const char * m_error = mysql_error ( proxysql_mysql ) ;
ok (
query_err & & ( m_errno = = exp_err_code ) & & ( exp_err_msg = = std : : string { m_error } ) ,
" Unsupported query '%s' should fail. Error code: (Expected: '%d' == Actual:'%d'), Error msg: (Expected: '%s' == Actual:'%s') " ,
query . c_str ( ) ,
exp_err_code ,
m_errno ,
exp_err_msg . c_str ( ) ,
m_error
) ;
mysql_close ( proxysql_mysql ) ;
}
diag ( " Phase 2: Testing conditionally-enabled queries via ProxySQL Admin " ) ;
MYSQL * admin = mysql_init ( NULL ) ;
if ( mysql_real_connect ( admin , cl . host , cl . admin_username , cl . admin_password , NULL , cl . admin_port , NULL , 0 ) ) {
for ( const auto & query_test_info : queries_tests_info ) {
MYSQL * mysql = mysql_init ( NULL ) ;
if ( mysql_real_connect ( mysql , cl . host , cl . username , cl . password , NULL , cl . port , NULL , 0 ) ) {
diag ( " Scenario: Testing %s " , std : : get < 0 > ( query_test_info ) . c_str ( ) ) ;
// 1. Enable
if ( enable_query ( admin , query_test_info , true ) ) {
// 2. Test Enabled (performs 1 ok)
std : : get < 5 > ( query_test_info ) ( cl , mysql , 0 , true ) ;
}
// 3. Disable
if ( enable_query ( admin , query_test_info , false ) ) {
// 4. Test Disabled (performs 1 ok)
std : : get < 5 > ( query_test_info ) ( cl , mysql , std : : get < 4 > ( query_test_info ) , false ) ;
}
mysql_close ( mysql ) ;
}
// Create required connection to ProxySQL admin required to perform the
// tests for conditionally enabled queries.
MYSQL * proxysql_admin = mysql_init ( NULL ) ;
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 ;
}
// Enable and test the queries that can be conditionally enabled
for ( const auto & query_test_info : queries_tests_info ) {
MYSQL * proxysql_mysql = mysql_init ( NULL ) ;
// extract the tuple elements
const std : : string query = std : : get < 0 > ( query_test_info ) ;
const std : : string variable_name = std : : get < 1 > ( query_test_info ) ;
int exp_err = std : : get < 4 > ( query_test_info ) ;
const auto & testing_fn = std : : get < 5 > ( query_test_info ) ;
if ( ! mysql_real_connect ( proxysql_mysql , cl . host , cl . username , cl . password , NULL , cl . port , NULL , 0 ) ) {
fprintf ( stderr , " File %s, line %d, Error: %s \n " , __FILE__ , __LINE__ , mysql_error ( proxysql_mysql ) ) ;
return EXIT_FAILURE ;
}
mysql_close ( admin ) ;
bool query_enabling_succeed = enable_query ( proxysql_admin , query_test_info , true ) ;
ok (
query_enabling_succeed , " Enabling query '%s' should succeed. " ,
std : : get < 0 > ( query_test_info ) . c_str ( )
) ;
// Check that the query is now properly supported
testing_fn ( cl , proxysql_mysql , 0 , true ) ;
bool query_disabling_succeed = enable_query ( proxysql_admin , query_test_info , false ) ;
ok (
query_disabling_succeed , " Disabling query '%s' should succeed. " ,
std : : get < 0 > ( query_test_info ) . c_str ( )
) ;
// Check that the query is now failing
testing_fn ( cl , proxysql_mysql , exp_err , false ) ;
mysql_close ( proxysql_mysql ) ;
}
mysql_close ( proxysql_admin ) ;
return exit_status ( ) ;
}