This project is a translation of PostgreSQL's DDL processing code from C to Go. Every function, file, and type should have a clear 1:1 correspondence to the original PostgreSQL source code.
Do NOT "implement" DDL semantics from scratch. Translate the PG source.
The PostgreSQL source tree is available at ../postgres/ for reference.
Every public function MUST have a // pg: <file>:<function> comment referencing the PostgreSQL source it translates.
// DefineRelation creates a new relation (table).
//
// pg: src/backend/commands/tablecmds.c — DefineRelation
func (c *Catalog) DefineRelation(stmt *nodes.CreateStmt, relkind byte) error {If a helper function is introduced that doesn't exist in PG (e.g., Go-specific adaptation), mark it:
// resolveTargetSchema determines the schema for a DDL operation.
// (pgddl helper — PG uses RangeVarGetAndCheckCreationNamespace instead)
func (c *Catalog) resolveTargetSchema(schemaName string) (*Schema, error) {Files in catalog/ map to PG's src/backend/commands/:
| pgddl file | PG source |
|---|---|
catalog/tablecmds.go |
src/backend/commands/tablecmds.c |
catalog/schemacmds.go |
src/backend/commands/schemacmds.c |
catalog/view.go |
src/backend/commands/view.c |
catalog/indexcmds.go |
src/backend/commands/indexcmds.c |
catalog/sequence.go |
src/backend/commands/sequence.c |
catalog/typecmds.go |
src/backend/commands/typecmds.c |
catalog/functioncmds.go |
src/backend/commands/functioncmds.c |
catalog/trigger.go |
src/backend/commands/trigger.c |
catalog/comment.go |
src/backend/commands/comment.c |
catalog/dropcmds.go |
src/backend/commands/dropcmds.c |
catalog/alter.go |
src/backend/commands/alter.c |
Infrastructure files (catalog data, OIDs, type resolution, coercion) keep their current names.
Use the same function names as PG (adapted to Go conventions):
| pgddl | PG |
|---|---|
DefineRelation |
DefineRelation |
RemoveRelations |
RemoveRelations |
AlterTable |
AlterTable |
DefineView |
DefineView |
DefineSequence |
DefineSequence |
DefineEnum |
DefineEnum |
DefineDomain |
DefineDomain |
AlterEnum |
AlterEnum |
CreateFunction |
CreateFunction |
CreateTrigger |
CreateTrigger |
CommentObject |
CommentObject |
CreateSchemaCommand |
CreateSchemaCommand |
ExecRenameStmt |
ExecRenameStmt |
DefineIndex |
DefineIndex |
DDL functions accept pgparser's nodes.* types directly. Do NOT create custom Input structs.
// CORRECT: direct consumption of parser AST (matches PG pattern)
func (c *Catalog) DefineRelation(stmt *nodes.CreateStmt, relkind byte) error
// WRONG: custom Input struct (creates unnecessary abstraction)
func (c *Catalog) CreateTable(input CreateTableInput) errorpgparser (github.com/pgplex/pgparser/nodes) provides Go translations of PG's parsenodes.h types:
CreateStmt, DropStmt, AlterTableStmt, ViewStmt, SelectStmt, ColumnDef, TypeName, Constraint, etc.
pgddl may define internal types for catalog state (the stored result):
Relation,Column,Schema,Index,Constraint,Sequence,Trigger— stored catalog objects- Cooked expression types if needed for view type inference — but these are internal processing results, never public API input
Skip (replace with no-ops or simplified logic):
- Physical storage: heap operations, buffer management, WAL, fsync
- Locking: LockRelation, AccessExclusiveLock, etc.
- ACL/permissions: aclcheck_error, pg_class_aclcheck
- System catalog tuple operations: SearchSysCache, heap_form_tuple, CatalogTupleInsert
- Transaction/subtransaction handling
- Event triggers, replication, logical decoding hooks
Do NOT skip (translate faithfully):
- Validation logic (duplicate checks, type compatibility, constraint validation)
- Name resolution (search path, schema qualification)
- Type resolution and coercion
- Dependency tracking (recordDependencyOn)
- Error messages and error codes
- Control flow and branch structure
When translating a PG function:
- Read the PG source in
../postgres/src/backend/commands/ - Identify the logic blocks — validation, resolution, creation, dependency recording
- Translate block by block, preserving the order and branch structure
- Replace storage operations with in-memory catalog updates
- Preserve error messages — use the same error codes and message patterns
- Keep the same variable names where practical (adapted to Go naming:
relname→relName)
Statement dispatch mirrors PG's ProcessUtilitySlow in src/backend/tcop/utility.c:
// ProcessUtility dispatches a DDL statement to the appropriate handler.
//
// pg: src/backend/tcop/utility.c — ProcessUtilitySlow (DDL cases only)
func (c *Catalog) ProcessUtility(stmt nodes.Node) error {
switch s := stmt.(type) {
case *nodes.CreateStmt:
return c.DefineRelation(s, 'r')
case *nodes.ViewStmt:
return c.DefineView(s)
case *nodes.DropStmt:
return c.RemoveRelations(s) // or other Remove* based on RemoveType
// ...
}
}Tests should use real SQL parsed through pgparser, not manually constructed AST nodes:
func TestDefineRelation(t *testing.T) {
c := catalog.New()
stmt := MustParseOne(t, "CREATE TABLE users (id int PRIMARY KEY, name text NOT NULL)")
err := c.ProcessUtility(stmt)
require.NoError(t, err)
// assert catalog state...
}github.com/pgplex/pgparser— parser AST types (the only external dependency for DDL input)- PG source at
../postgres/— reference for translation (not a code dependency)