|
|
|
|
@ -1,6 +1,76 @@
|
|
|
|
|
/**
|
|
|
|
|
* @file mysql-select_version_without_backend-t.cpp
|
|
|
|
|
* @brief This TAP test validates if SELECT VERSION() works without making calls to the backend.
|
|
|
|
|
* @brief TAP test for validating SELECT VERSION() behavior with mysql-select_version_forwarding
|
|
|
|
|
*
|
|
|
|
|
* ## Overview
|
|
|
|
|
*
|
|
|
|
|
* This test validates the behavior of the `mysql-select_version_forwarding` variable
|
|
|
|
|
* when ProxySQL has NO backend servers configured. This scenario tests how ProxySQL
|
|
|
|
|
* handles SELECT VERSION() and SELECT @@VERSION queries when there are no available
|
|
|
|
|
* backend connections to peek at.
|
|
|
|
|
*
|
|
|
|
|
* ## Background
|
|
|
|
|
*
|
|
|
|
|
* Since ProxySQL 3.0.4, SELECT VERSION() queries are intercepted by ProxySQL.
|
|
|
|
|
* The `mysql-select_version_forwarding` variable controls this behavior with 4 modes:
|
|
|
|
|
*
|
|
|
|
|
* - Mode 0 (NEVER): Always return ProxySQL's own mysql-server_version
|
|
|
|
|
* - Mode 1 (ALWAYS): Always proxy the query to a backend server
|
|
|
|
|
* - Mode 2 (SMART_FALLBACK_INTERNAL): Try to get version from backend connection,
|
|
|
|
|
* fallback to ProxySQL's mysql-server_version if no connection available
|
|
|
|
|
* - Mode 3 (SMART_FALLBACK_PROXY, default): Try to get version from backend connection,
|
|
|
|
|
* fallback to proxying the query if no connection available
|
|
|
|
|
*
|
|
|
|
|
* ## Test Scenarios
|
|
|
|
|
*
|
|
|
|
|
* This test runs TWO scenarios with NO backend servers configured:
|
|
|
|
|
*
|
|
|
|
|
* ### Scenario 1: Mode 3 (smart with fallback to proxying) - EXPECTED TO FAIL
|
|
|
|
|
*
|
|
|
|
|
* When `mysql-select_version_forwarding=3` and there are NO backend servers:
|
|
|
|
|
* - ProxySQL tries to peek at backend connections to get the version
|
|
|
|
|
* - No connections exist (no backends configured)
|
|
|
|
|
* - Fallback behavior: ProxySQL attempts to proxy the query to a backend
|
|
|
|
|
* - Result: Query FAILS because there is no backend to proxy to
|
|
|
|
|
*
|
|
|
|
|
* This test explicitly verifies that mode 3 behaves correctly when there are no
|
|
|
|
|
* backends - it should attempt to proxy and fail, rather than returning an internal
|
|
|
|
|
* version incorrectly.
|
|
|
|
|
*
|
|
|
|
|
* ### Scenario 2: Mode 2 (smart with fallback to internal) - EXPECTED TO SUCCEED
|
|
|
|
|
*
|
|
|
|
|
* When `mysql-select_version_forwarding=2` and there are NO backend servers:
|
|
|
|
|
* - ProxySQL tries to peek at backend connections to get the version
|
|
|
|
|
* - No connections exist (no backends configured)
|
|
|
|
|
* - Fallback behavior: Return ProxySQL's mysql-server_version (internal version)
|
|
|
|
|
* - Result: Query SUCCEEDS and returns the configured mysql-server_version
|
|
|
|
|
*
|
|
|
|
|
* This test verifies that mode 2 provides a safe fallback that allows queries
|
|
|
|
|
* to succeed even when no backends are available.
|
|
|
|
|
*
|
|
|
|
|
* ## Test Execution Flow
|
|
|
|
|
*
|
|
|
|
|
* For each scenario:
|
|
|
|
|
* 1. Set `mysql-select_version_forwarding` to the desired mode (3 or 2)
|
|
|
|
|
* 2. Set `mysql-server_version` to a known test value (e.g., "8.4.6")
|
|
|
|
|
* 3. Delete all backend servers (ensure no backends exist)
|
|
|
|
|
* 4. Execute two queries:
|
|
|
|
|
* - "SELECT @@VERSION" - Returns ProxySQL's mysql-server_version
|
|
|
|
|
* - "SELECT VERSION()" - Behavior depends on mode
|
|
|
|
|
* 5. Verify the results match the expected behavior for the mode
|
|
|
|
|
*
|
|
|
|
|
* ## Why Both Scenarios Matter
|
|
|
|
|
*
|
|
|
|
|
* - Mode 3 is the DEFAULT and provides the most accurate version information
|
|
|
|
|
* by ensuring clients get the real backend version or nothing. This is
|
|
|
|
|
* important for clients like SQLAlchemy that need to detect MariaDB vs MySQL.
|
|
|
|
|
*
|
|
|
|
|
* - Mode 2 provides a safer fallback that maintains availability at the cost
|
|
|
|
|
* of potentially returning less accurate version information when no backends
|
|
|
|
|
* are available.
|
|
|
|
|
*
|
|
|
|
|
* Testing both ensures the feature works correctly in all configurations and
|
|
|
|
|
* prevents regressions in future versions.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
@ -37,32 +107,30 @@ int run_q(MYSQL *mysql, const char *q) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
|
plan(2);
|
|
|
|
|
|
|
|
|
|
CommandLine cl;
|
|
|
|
|
if (cl.getEnv()) {
|
|
|
|
|
diag("Failed to get the required environmental variables.");
|
|
|
|
|
return exit_status();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MYSQL* admin = init_mysql_conn(cl.host, cl.admin_username, cl.admin_password, cl.admin_port);
|
|
|
|
|
if (!admin) {
|
|
|
|
|
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin));
|
|
|
|
|
return exit_status();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MYSQL_QUERY_T(admin, MYSQL_SET_SERVER_VERSION_QUERY);
|
|
|
|
|
/**
|
|
|
|
|
* @brief Test SELECT VERSION() behavior with a specific mode
|
|
|
|
|
*
|
|
|
|
|
* @param admin Admin connection for configuration
|
|
|
|
|
* @param proxy Proxy connection for queries
|
|
|
|
|
* @param mode The mysql-select_version_forwarding mode to test
|
|
|
|
|
* @param expect_success Whether queries are expected to succeed
|
|
|
|
|
* @return int 0 on success, EXIT_FAILURE on error
|
|
|
|
|
*/
|
|
|
|
|
int test_mode(MYSQL* admin, MYSQL* proxy, int mode, bool expect_success) {
|
|
|
|
|
// Set the mode explicitly
|
|
|
|
|
char set_mode_query[128];
|
|
|
|
|
snprintf(set_mode_query, sizeof(set_mode_query),
|
|
|
|
|
"SET mysql-select_version_forwarding=%d", mode);
|
|
|
|
|
MYSQL_QUERY_T(admin, set_mode_query);
|
|
|
|
|
MYSQL_QUERY_T(admin, "LOAD MYSQL VARIABLES TO RUNTIME");
|
|
|
|
|
|
|
|
|
|
// Ensure no backends exist
|
|
|
|
|
MYSQL_QUERY_T(admin, "DELETE FROM mysql_servers");
|
|
|
|
|
MYSQL_QUERY_T(admin, "LOAD MYSQL SERVERS TO RUNTIME");
|
|
|
|
|
|
|
|
|
|
MYSQL* proxy = init_mysql_conn(cl.host, cl.username, cl.password, cl.port);
|
|
|
|
|
if (!proxy) {
|
|
|
|
|
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy));
|
|
|
|
|
return exit_status();
|
|
|
|
|
}
|
|
|
|
|
diag("=== Testing mode %d (expect %s) ===",
|
|
|
|
|
mode,
|
|
|
|
|
expect_success ? "SUCCESS" : "FAILURE");
|
|
|
|
|
|
|
|
|
|
const char *version_get_queries[2] = {
|
|
|
|
|
"SELECT @@VERSION",
|
|
|
|
|
@ -73,8 +141,11 @@ int main(int argc, char** argv) {
|
|
|
|
|
MYSQL_ROW row = nullptr;
|
|
|
|
|
string res_server_version;
|
|
|
|
|
|
|
|
|
|
// Attempt to run the query
|
|
|
|
|
int rc = run_q(proxy, version_get_queries[i]);
|
|
|
|
|
|
|
|
|
|
if (rc == 0) {
|
|
|
|
|
// Query succeeded - fetch result
|
|
|
|
|
MYSQL_RES* proxy_res = mysql_store_result(proxy);
|
|
|
|
|
|
|
|
|
|
row = mysql_fetch_row(proxy_res);
|
|
|
|
|
@ -85,9 +156,65 @@ int main(int argc, char** argv) {
|
|
|
|
|
mysql_free_result(proxy_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ok(row && (res_server_version == MYSQL_TEST_SERVER_VERSION), "Server version: %s", res_server_version.c_str());
|
|
|
|
|
// Verify result matches expected behavior
|
|
|
|
|
if (expect_success) {
|
|
|
|
|
// Mode 2: Query should succeed and return ProxySQL's version
|
|
|
|
|
bool test_passed = row && (res_server_version == MYSQL_TEST_SERVER_VERSION);
|
|
|
|
|
ok(test_passed,
|
|
|
|
|
"Mode %d: %s should return '%s' - got '%s'",
|
|
|
|
|
mode,
|
|
|
|
|
version_get_queries[i],
|
|
|
|
|
MYSQL_TEST_SERVER_VERSION,
|
|
|
|
|
res_server_version.c_str());
|
|
|
|
|
} else {
|
|
|
|
|
// Mode 3: Query should FAIL (no backend to proxy to)
|
|
|
|
|
bool test_passed = (row == nullptr);
|
|
|
|
|
ok(test_passed,
|
|
|
|
|
"Mode %d: %s should FAIL (no backend) - %s",
|
|
|
|
|
mode,
|
|
|
|
|
version_get_queries[i],
|
|
|
|
|
row ? "UNEXPECTED SUCCESS" : "correctly failed");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
|
// We have 2 queries × 2 modes = 4 tests total
|
|
|
|
|
plan(4);
|
|
|
|
|
|
|
|
|
|
CommandLine cl;
|
|
|
|
|
if (cl.getEnv()) {
|
|
|
|
|
diag("Failed to get the required environmental variables.");
|
|
|
|
|
return exit_status();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MYSQL* admin = init_mysql_conn(cl.host, cl.admin_username, cl.admin_password, cl.admin_port);
|
|
|
|
|
if (!admin) {
|
|
|
|
|
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin));
|
|
|
|
|
return exit_status();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the ProxySQL version that will be returned for internal fallback
|
|
|
|
|
MYSQL_QUERY_T(admin, MYSQL_SET_SERVER_VERSION_QUERY);
|
|
|
|
|
MYSQL_QUERY_T(admin, "LOAD MYSQL VARIABLES TO RUNTIME");
|
|
|
|
|
|
|
|
|
|
MYSQL* proxy = init_mysql_conn(cl.host, cl.username, cl.password, cl.port);
|
|
|
|
|
if (!proxy) {
|
|
|
|
|
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy));
|
|
|
|
|
mysql_close(admin);
|
|
|
|
|
return exit_status();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scenario 1: Mode 3 (smart with fallback to proxying) - EXPECTED TO FAIL
|
|
|
|
|
// With no backends, mode 3 will try to proxy the query and fail
|
|
|
|
|
test_mode(admin, proxy, 3, false);
|
|
|
|
|
|
|
|
|
|
// Scenario 2: Mode 2 (smart with fallback to internal) - EXPECTED TO SUCCEED
|
|
|
|
|
// With no backends, mode 2 will fall back to returning mysql-server_version
|
|
|
|
|
test_mode(admin, proxy, 2, true);
|
|
|
|
|
|
|
|
|
|
mysql_close(admin);
|
|
|
|
|
mysql_close(proxy);
|
|
|
|
|
|
|
|
|
|
|