PostgreSQL Syntax Reference
Microck/ordinary-claude-skills
在實作 pgschema 功能時,請參閱 PostgreSQL 的解析器與語法(gram.y),以了解 SQL 語法、DDL 語句結構及解析規則
...展開全部關於PostgreSQL Syntax Reference
PostgreSQL Syntax Reference 技能提供了一份全面的指南,協助您理解 PostgreSQL 的 SQL 語法、解析規則以及資料定義語言(DDL)陳述式的結構。對於使用 PostgreSQL 資料庫的開發人員而言,這是一項不可或缺的工具,能協助他們實現精確的 SQL 解析,並產生有效的 DDL 陳述式。 此技能基於 PostgreSQL 解析器的文法(gram.y),該文法對於識別和結構化 SQL 查詢至關重要,可確保 SQL 檔案被正確解析,並能無誤地產生 SQL 陳述式。 本技能將引導使用者逐步掌握解析複雜 SQL 結構、理解運算優先級規則,以及運用 PostgreSQL 專屬的語法擴充功能。
常見問題
何時應使用「PostgreSQL Syntax Reference 」技能?
您應在實作或除錯 SQL 解析時使用此技能,特別是在處理 CREATE TABLE、CREATE TRIGGER 等 PostgreSQL SQL 語句時。它在產生 DDL 語句或驗證 SQL 語法時也十分有用。
使用此技能時應參考哪些檔案和目錄?
主要參考檔案包括用於語法定義的 'gram.y'、用於詞法分析規則的 'scan.l',以及各類解析與轉換檔案,例如用於子句解析的 'parse_clause.c' 和用於 DDL 語句的 'parse_utilcmd.c'。
使用此技能時是否有特定的工作流程需遵循?
是的,建議的工作流程是:首先識別 SQL 陳述式類型,在 'gram.y' 中找出相關的文法規則,然後根據文法定義來理解該 SQL 陳述式的結構與元素。
這項技能是否有任何限制?
此技能主要針對 PostgreSQL 的 SQL 語法與文法,因此可能不適用於其他資料庫系統。此外,此技能假設使用者熟悉 PostgreSQL 程式碼庫,並可能需要對 C 語言及 Yacc/Bison 語法有扎實的理解。
PostgreSQL Syntax Reference
Use this skill when you need to understand PostgreSQL's SQL syntax, DDL statement structure, or how PostgreSQL parses specific SQL constructs. This is essential for correctly parsing SQL files and generating valid DDL in pgschema.
When to Use This Skill
Invoke this skill when:
- Implementing new SQL statement parsing in
ir/parser.go - Debugging SQL parsing issues with pg_query_go
- Understanding complex SQL syntax (CREATE TABLE, CREATE TRIGGER, etc.)
- Generating DDL statements in
internal/diff/*.go - Validating SQL statement structure
- Understanding precedence and grammar rules
- Learning about PostgreSQL-specific syntax extensions
Source Code Locations
Main parser directory: https://github.com/postgres/postgres/blob/master/src/backend/parser/
Key files to reference:
Grammar and Lexer
gram.y- Main grammar file - Yacc/Bison grammar defining PostgreSQL SQL syntaxscan.l- Lexical scanner (Flex/Lex) - tokenization ruleskeywords.c- Reserved and non-reserved keywords
Parser Implementation
parse_clause.c- Parsing of clauses (WHERE, GROUP BY, ORDER BY, etc.)parse_expr.c- Expression parsing (operators, function calls, etc.)parse_type.c- Type name parsing and resolutionparse_relation.c- Table and relation parsingparse_target.c- Target list parsing (SELECT list, etc.)parse_func.c- Function call parsingparse_utilcmd.c- Utility commands (DDL statements like CREATE, ALTER, DROP)
Analysis and Transformation
analyze.c- Post-parse analysisparse_node.c- Parse node creation utilities
Step-by-Step Workflow
1. Identify the SQL Statement Type
Determine what kind of SQL you're working with:
| Statement Type | gram.y Section | parse_utilcmd.c Function |
|---|---|---|
| CREATE TABLE | CreateStmt | transformCreateStmt() |
| ALTER TABLE | AlterTableStmt | transformAlterTableStmt() |
| CREATE INDEX | IndexStmt | transformIndexStmt() |
| CREATE TRIGGER | CreateTrigStmt | transformCreateTrigStmt() |
| CREATE FUNCTION | CreateFunctionStmt | transformCreateFunctionStmt() |
| CREATE PROCEDURE | CreateFunctionStmt | (procedures are functions) |
| CREATE VIEW | ViewStmt | transformViewStmt() |
| CREATE MATERIALIZED VIEW | CreateMatViewStmt | - |
| CREATE SEQUENCE | CreateSeqStmt | transformCreateSeqStmt() |
| CREATE TYPE | CreateEnumStmt, CreateDomainStmt, CompositeTypeStmt | - |
| CREATE POLICY | CreatePolicyStmt | transformCreatePolicyStmt() |
| COMMENT ON | CommentStmt | - |
2. Locate the Grammar Rule in gram.y
Search gram.y for the statement's production rule:
Example - Finding CREATE TRIGGER syntax:
# In the postgres repositorygrep -n "CreateTrigStmt:" src/backend/parser/gram.y
What to look for:
- The production rule name (e.g.,
CreateTrigStmt:) - Alternative syntaxes (multiple
|branches) - Optional elements (
opt_*rules) - List constructs (
*_listrules) - Terminal tokens (keywords, literals)
3. Understand the Grammar Structure
gram.y uses Yacc/Bison syntax:
CreateTrigStmt: CREATE opt_or_replace TRIGGER name TriggerActionTime TriggerEvents ON qualified_name TriggerReferencing TriggerForSpec TriggerWhen EXECUTE FUNCTION_or_PROCEDURE func_name '(' TriggerFuncArgs ')' { CreateTrigStmt *n = makeNode(CreateTrigStmt); n->trigname = $4; n->relation = $8; n->funcname = $13; /* ... */ $$ = (Node *)n; }
Key elements:
- Terminals (uppercase): Keywords like
CREATE,TRIGGER,ON - Non-terminals (lowercase): Other grammar rules like
name,qualified_name - Actions (
{ ... }): C code that builds the parse tree - Alternatives (
|): Different ways to write the same statement - Optional elements: Rules prefixed with
opt_
4. Trace Through Related Rules
Follow the grammar rules to understand the complete syntax:
Example - Understanding trigger events:
TriggerEvents: TriggerOneEvent | TriggerEvents OR TriggerOneEventTriggerOneEvent: INSERT | DELETE | UPDATE | UPDATE OF columnList | TRUNCATE
This shows:
- Triggers can have multiple events combined with OR
- UPDATE can optionally specify columns with
OF columnList
5. Cross-Reference with parse_utilcmd.c
After understanding the grammar, check how PostgreSQL transforms the parsed statement:
Example - How CREATE TRIGGER is processed:
// In parse_utilcmd.cstatic voidtransformCreateTrigStmt(CreateTrigStmt *stmt, const char *queryString){ // Validation and transformation logic // - Check trigger name conflicts // - Validate trigger function exists // - Process WHEN condition // - Handle constraint triggers}
6. Apply to pgschema
Use this understanding in pgschema:
For parsing (ir/parser.go):
- pgschema uses
pg_query_gowhich wraps libpg_query (based on PostgreSQL's parser) - Parse tree structure matches gram.y production rules
- Access parsed nodes to extract information
For DDL generation (internal/diff/*.go):
- Follow gram.y syntax exactly
- Use proper keyword ordering
- Include all required elements
- Quote identifiers correctly
Key Grammar Concepts
Optional Elements
Grammar rules prefixed with opt_ are optional:
opt_or_replace: OR REPLACE { $$ = true; } | /* EMPTY */ { $$ = false; }
This means CREATE OR REPLACE TRIGGER ... and CREATE TRIGGER ... are both valid.
Lists
Lists are typically defined recursively:
columnList: columnElem { $$ = list_make1($1); } | columnList ',' columnElem { $$ = lappend($1, $3); }Alternatives
Use | to show different syntax options:
TriggerActionTime: BEFORE { $$ = TRIGGER_TYPE_BEFORE; } | AFTER { $$ = TRIGGER_TYPE_AFTER; } | INSTEAD OF { $$ = TRIGGER_TYPE_INSTEAD; }
Precedence
Operator precedence is defined at the top of gram.y:
%left OR%left AND%right NOT%nonassoc IS ISNULL NOTNULL%nonassoc '<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS
Common Grammar Patterns
CREATE Statement Pattern
Most CREATE statements follow this pattern:
CreateSomethingStmt: CREATE opt_or_replace SOMETHING name definition_elements
ALTER Statement Pattern
AlterSomethingStmt: ALTER SOMETHING name alter_action | ALTER SOMETHING IF_P EXISTS name alter_action
DROP Statement Pattern
DropSomethingStmt: DROP SOMETHING name opt_drop_behavior | DROP SOMETHING IF_P EXISTS name opt_drop_behavior
Important SQL Constructs for pgschema
Table Columns with Constraints
columnDef: ColId Typename opt_column_storage ColQualList | ColId Typename opt_column_storage GeneratedConstraintElem | ColId Typename opt_column_storage GENERATED generated_when AS IDENTITY_P OptParenthesizedSeqOptList
This covers:
- Regular columns:
column_name type - Generated columns:
column_name type GENERATED ALWAYS AS (expr) STORED - Identity columns:
column_name type GENERATED ALWAYS AS IDENTITY
Trigger WHEN Clause
TriggerWhen: WHEN '(' a_expr ')' { $$ = $3; } | /* EMPTY */ { $$ = NULL; }
Index Elements
index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
This shows indexes can be on:
- Simple columns
- Function expressions (functional indexes)
- Arbitrary expressions (expression indexes)
Foreign Key Options
ConstraintAttributeSpec: ON DELETE key_action | ON UPDATE key_action | DEFERRABLE | NOT DEFERRABLE | INITIALLY DEFERRED | INITIALLY IMMEDIATE
Keywords and Reserved Words
Check keywords.c for keyword classification:
Reserved keywords: Cannot be used as identifiers without quoting
SELECT,FROM,WHERE,CREATE,TABLE, etc.
Type function name keywords: Can be used as function or type names
CHAR,CHARACTER,VARCHAR, etc.
Unreserved keywords: Can be used as identifiers
ABORT,ABSOLUTE,ACCESS,ACTION, etc.
Impact on pgschema: When generating DDL, quote identifiers that match reserved keywords.
Examples
Example 1: Understanding CREATE TABLE LIKE
In gram.y:
TableLikeClause: LIKE qualified_name TableLikeOptionListTableLikeOptionList:
TableLikeOptionList: TableLikeOptionList INCLUDING TableLikeOption | TableLikeOptionList EXCLUDING TableLikeOption | /* EMPTY */
TableLikeOption:
TableLikeOption: COMMENTS | CONSTRAINTS | DEFAULTS | IDENTITY_P | GENERATED | INDEXES | STATISTICS | STORAGE | ALL
This tells us:
LIKE table_nameis the basic syntax- Can include/exclude specific features:
INCLUDING ALL,EXCLUDING INDEXES, etc. - Multiple options can be combined
pgschema usage (ir/parser.go):
// Parse CREATE TABLE ... LIKE statementsif createTableStmt.Inherits != nil { for _, inherit := range createTableStmt.Inherits { if inherit.Relpersistence == "l" { // LIKE clause table.LikeClause = &LikeClause{ Parent: inherit.Relname, Options: parseLikeOptions(inherit), } } }}
Example 2: Understanding Constraint Triggers
In gram.y:
ConstraintAttributeSpec: DEFERRABLE { $$ = CAS_DEFERRABLE; } | NOT DEFERRABLE { $$ = CAS_NOT_DEFERRABLE; } | INITIALLY DEFERRED { $$ = CAS_INITIALLY_DEFERRED; } | INITIALLY IMMEDIATE { $$ = CAS_INITIALLY_IMMEDIATE; }
For constraint triggers:
CreateTrigStmt: CREATE opt_or_replace CONSTRAINT TRIGGER name ...
This tells us:
- Constraint triggers use
CREATE CONSTRAINT TRIGGER - Can be
DEFERRABLEorNOT DEFERRABLE - Can be
INITIALLY DEFERREDorINITIALLY IMMEDIATE
pgschema DDL generation (internal/diff/trigger.go):
func generateCreateTrigger(trigger *ir.Trigger) string { var sql strings.Builder sql.WriteString("CREATE ") if trigger.IsConstraint { sql.WriteString("CONSTRAINT ") } sql.WriteString("TRIGGER ") sql.WriteString(quoteIdentifier(trigger.Name)) // ... if trigger.Deferrable { sql.WriteString(" DEFERRABLE") } if trigger.InitiallyDeferred { sql.WriteString(" INITIALLY DEFERRED") } return sql.String()}
Example 3: Understanding Expression Indexes
In gram.y:
index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order { $$ = makeIndexElem($1, NULL, NULL, $2, $3, $4, $5, NULL); } | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order { $$ = makeIndexElem(NULL, $1, NULL, $2, $3, $4, $5, NULL); } | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order { $$ = makeIndexElem(NULL, NULL, $2, $4, $5, $6, $7, NULL); }This tells us:
- Index elements can be:
- Column names:
CREATE INDEX idx ON table (column) - Function calls:
CREATE INDEX idx ON table (lower(column)) - Arbitrary expressions:
CREATE INDEX idx ON table ((column + 1))
- Column names:
- Note the extra parentheses for arbitrary expressions:
(( ... ))
pgschema parsing consideration:
// When parsing index definitions, handle all three forms:// 1. Simple column reference// 2. Function expression// 3. Arbitrary expression (needs extra parens in DDL)
Example 4: Understanding GENERATED Columns
In gram.y:
GeneratedConstraintElem: GENERATED generated_when AS '(' a_expr ')' STORED { Constraint *n = makeNode(Constraint); n->contype = CONSTR_GENERATED; n->generated_when = $2; n->raw_expr = $5; n->cooked_expr = NULL; n->location = @1; $$ = (Node *)n; }generated_when: ALWAYS { $$ = ATTRIBUTE_IDENTITY_ALWAYS; } | BY DEFAULT { $$ = ATTRIBUTE_IDENTITY_BY_DEFAULT; }
This tells us:
- Generated columns:
GENERATED ALWAYS AS (expression) STORED - Identity columns:
GENERATED ALWAYS AS IDENTITYorGENERATED BY DEFAULT AS IDENTITY - The expression must be in parentheses
- Must include
STOREDkeyword for computed columns
Working with pg_query_go
pgschema uses pg_query_go/v6 which provides Go bindings to libpg_query (PostgreSQL parser):
Parse Tree Structure
The parse tree from pg_query_go matches gram.y structure:
import "github.com/pganalyze/pg_query_go/v6"result, err := pg_query.Parse(sqlStatement)if err != nil { return err}// result.Stmts contains parsed statement nodes// Structure matches gram.y production rulesfor _, stmt := range result.Stmts { switch node := stmt.Stmt.Node.(type) { case *pg_query.Node_CreateStmt: // Handle CREATE TABLE case *pg_query.Node_CreateTrigStmt: // Handle CREATE TRIGGER case *pg_query.Node_IndexStmt: // Handle CREATE INDEX }}
Accessing Grammar Elements
Map gram.y rules to pg_query_go node fields:
gram.y:
CreateTrigStmt: CREATE TRIGGER name TriggerActionTime TriggerEvents ON qualified_name
pg_query_go:
createTrigStmt := node.CreateTrigStmttriggerName := createTrigStmt.Trigname // maps to 'name'timing := createTrigStmt.Timing // maps to 'TriggerActionTime'events := createTrigStmt.Events // maps to 'TriggerEvents'relation := createTrigStmt.Relation // maps to 'qualified_name'
Debugging Tips
1. Test Grammar Interactively
Clone postgres and build the parser:
git clone https://github.com/postgres/postgres.gitcd postgres./configuremake -C src/backend/parser2. Use pg_query_go for Validation
Test parsing in pgschema:
import "github.com/pganalyze/pg_query_go/v6"sql := "CREATE TRIGGER ..."result, err := pg_query.Parse(sql)if err != nil { // Invalid syntax fmt.Println("Parse error:", err)}// Valid syntax - examine result.Stmts
3. Compare with PostgreSQL Behavior
Test actual PostgreSQL behavior:
psql -c "CREATE TRIGGER ..."# If PostgreSQL accepts it, the syntax is valid# Use \d+ to see how PostgreSQL formats it
4. Check gram.y Comments
gram.y contains helpful comments explaining syntax choices and historical notes.
5. Search for Examples in Tests
PostgreSQL's test suite has extensive SQL examples:
# In postgres repofind src/test/regress/sql -name "*.sql" -exec grep -l "CREATE TRIGGER" {} \;
Version Differences
PostgreSQL syntax evolves across versions:
- PostgreSQL 14: Added
COMPRESSIONclause for tables - PostgreSQL 15: Added
MERGEstatement,UNIQUE NULLS NOT DISTINCT - PostgreSQL 16: Added SQL/JSON functions
- PostgreSQL 17: Added
MERGEenhancements, incremental view maintenance
For pgschema (supports 14-17):
- Check gram.y history to see when features were added
- Add version detection in parser if needed
- Test across all supported versions
Verification Checklist
After consulting gram.y and implementing in pgschema:
- Grammar rule fully understood from gram.y
- All syntax alternatives identified
- Optional elements properly handled
- List constructs correctly parsed
- Keywords and quoting rules followed
- pg_query_go parse tree structure matches expectations
- DDL generation produces valid PostgreSQL syntax
- Test case added with sample SQL
- Tested against PostgreSQL (manually or via integration test)
- Works across PostgreSQL versions 14-17
Quick Reference
Finding syntax in gram.y:
# Search for statement typegrep -n "CreateTrigStmt:" src/backend/parser/gram.y# Find keyword definitionsgrep -n "^TRIGGER" src/backend/parser/gram.y# Understand an optiongrep -A 10 "TriggerWhen:" src/backend/parser/gram.y
Understanding precedence:
# Look at top of gram.yhead -100 src/backend/parser/gram.y | grep -A 50 "%left\|%right\|%nonassoc"
Find utility command handling:
grep -n "transformCreateTrigStmt" src/backend/parser/parse_utilcmd.c 安裝 PostgreSQL Syntax Reference
請下載並將技能檔案解壓縮至您的 .claude/skills/ 目錄中。
下載 ZIP複製儲存庫並將技能檔案複製到您的專案中。
git clone https://github.com/Microck/ordinary-claude-skills/blob/main/skills_all/postgresql-syntax-reference/SKILL.md # Copy SKILL.md to your .claude/skills/ directory
複製





首頁
