Skip to content

fix(plugin-mysql): spell string literals for sessions with NO_BACKSLASH_ESCAPES - #3090

Merged
datlechin merged 1 commit into
mainfrom
fix/mysql-no-backslash-escapes
Sep 23, 2026
Merged

datlechin merged 1 commit into
mainfrom
fix/mysql-no-backslash-escapes

Conversation

@datlechin

@datlechin datlechin commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

Problem

The MySQL plugin quoted every string literal as if the session read backslash escapes. A session whose sql_mode holds NO_BACKSLASH_ESCAPES reads a backslash as an ordinary character. That mode can come from the server's global setting, from a connection's startup SQL, or from a SET run in the editor. In such a session every statement the plugin wrote came out wrong:

  • Structure edits corrupted what they restated. MODIFY COLUMN restates the whole column, so an edit to one attribute re-sent the default, the comment and the enum members. Every save doubled each backslash and turned each line break into the two characters \n, and did it again on the next save. On MariaDB an expression default holding a quote failed with ERROR 1064.
  • Create Table stored u\\v for a default of u\v.
  • A new user's password containing a backslash was stored with two, so the user could not log in.
  • Metadata reads could not find a table whose name holds a backslash. Status, row count, foreign keys, check constraints and the column catalog all missed it.

A second, separate defect: the plugin and the SQL export wrote a form feed as \f. MySQL and MariaDB have no such escape and read it as the letter f, in any mode.

Measured

MySQL 8.4.11 and MariaDB 13.0.2, both modes, every value checked by HEX():

default mode NO_BACKSLASH_ESCAPES
'x\\y' stores x\y x\\y
'a\nb' stores a, newline, b a\nb
raw newline inside '...' newline newline
'it''s' it's it's
'p\fq' pfq p\fq
COMMENT X'5C' ERROR 1064 ERROR 1064

No spelling of a backslash reads the same both ways, and COMMENT takes no hex literal. So the literal has to be spelled for the session.

The server prints SQL with backslash escapes whatever the session's mode: in SHOW CREATE TABLE, in a column's type (ENUM and SET members), in check clauses, in generation expressions, and in MariaDB's INFORMATION_SCHEMA defaults.

Fix

  • Every literal the plugin holds keeps the spelling the server prints, with backslash escapes. That is how the defaults, types and expressions it reads arrive, so nothing on the read side changes.
  • MySQLLiteralSpelling re-spells a statement for a session that has turned escapes off. It decodes each single-quoted literal the way the server reads it and writes it back with doubled quotes only. Identifiers, double-quoted text and comments are copied unchanged. libmariadb's mysql_real_escape_string makes the same choice from the same SERVER_STATUS_NO_BACKSLASH_ESCAPES flag.
  • The re-spelling happens once, where a statement leaves the plugin to run on the session:
    • the DDL it generates (Create Table, Add, Modify and Move Column, Add Check)
    • the account statements
    • the all-tables metadata query
    • its own catalog queries, through execute(ownStatement:)
  • A clipboard copy of a column definition keeps the default spelling, like SHOW CREATE TABLE.
  • A form feed is written raw in mysqlEscapeStringLiteral and in the export dialect's escaper. A raw form feed is valid in every dialect that escaper serves.

Before / after

A harness compiled the plugin's own sources against both servers in a NO_BACKSLASH_ESCAPES session. It re-ran an unchanged MODIFY for every column, as an edit to another attribute does, then compared HEX() of what was stored.

#3067 this branch
MySQL 8.4, 5 columns restated 4 changed: x\y to x\\y, newline to \n, enum a\b to a\\b, expression doubled every column unchanged
MariaDB 13, 5 columns restated 3 changed as above, expression default ERROR 1064 every column unchanged
table `we\ird`, status and row count not found found
Create Table, default u\v, comment w\x + newline u\\v, w\\x\ny stored as typed
CREATE USER with password p\w'd, then log in ERROR 1045 logs in
comment f + form feed + f, default mode stored fff stored as typed

Known limit

MySQL 8.4 re-parses its own stored text in the session's mode. On a table with an expression default that holds an escaped quote, such as concat('it''s', ...), the server itself fails SHOW CREATE TABLE and every ALTER TABLE with ERROR 1064 under NO_BACKSLASH_ESCAPES, from the stock mysql client too. The column read already falls back to the catalog for that table (#3067). Nothing client-side can make the ALTER run. MariaDB is not affected.

Tests

  • MySQLLiteralSpellingTests:
    • every spelling above
    • identifiers, double-quoted text and all three comment forms copied unchanged, and an unterminated literal left alone
    • \% and \_ keeping their backslash
    • a property check that re-spelling a written literal equals quoting the raw value for the session, over backslashes, quotes, control characters, %, _ and non-ASCII
  • SQLExportDialectTests.mysqlEscaperMatchesTheDriver pinned \f for a form feed. That was the defect, measured above, so it now expects the raw character.
  • MySQLLiteralSpellingTests ran locally in a scratch package against the plugin files: 11 tests, 24 cases, all pass.
  • The rest of TableProTests did not run locally. The shared build was stuck behind a stalled package download, so CI is its first run.
  • SwiftLint is clean on every changed file. The three violations in SQLExportDialectTests.swift are already on main.

Not changed

The app-side callers of escapeStringLiteral (SQL export, MCP, the foreign key row lookup) still use the default spelling. An export file has to stay portable, so it cannot follow the session that wrote it. The foreign key lookup runs on the session and would need its own spelling. Reported separately rather than folded in here.

@datlechin
datlechin force-pushed the fix/mysql-no-backslash-escapes branch from 765d484 to 4a30d97 Compare September 23, 2026 15:37
@datlechin
datlechin changed the base branch from fix/mysql-null-column-default to main September 23, 2026 15:37
@datlechin
datlechin merged commit 5b62cd6 into main Sep 23, 2026
3 checks passed
@datlechin
datlechin deleted the fix/mysql-no-backslash-escapes branch September 23, 2026 19:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant