Background
internal/sqlsafe currently hardcodes PostgreSQL + SQLite paths (classify.go / classify_sqlite.go / postgres.go). MySQL is the highest-frequency next engine, but adding it naively risks combinatorial explosion across classify → gate → explain → backup → execute.
Plan
- Refactor first: extract a
Dialect interface (classification quirks, identifier quoting, read-only session mechanism, EXPLAIN/row-estimate strategy, backup strategy, client invocation) with Postgres/SQLite as the two implementations. Zero behavior change, existing tests must stay green.
- Then MySQL: implement the dialect via remote
mysql client — fail-closed classification (block multi-statement, INTO OUTFILE, LOAD DATA, stored-procedure/DELIMITER forms, writable SET GLOBAL…), read-only via SET TRANSACTION READ ONLY, DML backup via CREATE TABLE … SELECT or CSV snapshot under explicit locking, credential discovery parity with the Docker/env-file flow, run-mode command-position block for mysql/mariadb clients redirecting to sshx sql.
Acceptance
Background
internal/sqlsafecurrently hardcodes PostgreSQL + SQLite paths (classify.go / classify_sqlite.go / postgres.go). MySQL is the highest-frequency next engine, but adding it naively risks combinatorial explosion across classify → gate → explain → backup → execute.Plan
Dialectinterface (classification quirks, identifier quoting, read-only session mechanism, EXPLAIN/row-estimate strategy, backup strategy, client invocation) with Postgres/SQLite as the two implementations. Zero behavior change, existing tests must stay green.mysqlclient — fail-closed classification (block multi-statement,INTO OUTFILE,LOAD DATA, stored-procedure/DELIMITERforms, writableSET GLOBAL…), read-only viaSET TRANSACTION READ ONLY, DML backup viaCREATE TABLE … SELECTor CSV snapshot under explicit locking, credential discovery parity with the Docker/env-file flow, run-mode command-position block formysql/mariadbclients redirecting tosshx sql.Acceptance