Repository navigation
fix: no connection character set reads a MySQL string differently - #34
Merged
christiangda merged 1 commit intoOct 24, 2026
Merged
Conversation
The review of the MySQL dialect found that its string reading held for utf8mb4 and latin1 connections and not for gbk, gb18030, sjis, cp932 or big5, where a backslash can be the last byte of a character that starts with any byte from 0x81: MySQL reads the two as one, and the backslash escapes nothing. name = '丁\' OR id = 2 #' was one comparison to the parser, and MySQL 8.4.13 ran id = 2 with SET NAMES gbk. A backslash right after a character outside ASCII is refused, with why. 6,000 accepted filters run under all seven character sets: none read differently (before the rule, 115 under gbk and 49 under sjis). And what MySQL refuses of an ESCAPE string is refused: \% and \_ are two characters outside a pattern, and a byte that is not UTF-8 is none (the scanner reads it as U+FFFD; the input says which it was). A string's Value takes one quote off each end, where Trim took every one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
christiangda
deleted the
fix/a-multibyte-character-does-not-take-the-backslash
branch
October 24, 2026 18:43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An independent review of the MySQL dialect (#32) ran the string-reading claim against MySQL 8.4.13, before
v1.2.0was tagged. The tag is held for this PR.What it found
1. Security: the claim held for
utf8mb4andlatin1connections, and not forgbk,gb18030,sjis,cp932orbig5. In those character sets a backslash (0x5C) can be the last byte of a character that starts with any byte from 0x81. The server reads the two as one character, and the backslash escapes nothing.["name"]SET NAMES utf8mb4SET NAMES gbkname = '丁\' OR id = 2 #'id = 2ranThe same happens with an invalid byte before the backslash, and under
gb18030,sjisandcp932.2.
ESCAPE '\%'andESCAPE '\_': qfv counted each as one character. MySQL keeps them as two outside a pattern, and answers "Incorrect arguments to ESCAPE". The same goes for an escape byte that is not UTF-8.3. Minor:
LiteralNode.Valueusedstrings.Trim, so a string ending on an escaped quote lost it.What changes
illegal string: a backslash right after a character outside ASCII, … put a space or another ASCII character between them. In UTF-8, every non-ASCII character ends in a byte from 0x80, and so does every invalid byte. A quote (0x27) is never part of a character in any of these sets, so nothing else can be read differently.ESCAPE:\%and\_count as two characters, as MySQL counts them;ErrorCountcannot tell, because it decodes one character ahead.Valuetakes one quote off each end.dialects.mdhas a section on the connection character set and the new refusals (held toParseby the docs test). The "never looser" line now says where the database's answer depends on the connection.migration.mdand theDialectMySQLdoc comment are updated too.How it was checked
Differential, against the server: 6,000 filters the dialect accepts, built from quotes, backslashes, comments, and multi-byte and invalid bytes, run through MySQL 8.4.13 under seven connection character sets.
utf8mb4,latin1,gb18030,cp932,big5gbksjisWhat remains under the multi-byte sets is "Illegal mix of collations", and, under
latin1andsjis, an escape likeébeing two characters: MySQL refusing, after parsing, depending on the connection. Underutf8mb4, nothing remains.Tests:
TestNoConnectionCharacterSetReadsAStringDifferently: the review's inputs refused, an ASCII character between allowed, PostgreSQL unaffected;TestTheMySQLEscapeCharacterIsOneCharacterToMySQL;TestAStringsValueKeepsItsLastQuote.Mutants: 7 of 7 caught, each compiling.
\%counted as one.Valuetrimming every quote.go test -racepasses, coverage is 98.3%, andgolangci-lintreports 0 issues. All 21 diagrams render after GitHub-style entity decoding.After this merges,
v1.2.0is tagged.🤖 Generated with Claude Code