Repository navigation
Conversation
This was referenced Oct 14, 2026
With Dialect.POSTGRESQL, U&'\0041BC \00e9' was read as the column U, the & operator and a string, and U&'d!0061t!+000061' UESCAPE '!' failed, for example in COMMENT ON ... IS, column defaults and expressions. Lex U&'...' with an optional UESCAPE 'c' clause as one string literal under the PostgreSQL dialect. StringValue records it with the new POSTGRESQL_UNICODE_ESCAPE escaping and its escape character, and getNotExcapedValue() resolves \XXXX and \+XXXXXX escapes, surrogate pairs and doubled escape characters in linear time. The parser rejects the escapes and escape characters that PostgreSQL rejects. Rendering keeps the literal and its UESCAPE clause. Other dialects and the default configuration keep reading U&'x' as before. Signed-off-by: minleejae <mmj9808@gmail.com>
minleejae
force-pushed
the
feat/postgresql-unicode-strings-20261014
branch
from
October 14, 2026 09:56
8c8e88f to
99dd9e9
Compare
This branch has not been deployed
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.
Purpose
Parse PostgreSQL string constants with Unicode escapes (
U&'...', optionally followed byUESCAPE 'c') withDialect.POSTGRESQL. Today:SELECT U&'\0041BC \00e9'silently becomesU & '\0041BC \00e9': the columnU, the&operator and a string. PostgreSQL reads the stringABC é.COMMENT ON TABLE t IS U&'\0041BC \00e9'fails because the comment must be a literal. Column defaults such asDEFAULT U&'\0041'get the same bitwise reading.U&'d!0061t!+000061' UESCAPE '!'fails in every position.Current design (As-is)
S_CHAR_LITERALknows the prefixesU,E,N,R,B,RBand_utf8, but notU&.U&'x'is lexed as the identifierU,&and'x'.StringValue.Escaping(Resolve backslash escapes in StringValue.getNotExcapedValue() #2791) covers standard strings, MySQL backslash escapes and PostgreSQLE''strings.UESCAPEis not a token.Target design (To-be)
S_CHAR_LITERALgets aU&'...'alternative. It reads doubled quotes, and backslashes do not escape quotes, as in PostgreSQL.UESCAPE 'c'clause after spaces, tabs or newlines belongs to the same token. Every place that takes a string literal therefore accepts the clause, and no keyword is added.Dialect.POSTGRESQL. Otherwise the token action shortens it to the identifierU, soU&'x'keeps its current reading.U & 'x',U & 1anda & bare unchanged in every dialect.+, a quote, whitespace or not ASCII. The input is rewound before the error, so lookahead helpers that catch lexer errors cannot skip the literal.StringValueEscaping.POSTGRESQL_UNICODE_ESCAPE, andgetUnicodeEscapeCharacter()/setUnicodeEscapeCharacter()/withUnicodeEscapeCharacter(). The value is null when there is noUESCAPEclause.U&'body' [UESCAPE 'c']. The prefix isU&andgetValue()is the body as written.getNotExcapedValue()resolves\XXXX,\+XXXXXX, surrogate pairs written as two escapes, doubled escape characters and doubled quotes. It runs in linear time. For a constructed value that PostgreSQL would reject, it throwsIllegalArgumentException.toString()andExpressionDeParserrenderUESCAPE 'c'after the literal.U&positions and literal handling are unchanged:U&literal is concatenated as before and keeps the Unicode escaping, as in PostgreSQL.UESCAPEafter a comment or after a continuation line is not supported.U&"..."quoted identifiers are not covered.usage.rstgets a "PostgreSQL Unicode escape strings" section next to "String literal values".Validation
U&'\0041BC \00e9'→ABC é,U&'d!0061t!+000061' UESCAPE '!'→data,U&'\+01F600'andU&'\D83D\DE00'→ 😀,U&'a\\b'→a\b,U&'it''s'→it's;COMMENT ON,CREATE TABLE ... DEFAULT,ALTER COLUMN ... SET DEFAULT,INSERT ... VALUES, andWHERE/LIKEexpressions;COMMENT ON;U & 'x'with PostgreSQL. Strings starting with&are unchanged;ExpressionDeParseroutput, and linear-time decoding of 1,000,000 escapes.spotlessApply check: 10,361 tests, 10,336 passed, 25 skipped, zero failures/errors. The grammar choice conflict check passes.master(bdf8ac31) on the DDL/DCL survey corpora (MySQL 8.4 and PostgreSQL 18, with and without a dialect), every result is unchanged. The PostgreSQL comment corpora have 3U&comments, which used to fail. They now parse, with valuesABC é,ABC éanddata.toString()on a second, identical server, in the same order. All 205 MySQL and 327 PostgreSQL statements give the same result (success, or the same error).SELECTon 39U&literals covering the escape forms,UESCAPE, surrogate pairs and invalid literals. For the 22 the server accepts,getNotExcapedValue()is byte-identical to the value the server returns. The parser rejects the other 17, which the server also rejects.COMMENT ON TABLE … IS U&'…', which used to fail. Afterwards, every stored comment (MySQL table, column, index, partition, routine and event comments; PostgreSQLpg_description) is byte-identical on both servers.Integration validation
Applied this PR together with the other comment PRs (#2821, #2822, #2824, #2823), the DDL/DCL PRs #2806–#2819 and #2778 on
master(bdf8ac31), with the stacked PRs on top of their bases (#2814 → #2821, #2816 → #2822, #2806 → #2824 and #2811 → #2812 → #2823).master. Between the branches, conflicts are limited to adjacent additions (README/index rows, usage sections, imports, test methods, visitor and feature lists, an enum line and an alias guard).spotlessApply check: 11,877 tests, 11,852 passed, 25 skipped, zero failures/errors. The grammar choice conflict check passes.master: 206) and 363 of 364 PostgreSQL statements (master: 328), the same as Share MySQL account names across CREATE USER and DEFINER #2806–Render single spaces in ALTER visibility, FORCE and row level security actions #2819 together. There are no regressions.master: 67) and for 118 of 119 PostgreSQL statements (master: 76). The exception is aCREATE AGGREGATEsetup statement. Every table, column, index, partition, routine, event, user andCOMMENT ONcomment literal is aStringValue, exceptCOMMENT = _utf8mb4'x', which MySQL rejects.