fix(editability): allow digit-leading unquoted table names

This commit is contained in:
zipg authored and GitHub committed 2026-09-22 18:00:59 +08:00
1 parent 6a417b9c90
commit 25a36deec9
5 files changed
+179 -11

No files matched your search

+22 -1
View File
@@ -748,7 +748,28 @@ function readIdentifier(text: string, start: number): { value: string; quoted: b
return null;
}
const match = text.slice(pos).match(/^[\p{ID_Start}_][\p{ID_Continue}$]*/u);
return match ? { value: match[0], quoted: false, end: pos + match[0].length } : null;
if (match) return { value: match[0], quoted: false, end: pos + match[0].length };
// MySQL/MariaDB also allow an unquoted identifier to begin with a digit as long as it is not a
// pure number, so `select * from 01_tablename` maps back to a base table while `123` / `1e3`
// stay literals (#9992).
const digitMatch = text.slice(pos).match(/^[0-9][\p{ID_Continue}$]*/u);
if (digitMatch && !isNumberShapedToken(digitMatch[0])) {
return { value: digitMatch[0], quoted: false, end: pos + digitMatch[0].length };
}
return null;
}
/**
* Mirrors `sqlNavigation.isNumberShapedToken`: a digit-leading token is only an identifier when
* it is not a number (`123`, `1e3`, `0x1f`, `0b101`). See #9992.
*/
function isNumberShapedToken(value: string): boolean {
if (!/^[0-9]/.test(value)) return false;
const rest = value.slice(1).toLowerCase();
if (rest === "") return true;
if (rest.startsWith("x")) return /^x[0-9a-f]+$/.test(rest);
if (rest.startsWith("b")) return /^b[01]+$/.test(rest);
return /^[0-9e]+$/.test(rest);
}
function skipWhitespace(text: string, pos: number): number {
+21 -2
View File
@@ -315,6 +315,21 @@ function isIdentifierStartChar(char: string | undefined): boolean {
return !!char && IDENTIFIER_START_CHAR.test(char);
}
/**
* MySQL/MariaDB allow an unquoted identifier to begin with a digit as long as it is not a pure
* number, so `01_tablename` is a table name while `123` / `1e3` / `0x1f` stay numeric literals
* (#9992). Other dialects reject the statement at parse time, so accepting the token here cannot
* resolve an identifier the server would refuse to run.
*/
function isNumberShapedToken(value: string): boolean {
if (!/^[0-9]/.test(value)) return false;
const rest = value.slice(1).toLowerCase();
if (rest === "") return true;
if (rest.startsWith("x")) return /^x[0-9a-f]+$/.test(rest);
if (rest.startsWith("b")) return /^b[01]+$/.test(rest);
return /^[0-9e]+$/.test(rest);
}
function isIdentifierChar(char: string | undefined): boolean {
return !!char && IDENTIFIER_PART_CHAR.test(char);
}
@@ -341,10 +356,14 @@ function readQuotedPart(text: string, start: number): IdentifierPart | null {
}
function readUnquotedPart(text: string, start: number): IdentifierPart | null {
if (!isIdentifierStartChar(text[start])) return null;
const first = text[start];
const startsWithDigit = !!first && /^[0-9]$/.test(first);
if (!isIdentifierStartChar(first) && !startsWithDigit) return null;
let end = start + 1;
while (end < text.length && isIdentifierChar(text[end])) end += 1;
return { value: text.slice(start, end), start, end, quoted: false };
const value = text.slice(start, end);
if (startsWithDigit && isNumberShapedToken(value)) return null;
return { value, start, end, quoted: false };
}
function readIdentifierPart(text: string, start: number): IdentifierPart | null {