Skip to content

Postgres: allow reserved keywords as bare column alias - #2491

Open
BenSatori wants to merge 7 commits into
apache:mainfrom
BenSatori:postgres-bare-keyword-column-alias
Open

BenSatori wants to merge 7 commits into
apache:mainfrom
BenSatori:postgres-bare-keyword-column-alias

Conversation

@BenSatori

Copy link
Copy Markdown
Contributor

PostgreSQL allows almost any keyword (SELECT, ANALYZE, LATERAL, AND, OR, COLLATE, ...) to be used as a bare (AS-less) column alias unless it is in a small set of keywords that require a leading AS.

See https://www.postgresql.org/docs/current/sql-keywords-appendix.html

Previously:
SELECT (SELECT c FROM tbl_name LIMIT 1) select;

(or analyze/lateral/and/or/collate)
failed to parse.

This PR fixes that

Comment thread src/dialect/postgresql.rs Outdated
Comment thread src/parser/mod.rs
Comment thread src/parser/mod.rs
PostgreSQL allows almost any keyword (SELECT, ANALYZE, LATERAL, AND, OR,
COLLATE, ...) to be used as a bare (AS-less) column alias unless it is in
a small set of keywords that require a leading AS.

See https://www.postgresql.org/docs/current/sql-keywords-appendix.html

Previously:
  SELECT (SELECT c FROM tbl_name LIMIT 1) select;
failed to parse.
@BenSatori
BenSatori force-pushed the postgres-bare-keyword-column-alias branch from 907398c to 0de43ef Compare September 14, 2026 10:21
@BenSatori

Copy link
Copy Markdown
Contributor Author

I also tested directly on pg the aliases in the tests @LucaCappelletti94 .

@codecov-commenter

codecov-commenter commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.50000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.97%. Comparing base (6862e71) to head (7a9d776).

Files with missing lines Patch % Lines
src/parser/mod.rs 83.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2491   +/-   ##
=======================================
  Coverage   80.96%   80.97%           
=======================================
  Files          42       42           
  Lines       33386    33402   +16     
  Branches    33386    33402   +16     
=======================================
+ Hits        27032    27046   +14     
- Misses       2789     2790    +1     
- Partials     3565     3566    +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@LucaCappelletti94 LucaCappelletti94 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snowflake's is_column_alias returns true for any keyword not in its own list, so the new loop break accepts SELECT 1 AND and SELECT 1 OR. Snowflake reserves both (reserved keywords).

Do extend the keyword list, and add the necessary tests for them.

-            Keyword::FROM
+            Keyword::AND
+            | Keyword::FROM
             | Keyword::GROUP
             | Keyword::HAVING
             | Keyword::INTERSECT
             | Keyword::INTO
             | Keyword::MINUS
+            | Keyword::OR
             | Keyword::ORDER

Comment thread src/dialect/postgresql.rs
Keyword::EXCLUDE,
Keyword::EXPLAIN,
Keyword::VALUES,
Keyword::VIEW,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also list the keywords PostgreSQL does not know at all. next_token_starts_an_expr reads SORT, TOP, MINUS and DISTRIBUTE as clause keywords, so SELECT a AND sort FROM t WHERE b OR top now fails with Expected: end of statement, found: sort. It parses on main and runs on PostgreSQL 17.

Suggested change
Keyword::VIEW,
Keyword::VIEW,
// Not PostgreSQL keywords at all.
Keyword::DISTRIBUTE,
Keyword::MINUS,
Keyword::SORT,
Keyword::TOP,

// `AND`/`OR`/`COLLATE` are still parsed as operators when followed by an operand.
pg().verified_stmt("SELECT 1 AND 2");
pg().verified_stmt("SELECT 1 OR 2");
pg().verified_stmt(r#"SELECT 1 COLLATE "de_DE" FROM tbl_name"#);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line fails on the current head.

Suggested change
pg().verified_stmt(r#"SELECT 1 COLLATE "de_DE" FROM tbl_name"#);
pg().verified_stmt(r#"SELECT 1 COLLATE "de_DE" FROM tbl_name"#);
pg().verified_stmt("SELECT a AND sort FROM tbl_name WHERE b OR top");

Comment thread tests/sqlparser_common.rs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add the negative non-PostgreSQL test. On the current head it fails because SnowflakeDialect parses SELECT 1 AND as SELECT 1 AS AND.

Suggested change
}
#[test]
fn parse_and_or_not_bare_column_alias() {
let dialects = all_dialects_but_pg();
for sql in ["SELECT 1 AND", "SELECT 1 OR", "SELECT 1 AND, 2"] {
assert!(dialects.parse_sql_statements(sql).is_err(), "{sql}");
}
}

@LucaCappelletti94 LucaCappelletti94 added the waiting on contributor The review needs further refinements by its author label Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PostgreSQL waiting on contributor The review needs further refinements by its author

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants