docs/superpowers/plans/2026-08-14-issue-6010-admin-mysqldump-set.md
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Let MySQL 8 mysqldump --set-gtid-purged=OFF --column-statistics=0 complete an Admin backup by accepting its scoped client-timeout SET statement.
Architecture: Preserve Admin configuration updates on the existing SET-to-UPDATE global_variables path. Add a narrow parser ahead of that path: scope-qualified assignments whose names appear in mysql_variables.ignore_vars are successful Admin no-ops; the mysqldump two-timeout statement therefore returns an OK packet without persistent state. Strip an optional SESSION, LOCAL, or GLOBAL token before the legacy single-assignment validation so scope text is never incorporated into a variable name.
Tech Stack: C++17, ProxySQL Admin handler, MariaDB C client TAP test, Docker-backed TAP infrastructure.
--set-gtid-purged=OFF --column-statistics=0.@@GLOBAL.gtid_executed or information_schema.COLUMN_STATISTICS compatibility in this issue.global_variables rows.mysql84-g1, matching the affected MySQL 8 client path.lib/Admin_Handler.cpp — recognizes scope tokens and the existing MySQL ignored-variable set while handling Admin SET commands.test/tap/tests/reg_test_6010_admin_mysqldump_set-t.cpp — exercises the real Admin protocol behavior required by mysqldump.test/tap/groups/groups.json — schedules the regression test in the MySQL 8 TAP group.Files:
test/tap/tests/reg_test_6010_admin_mysqldump_set-t.cpptest/tap/groups/groups.json: near the other admin testsInterfaces:
Consumes: CommandLine, mysql_query_t, tap.h, and the Admin endpoint supplied by the TAP infrastructure.
Produces: reg_test_6010_admin_mysqldump_set-t, a standalone TAP binary with five assertions.
Step 1: Write the failing regression source
#include <cstdlib>
#include "mysql.h"
#include "tap.h"
#include "command_line.h"
#include "utils.h"
static long long count_timeout_rows(MYSQL* admin) {
const char* query =
"SELECT COUNT(*) FROM global_variables "
"WHERE variable_name IN ('net_read_timeout', 'net_write_timeout')";
if (mysql_query_t(admin, query) != 0) return -1;
MYSQL_RES* result = mysql_store_result(admin);
MYSQL_ROW row = result ? mysql_fetch_row(result) : nullptr;
const long long count = row ? atoll(row[0]) : -1;
if (result) mysql_free_result(result);
return count;
}
int main() {
CommandLine cl;
if (cl.getEnv()) return EXIT_FAILURE;
MYSQL* admin = init_mysql_conn(cl.host, cl.admin_port,
cl.admin_username, cl.admin_password, false, false);
if (!admin) return EXIT_FAILURE;
plan(5);
const long long before = count_timeout_rows(admin);
ok(before == 0, "timeout variables are not Admin global variables before SET");
const int dump_set_rc = mysql_query_t(admin,
"SET SESSION NET_READ_TIMEOUT=86400, SESSION NET_WRITE_TIMEOUT=86400");
ok(dump_set_rc == 0, "mysqldump timeout SET succeeds");
const long long after = count_timeout_rows(admin);
ok(after == before, "mysqldump timeout SET has no persistent Admin side effect");
const int single_set_rc = mysql_query_t(admin,
"SET SESSION net_read_timeout=86400");
ok(single_set_rc == 0, "a scoped ignored timeout SET succeeds alone");
const int unknown_set_rc = mysql_query_t(admin,
"SET SESSION issue_6010_unknown_variable=1");
ok(unknown_set_rc != 0, "unknown scoped SET remains an error");
mysql_close(admin);
return exit_status();
}
Add this scheduling entry in alphabetical position in groups.json:
"reg_test_6010_admin_mysqldump_set-t" : [ "mysql84-g1" ],
Run:
make -C test/tap/tests reg_test_6010_admin_mysqldump_set-t
export WORKSPACE=$PWD
export INFRA_ID="issue6010-before-$(date +%s)"
export TAP_GROUP="mysql84-g1"
export TEST_PY_TAP_INCL="reg_test_6010_admin_mysqldump_set-t"
export SKIP_CLUSTER_START=1
source test/infra/common/env.sh
./test/infra/control/ensure-infras.bash
./test/infra/control/run-tests-isolated.bash
./test/infra/control/stop-proxysql-isolated.bash
Expected: the second assertion fails because Admin reports Unknown global variable: 'SESSION NET_READ_TIMEOUT'.
git add test/tap/tests/reg_test_6010_admin_mysqldump_set-t.cpp test/tap/groups/groups.json
git commit -m "test: reproduce Admin mysqldump SET failure"
Files:
lib/Admin_Handler.cpp: include block and admin_handler_command_settest/tap/tests/reg_test_6010_admin_mysqldump_set-t.cppInterfaces:
Consumes: mysql_variables.ignore_vars declared by MySQL_Protocol.h; its members include net_read_timeout and net_write_timeout.
Produces: admin_handler_command_set() sends an Admin OK packet and returns false when every comma-separated assignment is a SESSION or LOCAL ignored MySQL variable; it otherwise follows the existing validation/update path.
Step 1: Add focused parser helpers above admin_handler_command_set
Include MySQL_Protocol.h, then add helpers with these responsibilities:
enum class admin_set_scope { implicit, session, local, global };
static admin_set_scope strip_admin_set_scope(char*& variable_name) {
// Skip leading spaces, compare SESSION/LOCAL/GLOBAL case-insensitively,
// move variable_name past a matched token and its following spaces, and
// return the matched scope. Return implicit when no token is present.
}
static bool is_ignored_mysql_variable(const char* variable_name) {
return std::any_of(mysql_variables.ignore_vars.cbegin(),
mysql_variables.ignore_vars.cend(), [variable_name](const std::string& ignored) {
return strcasecmp(ignored.c_str(), variable_name) == 0;
});
}
static bool is_ignored_scoped_set(char* assignments) {
// Split only on commas between assignments, split each assignment once at
// '=', trim its left side, strip its scope, and return true only when the
// input contains at least one assignment and every assignment has SESSION
// or LOCAL scope and names an ignored MySQL variable.
}
Use a local mutable copy of the assignment text in is_ignored_scoped_set;
the original query remains available for the legacy handler. The comma loop
must consume both timeout assignments, not stop after the first =.
Immediately after logging in admin_handler_command_set, classify the text
after SET :
if (is_ignored_scoped_set(query_no_space + sizeof("SET ") - 1)) {
pa->send_ok_msg_to_client(sess, NULL, 0, query_no_space);
return false;
}
For the existing one-assignment path, call
strip_admin_set_scope(var_name) after
trim_spaces_in_place(untrimmed_var_name) and before
is_sensitive_set_variable_name() or is_valid_global_variable(). This
lets SET GLOBAL mysql-...=... reach the current Admin variable validation
while leaving unscoped Admin behavior unchanged. The unknown-variable error
branch remains unchanged.
Run:
make -j2 build_src_debug
make -C test/tap/tests reg_test_6010_admin_mysqldump_set-t
Expected: both commands finish successfully; src/proxysql and the focused
TAP binary exist.
Run:
export WORKSPACE=$PWD
export INFRA_ID="issue6010-after-$(date +%s)"
export TAP_GROUP="mysql84-g1"
export TEST_PY_TAP_INCL="reg_test_6010_admin_mysqldump_set-t"
export SKIP_CLUSTER_START=1
source test/infra/common/env.sh
./test/infra/control/ensure-infras.bash
./test/infra/control/run-tests-isolated.bash
./test/infra/control/stop-proxysql-isolated.bash
Expected: all five TAP assertions pass. On a failure, inspect
ci_infra_logs/$INFRA_ID/tests/proxysql-tester.py/tests/reg_test_6010_admin_mysqldump_set-t.log.gz.
git add lib/Admin_Handler.cpp
git commit -m "fix: accept mysqldump session timeout SETs in Admin"
Files:
test/tap/tests/reg_test_6010_admin_mysqldump_set-t.cppInterfaces:
Consumes: the focused TAP test and the new Admin handler classification.
Produces: evidence that this branch implements only the flagged mysqldump compatibility path.
Step 1: Confirm excluded functionality has not been added
Run:
git diff origin/v3.0...HEAD -- lib/Admin_Handler.cpp test/tap/tests/reg_test_6010_admin_mysqldump_set-t.cpp test/tap/groups/groups.json
rg -n 'gtid_executed|COLUMN_STATISTICS' lib/Admin_Handler.cpp test/tap/tests/reg_test_6010_admin_mysqldump_set-t.cpp
Expected: the diff is limited to scoped ignored-SET handling and the
regression test. No new GTID or COLUMN_STATISTICS compatibility behavior is
present.
Run:
git diff --check origin/v3.0...HEAD
git status --short --branch
git log --oneline origin/v3.0..HEAD
Expected: no whitespace errors, a clean worktree, and the design, test, and
implementation commits on fix/6010-admin-mysqldump-set.
For a needed correction, stage the exact changed files and use this message:
git commit -m "test: finalize issue 6010 coverage"