Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
44139c5
sql-parser: Escape single quotes in HexString AstDisplay
def- May 29, 2026
b04e943
sql-parser: Disambiguate field-access receivers with parens
def- May 29, 2026
caa3324
sql-parser: Force-quote function names that clash with special-gramma…
def- May 29, 2026
a7c08a8
sql-parser: Force-quote `map` type name in AstDisplay
def- May 29, 2026
74dffb2
sql-parser: Quote context-sensitive keywords as identifiers
def- May 29, 2026
7965ab8
sql-parser: Parenthesize low-precedence LHS of quantified comparisons
def- May 29, 2026
d3bdc1d
sql-parser: Don't force-quote context-sensitive keywords in identifie…
def- May 29, 2026
b299644
sql-parser: Cap maybe_parse failures to prevent exponential backtracking
def- May 30, 2026
b4665fd
sql-parser: Quote renaming type-name keywords on display
def- May 30, 2026
7a00b06
sql-pretty: Quote special-grammar function names
def- May 30, 2026
3d03f2e
sql-parser: Parenthesize subscript receivers with keyword names
def- May 30, 2026
6208e8e
sql-parser: Check qualified type names for canonicalizing keywords
def- May 30, 2026
62d91fe
sql-lexer: Mark DISTINCT as always reserved
def- May 30, 2026
305005c
sql-parser: Scale maybe_parse failure cap with token count
def- May 30, 2026
647ba13
sql-parser: Remove dead ROLE branch in connection option name
def- May 31, 2026
2ada1b5
sql-parser: Omit leading comma in CREATE TABLE with no columns
def- May 31, 2026
d0258fa
sql-parser: Unwrap (SHOW …) into a top-level Show statement
def- May 31, 2026
29959c6
sql-lexer: Mark ALL as always reserved
def- May 31, 2026
d0ee6a7
sql-parser: Escape single quotes in protobuf MESSAGE name on display
def- May 31, 2026
84c36a9
sql-parser: Display SUBSCRIBE TO so keyword-named relations round-trip
def- May 31, 2026
204a85b
sql-parser: Bound table-factor recursion to reject deeply-nested parens
def- Jun 1, 2026
6260753
sql-parser: Quote query-body keywords used as identifiers
def- Jun 1, 2026
ac45a60
sql-parser: Bound iterative expression chain depth
def- Jun 1, 2026
4df990e
sql-parser: Parenthesize a SHOW query body on display
def- Jun 1, 2026
8576955
sql-parser: Don't use EXTRACT/POSITION special display for table func…
def- Jun 1, 2026
6510cc2
sql-parser: Quote `list` as a special-grammar function name on display
def- Jun 2, 2026
aee5b52
sql-parser: Quote `as` identifier on display
def- Jun 2, 2026
f263b3c
sql-parser: Only special-display `extract` when its field is a string
def- Jun 2, 2026
5a5d629
sql-parser: Reject an empty resolved cluster/network-policy id
def- Jun 2, 2026
629d16d
sql-parser: Quote identifiers needing it in FETCH cursor and resolved…
def- Jun 3, 2026
9e0ca07
sql-parser: Parenthesize a SHOW-led set operation on display
def- Jun 3, 2026
53efe54
sql-parser: Parenthesize a numeric cast operand under a prefix operat…
def- Jun 3, 2026
442050e
sql-parser: Quote quantifier-keyword function names on display
def- Jun 3, 2026
d053adb
sql: Quote bare ANY/ALL/SOME identifiers so they round-trip
def- Jun 3, 2026
74df540
sql: Fix three pretty/display round-trip bugs found by grammar fuzzing
def- Jun 3, 2026
9faa8c1
sql: Fix more pretty/display round-trip bugs found by grammar fuzzing
def- Jun 3, 2026
3a6ddf1
sql: Restrict the position special form to a primary needle
def- Jun 3, 2026
1edce12
sql: Parenthesize low-precedence COLLATE operands
def- Jun 3, 2026
beb4175
sql: Parenthesize prefix-operator operands by precedence
def- Jun 3, 2026
52107dd
sql: Don't parenthesize unary-operator operands of prefix operators
def- Jun 4, 2026
7f9fa14
sql: Fix GRANT/REVOKE ON ALL POLICIES display round-trip
def- Jun 4, 2026
480023a
sql-pretty: Print secrets in DECLARE/PREPARE inner statements
def- Jun 5, 2026
fcc33e0
sql-parser: Quote a `PREPARE` identifier so DEALLOCATE round-trips
def- Jun 5, 2026
bdae32f
sql-parser: Quote an `IN` index name so CREATE INDEX round-trips
def- Jun 6, 2026
a48f826
sql-pretty: Quote an `IN` index name in the pretty printer too
def- Jun 6, 2026
73a14a0
sql-parser: Quote a `WHEN` identifier so a CASE operand round-trips
def- Jun 6, 2026
5750e78
sql-pretty: Cover the DECLARE/PREPARE secret case in the lossless rou…
def- Jun 10, 2026
c65aa8f
sql-parser: Cap the expression chain at EXPR_CHAIN_LIMIT (1024)
def- Jun 10, 2026
8aea7bb
tests: Update SQL goldens for the identifier-quoting fixes
def- Jun 11, 2026
27a76bb
sql: Quote ALL/DISTINCT identifiers on display, not at parse time
def- Jun 15, 2026
c6a8e03
address reviewer comment
def- Jun 15, 2026
8cde935
address reviewer comments (#2)
def- Jun 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions src/expr/src/explain/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1197,6 +1197,14 @@ impl HumanizerMode for HumanizedExplain {
fn humanize_ident(col: usize, ident: Ident, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if ident.as_str() == UNKNOWN_COLUMN_NAME {
write!(f, "#{col}")
} else if ident.has_only_bare_chars() {
// The leading `#c` already disambiguates the column, and EXPLAIN
// output is never reparsed, so a keyword-named column (`any`,
// `all`, …) needs no quoting here — unlike in SQL display. Print
// the name bare for legibility; only fall back to the quoting
// `Ident` display for names with awkward characters (whitespace,
// braces, quotes) that would otherwise muddle the `{…}` annotation.
write!(f, "#{col}{{{}}}", ident.as_str())
} else {
write!(f, "#{col}{{{ident}}}")
}
Expand Down
40 changes: 40 additions & 0 deletions src/sql-lexer/src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ impl Keyword {
)
}

/// Reports whether this keyword begins a query body (`SELECT`, `VALUES`,
/// `TABLE …`, etc.).
///
/// When `AstDisplay` parenthesizes an expression (e.g. to disambiguate a
/// field access like `(expr).f`) and that expression's leading token is a
/// bare identifier with one of these names, the re-parser treats the
/// parentheses as a subquery and the identifier as its leading clause
/// (e.g. `(table & x)` parses as a `TABLE`-query). Such identifiers must be
/// quoted to round-trip. `SELECT`/`WITH` are already `is_always_reserved`.
pub fn begins_query_body(self) -> bool {
matches!(self, WITH | SELECT | VALUES | SHOW | TABLE)
}

/// Reports whether this keyword requires quoting when used in scalar expressions.
///
/// These are the keywords `Parser::parse_prefix` won't parse as an identifier.
Expand Down Expand Up @@ -140,6 +153,33 @@ impl Keyword {
|| self.is_reserved_in_column_alias()
|| self.is_reserved_in_scalar_expression()
}

/// Reports whether a keyword has a special parser-dispatch form (e.g.
/// `POSITION(expr IN expr)`, `MAP[K => V]`) such that an unquoted
/// occurrence in expression position triggers the special grammar
/// rather than parsing as a plain identifier. The parser itself
/// disambiguates by looking at the next token, but `AstDisplay` has no
/// such context — so when emitting an `Ident` whose name matches one
/// of these, we force quoting to keep the round trip stable.
pub fn is_context_sensitive_keyword(self) -> bool {
matches!(
self,
ALL | ANY
| COALESCE
| EXISTS
| EXTRACT
| GREATEST
| LEAST
| MAP
| NORMALIZE
| NULLIF
| POSITION
| ROW
| SOME
| SUBSTRING
| TRIM
)
}
}

impl FromStr for Keyword {
Expand Down
2 changes: 1 addition & 1 deletion src/sql-parser/src/ast/defs/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ impl AstDisplay for CsrSeedProtobufSchema {
f.write_str("SCHEMA '");
f.write_str(&display::escape_single_quote_string(&self.schema));
f.write_str("' MESSAGE '");
f.write_str(&self.message_name);
f.write_str(&display::escape_single_quote_string(&self.message_name));
f.write_str("'");
}
}
Expand Down
Loading
Loading