Skip to content

fix(parser): accept string literal aliases after AS#22

Merged
yigalrozenberg merged 1 commit into
protegrity:masterfrom
AccountAim:fix/string-literal-alias
Jul 9, 2026
Merged

fix(parser): accept string literal aliases after AS#22
yigalrozenberg merged 1 commit into
protegrity:masterfrom
AccountAim:fix/string-literal-alias

Conversation

@gauravs

@gauravs gauravs commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Problem

SQLite, MySQL, and Snowflake accept a string literal as a column or table
alias, but the parser rejects it:

$ echo "select \"deals\".\"record_id\" as 'Record Id', sum(amount) from \"deals\" group by 1" \
    | sqlglot format --read sqlite

# Before (v0.10.9, unpatched):

error: Parser error: Expected identifier, got String ('Record Id') at line 1 col 31

# After:

SELECT
  "deals"."record_id" AS "Record Id",
  SUM(amount)
FROM
  "deals"
GROUP BY
  1

This form is common in machine-generated SQL (e.g. ORMs that quote
display-name aliases as string literals).

Fix

parse_optional_alias now accepts a String token in alias position after
an explicit AS, for both column and table aliases. The alias is an
identifier despite the quoting, so it is normalized to
QuoteStyle::DoubleQuote and the generator re-quotes it in each target
dialect's canonical style:

-- SQLite/Postgres:  SELECT record_id AS "Record Id" FROM deals
-- MySQL:            SELECT record_id AS `Record Id` FROM deals
-- T-SQL:            SELECT record_id AS [Record Id] FROM deals

The implicit form (no AS, e.g. SELECT 'a' 'b') is intentionally not
accepted — adjacent string literals are concatenation in MySQL, so treating
a trailing string as an alias would silently change semantics.

Escaped quotes in the literal are handled by the tokenizer as usual
(AS 'It''s' → AS "It's").

Tests

Six new tests in tests/test_quoted_aliases.rs: column alias, table alias,
alias alongside quoted column refs, escaped inner quote, SQLite→MySQL
transpile, and a regression guard that a trailing string without AS
remains a string expression.

SQLite, MySQL, and Snowflake accept a string literal as a column or
table alias (SELECT x AS 'My Alias'). The parser rejected it with
"Expected identifier, got String".

Accept a String token in alias position after an explicit AS and
normalize it to QuoteStyle::DoubleQuote — the alias is an identifier
despite the quoting, and the generator already re-quotes per target
dialect. The implicit form (no AS) is intentionally not accepted, since
a trailing string is concatenation in MySQL.

@yigalrozenberg yigalrozenberg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved — small, focused, and correct. ✅

The key detail that makes this robust: the string-literal alias is returned as a quoted identifier (QuoteStyle::DoubleQuote) rather than an unquoted name, so it flows through write_quotedQuoteStyle::for_dialect and emits the right quote style for every dialect family, with proper embedded-quote escaping:

  • double-quote → PostgreSQL / Oracle / Snowflake / ANSI
  • backtick → MySQL / BigQuery / Hive / Spark
  • bracket → T-SQL / Fabric

Choosing quoted (not None) is exactly right — it preserves case under PG/Oracle/Snowflake folding. Gating strictly behind an explicit AS is also correct (a trailing string with no AS stays an expression / MySQL concatenation).

Verified locally: all 20 tests in test_quoted_aliases pass, no regressions across the test_transpile / test_dialects / test_expressions suites.

Merging with a few small maintainer follow-ups: broaden coverage with a T-SQL (bracket) test, a Snowflake case, and PG/Oracle transpile targets, and clarify the code comment (T-SQL also uses AS 'col', and the acceptance is intentionally dialect-agnostic / lenient on input). Thanks for the clean, well-tested fix!

yigalrozenberg added a commit that referenced this pull request Jul 9, 2026
…p to #22)

- Add T-SQL (bracket), Snowflake, and PG/Oracle/T-SQL transpile tests so all
  three quote families are exercised, not just double-quote + backtick.
- Clarify the comment: T-SQL also accepts AS 'col', and acceptance is
  intentionally dialect-agnostic / lenient on input.
@yigalrozenberg
yigalrozenberg merged commit ab1853c into protegrity:master Jul 9, 2026
@yigalrozenberg

Copy link
Copy Markdown
Collaborator

🎉 Thanks so much for this contribution, @gauravs — a clean, focused, and well-tested fix. Really appreciate the thorough test cases (the escaped-quote and "no AS ⇒ not an alias" ones especially).

This has been merged to master and released in v0.10.11, now live on crates.io:

[dependencies]
sqlglot-rust = "0.10.11"

I added a couple of small maintainer follow-ups on top of your change (commit cab52c9):

  • Broadened the tests so all three quote families are exercised end-to-end — T-SQL (AS [Mixed]), Snowflake and PostgreSQL/Oracle transpile targets — in addition to your MySQL/backtick case.
  • Clarified the code comment to note that T-SQL also accepts AS 'col', and that the acceptance is intentionally dialect-agnostic (lenient on input, consistent with the existing TRUE/FALSE and DuckDB-keyword branches).

Thanks again for making the parser better! 🙌

@gauravs
gauravs deleted the fix/string-literal-alias branch July 9, 2026 15:50
yigalrozenberg added a commit that referenced this pull request Jul 9, 2026
Docs-only: adds the 'String-Literal Aliases' subsection to docs/reference.md,
documenting the v0.10.11 feature (PR #22). No code changes.
gauravs added a commit to AccountAim/sql-glot-ruby that referenced this pull request Jul 9, 2026
Primarily the change is to support single quotes in column names. See
protegrity/sql-glot-rust#22 for additional
details.
gauravs pushed a commit to AccountAim/sql-glot-rust that referenced this pull request Jul 13, 2026
…egrity#22 follow-up)

Add a 'String-Literal Aliases' subsection to the QuoteStyle reference: AS 'x'
is accepted for every dialect (lenient input) and normalized to the target
dialect's canonical quoted identifier (double-quote / backtick / bracket) with
case preserved. Examples are the exact outputs asserted by the
test_single_quoted_alias_transpile_to_* tests. Follow-up to PR protegrity#22 (v0.10.11).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants