Skip to content

Latest commit

 

History

History
162 lines (124 loc) · 6.01 KB

File metadata and controls

162 lines (124 loc) · 6.01 KB

pgddl — PostgreSQL DDL Semantic Layer

Core Principle: Translation, Not Implementation

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.

Rules

1. Source Mapping Comments

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) {

2. File Structure Mirrors PG

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.

3. Function Names Mirror PG

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

4. Input Types = pgparser AST Types

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) error

pgparser (github.com/pgplex/pgparser/nodes) provides Go translations of PG's parsenodes.h types: CreateStmt, DropStmt, AlterTableStmt, ViewStmt, SelectStmt, ColumnDef, TypeName, Constraint, etc.

5. Internal Types Are for Catalog State, Not Input

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

6. What to Skip (and What NOT to Skip)

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

7. How to Translate

When translating a PG function:

  1. Read the PG source in ../postgres/src/backend/commands/
  2. Identify the logic blocks — validation, resolution, creation, dependency recording
  3. Translate block by block, preserving the order and branch structure
  4. Replace storage operations with in-memory catalog updates
  5. Preserve error messages — use the same error codes and message patterns
  6. Keep the same variable names where practical (adapted to Go naming: relnamerelName)

8. Utility Dispatch

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
    // ...
    }
}

Testing

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...
}

Dependencies

  • 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)