mirror of https://github.com/sysown/proxysql
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
5.7 KiB
139 lines
5.7 KiB
#include <string>
|
|
#include <stdio.h>
|
|
#include <cstring>
|
|
#include <unistd.h>
|
|
#include <vector>
|
|
#include "mysql.h"
|
|
#include "tap.h"
|
|
#include "command_line.h"
|
|
#include "utils.h"
|
|
|
|
int main(int argc, char** argv) {
|
|
CommandLine cl;
|
|
if (cl.getEnv()) {
|
|
diag("Failed to get the required environmental variables.");
|
|
return -1;
|
|
}
|
|
|
|
plan(15);
|
|
|
|
diag("=== FFTO Bypass Test ===");
|
|
diag("This test validates that queries larger than the FFTO max buffer size");
|
|
diag("bypass the FFTO (Fast Forward To Optimization) mechanism entirely.");
|
|
diag("========================");
|
|
|
|
MYSQL* admin = mysql_init(NULL);
|
|
if (!mysql_real_connect(admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
|
|
diag("Admin connection failed");
|
|
return -1;
|
|
}
|
|
ok(admin != NULL, "Connected to ProxySQL Admin");
|
|
|
|
// Set a very small threshold: 100 bytes
|
|
ok(mysql_query(admin, "UPDATE global_variables SET variable_value='true' WHERE variable_name='mysql-ffto_enabled'") == 0, "Enable FFTO");
|
|
ok(mysql_query(admin, "UPDATE global_variables SET variable_value='100' WHERE variable_name='mysql-ffto_max_buffer_size'") == 0, "Set FFTO max buffer size to 100");
|
|
ok(mysql_query(admin, "LOAD MYSQL VARIABLES TO RUNTIME") == 0, "Load variables to runtime");
|
|
|
|
// Ensure user exists
|
|
char user_query[1024];
|
|
snprintf(user_query, sizeof(user_query), "INSERT OR REPLACE INTO mysql_users (username, password, default_hostgroup, fast_forward, default_schema) VALUES ('%s', '%s', 0, 1, 'information_schema')", cl.username, cl.password);
|
|
ok(mysql_query(admin, user_query) == 0, "Configure user with fast_forward=1 and default_schema");
|
|
ok(mysql_query(admin, "LOAD MYSQL USERS TO RUNTIME") == 0, "Load users to runtime");
|
|
|
|
// Ensure backend server exists
|
|
char server_query[1024];
|
|
snprintf(server_query, sizeof(server_query), "INSERT OR REPLACE INTO mysql_servers (hostgroup_id, hostname, port) VALUES (0, '%s', %d)", cl.mysql_host, cl.mysql_port);
|
|
mysql_query(admin, server_query);
|
|
mysql_query(admin, "LOAD MYSQL SERVERS TO RUNTIME");
|
|
|
|
ok(mysql_query(admin, "TRUNCATE TABLE stats_mysql_query_digest") == 0, "Clear stats_mysql_query_digest using TRUNCATE");
|
|
|
|
MYSQL* conn = mysql_init(NULL);
|
|
if (!mysql_real_connect(conn, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
|
|
diag("Client connection failed: %s", mysql_error(conn));
|
|
return -1;
|
|
}
|
|
ok(conn != NULL, "Connected to ProxySQL Data Port");
|
|
|
|
ok(mysql_query(conn, "USE information_schema") == 0, "Execute USE information_schema");
|
|
|
|
// 1. Send a SMALL query (should be recorded)
|
|
const char* small_query = "SELECT 1111";
|
|
ok(mysql_query(conn, small_query) == 0, "Send small query (should be recorded)");
|
|
{
|
|
MYSQL_RES* query_res = mysql_store_result(conn);
|
|
if (query_res) mysql_free_result(query_res);
|
|
}
|
|
|
|
// Verify small query recorded - wait up to 2 seconds
|
|
int count = 0;
|
|
for (int i=0; i<20; i++) {
|
|
mysql_query(admin, "SELECT count(*) FROM stats_mysql_query_digest WHERE digest_text = 'SELECT ?'");
|
|
MYSQL_RES* res = mysql_store_result(admin);
|
|
MYSQL_ROW row = mysql_fetch_row(res);
|
|
count = row ? atoi(row[0]) : -1;
|
|
if (res) mysql_free_result(res);
|
|
if (count > 0) break;
|
|
usleep(100000); // 100ms
|
|
}
|
|
if (count == 0) {
|
|
diag("Small query NOT found. Dumping ALL digests in stats_mysql_query_digest:");
|
|
mysql_query(admin, "SELECT digest_text, count_star FROM stats_mysql_query_digest");
|
|
MYSQL_RES* dr = mysql_store_result(admin);
|
|
MYSQL_ROW drw;
|
|
while(dr && (drw = mysql_fetch_row(dr))) {
|
|
diag(" Digest: %s, Count: %s", drw[0], drw[1]);
|
|
}
|
|
if (dr) mysql_free_result(dr);
|
|
}
|
|
ok(count > 0, "Small query was recorded by FFTO (count: %d)", count);
|
|
|
|
// 2. Clear and verify it is empty
|
|
ok(mysql_query(admin, "TRUNCATE TABLE stats_mysql_query_digest") == 0, "Clear stats_mysql_query_digest again using TRUNCATE");
|
|
|
|
mysql_query(admin, "SELECT count(*) FROM stats_mysql_query_digest");
|
|
MYSQL_RES* res = mysql_store_result(admin);
|
|
MYSQL_ROW row = mysql_fetch_row(res);
|
|
count = row ? atoi(row[0]) : -1;
|
|
ok(count == 0, "Verified stats_mysql_query_digest is empty after TRUNCATE (count: %d)", count);
|
|
if (res) mysql_free_result(res);
|
|
|
|
// 3. Send LARGE query (should NOT be recorded)
|
|
std::string large_query = "SELECT '";
|
|
for(int i=0; i<200; i++) large_query += "x";
|
|
large_query += "', 2222";
|
|
|
|
ok(mysql_query(conn, large_query.c_str()) == 0, "Send large query (exceeding threshold, should NOT be recorded)");
|
|
{
|
|
MYSQL_RES* query_res = mysql_store_result(conn);
|
|
if (query_res) mysql_free_result(query_res);
|
|
}
|
|
|
|
// Wait a bit to ensure it would have appeared if it was recorded
|
|
usleep(500000);
|
|
|
|
// Verify that NO digest was recorded for the large query
|
|
mysql_query(admin, "SELECT count(*), digest_text FROM stats_mysql_query_digest");
|
|
res = mysql_store_result(admin);
|
|
if (!res) {
|
|
ok(0, "Failed to store result from stats query");
|
|
} else {
|
|
row = mysql_fetch_row(res);
|
|
count = row ? atoi(row[0]) : -1;
|
|
if (count != 0) {
|
|
diag("Unexpected digests found in stats_mysql_query_digest:");
|
|
mysql_data_seek(res, 0);
|
|
while((row = mysql_fetch_row(res))) {
|
|
diag(" Count: %s, Digest: %s", row[0], row[1]);
|
|
}
|
|
}
|
|
ok(count == 0, "No digests recorded for queries exceeding threshold (count: %d)", count);
|
|
}
|
|
|
|
if (res) mysql_free_result(res);
|
|
mysql_close(conn);
|
|
mysql_close(admin);
|
|
|
|
return exit_status();
|
|
}
|