feat: add relaxed quoting for qualified name tails#247
Merged
pyramation merged 3 commits intomainfrom Dec 25, 2025
Merged
Conversation
PostgreSQL's grammar accepts all keyword categories (including RESERVED_KEYWORD) in qualified name positions (after a dot). This change adds a new quoteIdentifierQualifiedTail() method that only quotes for lexical reasons (uppercase, special characters, leading digits) not for keyword reasons. This allows the deparser to emit: - faker.float instead of faker."float" - myschema.select instead of myschema."select" - t.from instead of t."from" while still correctly quoting unqualified identifiers that are keywords. Empirically verified with libpg-query that all keyword categories parse successfully in qualified positions across DDL and DML contexts.
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
- Rename quoteIdentifierQualifiedTail to quoteIdentifierAfterDot for clarity - Add quoteDottedName() helper that applies strict quoting to first part and relaxed (lexical-only) quoting to subsequent parts - Update FuncCall and CreateFunctionStmt to use quoteDottedName() - Remove unused nquotes variable from quoteIdentifier() - Update snapshots to show unquoted function names (faker.float, etc.)
Comprehensive documentation of the identifier quoting strategy including: - PostgreSQL background on identifier folding and quoting rules - The strict quoting algorithm (quote_identifier port) - The relaxed after-dot quoting algorithm - Grammar-slot sensitivity explanation - Composition helpers (quoteDottedName, quoteQualifiedIdentifier) - Deparser integration rules and anti-patterns - Examples and test matrix - Keyword table accuracy notes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat: add relaxed quoting for qualified name tails
Summary
This PR reduces unnecessary quoting of identifiers that appear after a dot in qualified names (e.g.,
schema.name,table.column).PostgreSQL's grammar accepts all keyword categories—including
RESERVED_KEYWORDlikeselect,from,table—in qualified name positions. The previous implementation was over-conservative, quoting keywords likefloat,interval,booleaneven when they appeared after a dot where quoting isn't required.Before:
faker."float",faker."interval",pg_catalog."substring"After:
faker.float,faker.interval,pg_catalog.substringChanges:
quoteIdentifierAfterDot()method that only quotes for lexical reasons (uppercase, special chars, leading digits) not keyword reasonsquoteDottedName(parts[])helper that applies strict quoting to the first part and relaxed quoting to subsequent partsFuncCallandCreateFunctionStmtto usequoteDottedName()for function namesRangeVarandResTargetindirection handling to usequoteIdentifierAfterDot()quoteQualifiedIdentifier()to use relaxed quoting for the tail componentnquotesvariable fromquoteIdentifier()QUOTING-RULES.mddocumentation explaining the quoting strategy, PostgreSQL background, algorithms, and contributor guidanceReview & Testing Checklist for Human
CREATE FUNCTION faker.float() RETURNS float AS $$ SELECT 1.0; $$ LANGUAGE sqland similar statements actually parse in PostgreSQL without quotes.join('.')patterns) that might need updating to usequoteDottedName()name === 'pg_catalog.substring'etc. after quoting - verify these comparisons still match since the function names should remain unquotedRecommended test plan: Parse and deparse SQL like
CREATE FUNCTION faker.float() RETURNS float AS $$ SELECT 1.0; $$ LANGUAGE sqland verify the output is valid PostgreSQL that can be re-parsed.Notes
AlertLevelvsalertlevelquoteIdentifier()method remains unchanged as the strict PostgreSQL-faithful port for standalone identifiersQUOTING-RULES.mddocumentation covers: PostgreSQL identifier folding, keyword categories, the strict vs relaxed quoting algorithms, composition helpers, deparser integration rules, and common pitfallsLink to Devin run: https://app.devin.ai/sessions/019c4a70e9164830aef1f807aad33e1d
Requested by: Dan Lynch (@pyramation)