Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 46a138b

Browse files
committed
fix REVOKE parser look-ahead failure due to FROM being parsed as IDENTIFIER
1 parent 0347a8e commit 46a138b

2 files changed

Lines changed: 23 additions & 11 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ cmake --build build
240240

241241
## Architecture
242242

243-
Header-only design: you only pay for what you use. 19 header files, no `.cpp`. See `include/libsqlglot/` for the full layout. Core files: `parser.h` (4191 lines), `generator.h` (2149), `expression.h` (1385, 115 expression types). Entry point is `transpiler.h` (86 lines).
243+
Header-only design: you only pay for what you use. 19 header files, no `.cpp`. See `include/libsqlglot/` for the full layout. Core files: `parser.h` (4203 lines), `generator.h` (2149), `expression.h` (1385, 115 expression types). Entry point is `transpiler.h` (86 lines).
244244

245245
### Memory management
246246

include/libsqlglot/parser.h

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,7 +1587,7 @@ class Parser {
15871587
pos_ = saved_pos; // Restore position
15881588
stmt->is_role_grant = true;
15891589

1590-
// Parse role names (preserve case using source view)
1590+
// Parse role names (use .view(source_) to preserve original case)
15911591
do {
15921592
if (current().type == TokenType::IDENTIFIER) {
15931593
stmt->roles.push_back(std::string(current().view(source_)));
@@ -1771,7 +1771,8 @@ class Parser {
17711771
if (obj_type == "SEQUENCE" || obj_type == "VIEW" || obj_type == "PROCEDURE" ||
17721772
obj_type == "ROUTINE" || obj_type == "TABLESPACE" || obj_type == "TYPE" ||
17731773
obj_type == "DOMAIN" || obj_type == "FOREIGN" || obj_type == "SERVER" ||
1774-
obj_type == "WAREHOUSE" || obj_type == "DATASET" || obj_type == "LOGIN") {
1774+
obj_type == "WAREHOUSE" || obj_type == "DATASET" || obj_type == "LOGIN" ||
1775+
obj_type == "STAGE") {
17751776
stmt->object_type = obj_type;
17761777
advance();
17771778

@@ -1814,7 +1815,11 @@ class Parser {
18141815
if (match(TokenType::DOT)) {
18151816
std::string second_segment;
18161817

1817-
if (current().type == TokenType::IDENTIFIER) {
1818+
if (current().type == TokenType::IDENTIFIER ||
1819+
current().type == TokenType::TABLE ||
1820+
current().type == TokenType::VIEW ||
1821+
current().type == TokenType::DATABASE ||
1822+
current().type == TokenType::SCHEMA) {
18181823
second_segment = std::string(current().text);
18191824
advance();
18201825
} else if (current().type == TokenType::STAR) {
@@ -1825,7 +1830,12 @@ class Parser {
18251830
// Check for third segment: catalog.schema.table
18261831
if (match(TokenType::DOT)) {
18271832
// Three-segment name: store as full qualified name
1828-
if (current().type == TokenType::IDENTIFIER) {
1833+
// Note: third segment can be TABLE/VIEW/etc keywords or identifiers
1834+
if (current().type == TokenType::IDENTIFIER ||
1835+
current().type == TokenType::TABLE ||
1836+
current().type == TokenType::VIEW ||
1837+
current().type == TokenType::DATABASE ||
1838+
current().type == TokenType::SCHEMA) {
18291839
obj_name = obj_name + "." + second_segment + "." + std::string(current().text);
18301840
advance();
18311841
} else if (current().type == TokenType::STAR) {
@@ -1975,22 +1985,23 @@ class Parser {
19751985
}
19761986
}
19771987

1978-
if (current().type == TokenType::IDENTIFIER &&
1979-
(std::string(current().text) == "FROM" || std::string(current().text) == "from")) {
1988+
// FROM is a keyword (TokenType::FROM), not an IDENTIFIER
1989+
if (current().type == TokenType::FROM) {
19801990
// This is a role revoke
19811991
pos_ = saved_pos;
19821992
stmt->is_role_revoke = true;
19831993

1984-
// Parse role names (preserve case using source view)
1994+
// Parse role names (REVOKE-specific - use .view(source_) to preserve case)
19851995
do {
19861996
if (current().type == TokenType::IDENTIFIER) {
1997+
// CRITICAL: Use view(source_) not .text to preserve original case!
19871998
stmt->roles.push_back(std::string(current().view(source_)));
19881999
advance();
19892000
}
19902001
} while (match(TokenType::COMMA));
19912002

1992-
// FROM clause
1993-
if (current().type == TokenType::IDENTIFIER && std::string(current().text) == "FROM") {
2003+
// FROM clause (FROM is a keyword token, not IDENTIFIER)
2004+
if (current().type == TokenType::FROM) {
19942005
advance();
19952006

19962007
do {
@@ -2154,7 +2165,8 @@ class Parser {
21542165
if (obj_type == "SEQUENCE" || obj_type == "VIEW" || obj_type == "PROCEDURE" ||
21552166
obj_type == "ROUTINE" || obj_type == "TABLESPACE" || obj_type == "TYPE" ||
21562167
obj_type == "DOMAIN" || obj_type == "FOREIGN" || obj_type == "SERVER" ||
2157-
obj_type == "WAREHOUSE" || obj_type == "DATASET" || obj_type == "LOGIN") {
2168+
obj_type == "WAREHOUSE" || obj_type == "DATASET" || obj_type == "LOGIN" ||
2169+
obj_type == "STAGE") {
21582170
stmt->object_type = obj_type;
21592171
advance();
21602172

0 commit comments

Comments
 (0)