fix(parser): support unquoted non-ASCII identifiers - #27010
fix(parser): support unquoted non-ASCII identifiers#27010iamlinjunhong wants to merge 3 commits into
Conversation
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
e7e31e8 to
065efd8
Compare
065efd8 to
65f88bc
Compare
XuPeng-SH
left a comment
There was a problem hiding this comment.
[P1] Preserve raw client bytes when a digit-leading token is promoted to an identifier.
This change explicitly supports both raw high-byte client identifiers (for example t_\xe9g) and digit-leading extended identifiers, but their combination takes a different path. In pkg/sql/parsers/dialect/mysql/scanner.go:813-820, scanNumber promotes the token to ID and then returns strings.ToLower(s.buf[start:s.Pos]). Go's Unicode lowercasing replaces an invalid UTF-8 byte such as the latin1 0xe9 with U+FFFD.
I reproduced this through the public parser at head 65f88bcf1d0388e299fd2ac85d8bf6438697fd13:
stmt, _ := ParseOne(ctx, "CREATE TABLE 1\xe9 (a INT)", 1)
tree.String(stmt, dialect.MYSQL)
// actual: "create table 1� (a int)"
// expected: "create table 1\xe9 (a int)"This silently addresses a different catalog object and can collapse distinct byte identifiers. Please keep the identifier bytes intact (or use byte-preserving ASCII-only folding if normalization is required), and add both scanner-level and ParseOne regressions for the pairwise case.
There is a second compatibility boundary to close: isUnquotedIdentifierLetter accepts every byte >= 0x80, so a valid UTF-8 supplementary character such as U+1F600 is accepted as an unquoted ID. MySQL explicitly limits identifiers to the BMP and prohibits U+10000 and above in quoted and unquoted identifiers: https://dev.mysql.com/doc/refman/8.0/en/identifiers.html. I confirmed CREATE TABLE 😀 (a INT) parses successfully on this head. The validation needs to be charset-aware (or otherwise distinguish the supported raw-byte contract) so the fix does not broaden the grammar beyond the compatibility contract.
aptend
left a comment
There was a problem hiding this comment.
Deep-reviewed exact head 65f88bcf1d0388e299fd2ac85d8bf6438697fd13, including the existing same-head feedback. Two compatibility blockers remain:
-
[P1] Preserve raw client bytes when a digit-leading token is promoted to
ID.scanNumbernow accepts a following extended identifier byte, but still returnsstrings.ToLower(s.buf[start:s.Pos]). Go Unicode lowercasing rewrites invalid UTF-8 bytes. I reproducedCREATE TABLE 1\xe9 (a INT)throughParseOne: deparse returnscreate table 1� (a int)rather than preserving byte0xe9, so the parsed catalog name changes. Use byte-preserving ASCII-only folding (or return the original identifier bytes) and cover this combined digit-leading/raw-byte path at scanner and parser levels. -
[P1] Do not accept supplementary Unicode code points as MySQL identifiers.
isUnquotedIdentifierLetteraccepts every byte >= 0x80 independently, so UTF-8 for U+10000 and above is accepted as an unquoted identifier. I independently confirmedCREATE TABLE 😀 (a INT)parses successfully. MySQL restricts identifiers to the BMP; the validation boundary must distinguish supported raw client bytes from valid UTF-8 supplementary sequences instead of broadening the grammar to all high-byte sequences.
The two focused counterexamples fail on this exact head as described.
|
当前 exact head
本次在 exact head 重新运行确定性回归,exit 0: 当前 exact-head check rollup 无 failure,Coverage 为 success;PR |
aptend
left a comment
There was a problem hiding this comment.
Deep re-review completed on exact head 2f9b4f8. The previously reported raw-byte, supplementary-code-point, and direct scanIdentifier entry issues are closed. One new correctness blocker remains:
- [P1] Preserve identifiers whose prefix is
_utf8mb4.Scanner.Scantreats every leading_utf8mb4as a charset introducer without checking that the prefix ends at an identifier boundary. With the newly supported BMP suffix,CREATE TABLE _utf8mb4数量 (a INT)parses successfully but deparses ascreate table (a int): the table name is silently erased. At scanner level,_utf8mb4数量returns an ID with an empty value instead of the original identifier. This can address or attempt to create the wrong catalog object. Only enter the charset-introducer path when_utf8mb4is followed by the syntax required for an introducer (or at least not by another identifier character); otherwise scan the complete identifier. Add scanner and public ParseOne regressions for BMP and raw-client-byte suffixes, plus a valid_utf8mb4'...'introducer control.
|
已按 aptend 在 exact head 根因是旧 回归覆盖:
验证通过(均为最终语义修改后):
新 CI run |
|
当前 exact head 本次在当前 head fresh 执行确定性 CGo 定向回归:
结果 exit 0;scanner 与 public ParseOne 覆盖并通过:扩展标识符与 |
68ec2d9 to
fb5cfe6
Compare
|
已将分支从精确旧 head 68ec2d9 rebase 到当前 canonical main 0fc87ec,并用精确 lease 推送;新 head 为 fb5cfe6,无冲突。 失败的 UT run 31711715051 / job 94486146456(attempt 1)完整日志显示失败命令为 make ut UT_PARALLEL=6 UT_TIMEOUT=40,唯一失败是 pkg/tests/features.TestTableFeatures;稳定错误签名是 HAKeeper 连接 ID 分配期间 connection reset by peer / broken pipe / context deadline exceeded。该 run 执行期间合入的 #27119(fix(logservice): prevent connection ID refill convoys)直接修复了同一调用链和 2 秒超时/共享 transport reset 问题,本次 rebase 已包含该修复。未 rerun 旧 head 的 run,push 已触发新 head CI。 本地验证(rebase 后):
现有 CHANGES_REQUESTED 均基于旧 head;raw client bytes、digit-leading/raw-byte 组合、BMP/补充平面边界及 charset introducer 反馈已包含回归覆盖。请基于 fb5cfe6 重新 review。 |
|
@XuPeng-SH @aptend 重新核对当前 exact head 旧反馈在当前实现中的闭环证据:
该 exact head 在 rebase 后已 fresh 通过 parser tree/mysql 的 list、build、vet、完整 deterministic tests,以及 |
What type of PR is this?
Which issue(s) this PR fixes:
issue #26715
What this PR does / why we need it:
fix(parser): support unquoted non-ASCII identifiers