Savvi Studio

Type Introspection

Purpose: How codegen discovers PostgreSQL types and functions
Last Updated: 2024-11-28

Overview

The introspection layer queries PostgreSQL system catalogs (pg_catalog) to discover types, functions, and their relationships. This information drives code generation.

Architecture

pg_catalog (PostgreSQL system tables)
  ↓ queries
IntrospectionBootstrap
  ↓ creates
TypeCatalog + FunctionCatalog
  ↓ provides
Type and Function metadata
  ↓ used by
Schema Generation + Function Generation

Key Classes

IntrospectionBootstrap

Purpose: Entry point for introspection process

Key Methods:

  • connect() - Establish database connection
  • loadSchema(name) - Load schema information
  • getTypeCatalog() - Get type catalog
  • getFunctionCatalog() - Get function catalog

TypeCatalog

Purpose: Manage PostgreSQL types and dependencies

Key Methods:

  • loadSchema(name) - Load types from schema
  • getType(oid) - Get type by OID
  • getTypeByName(schema, name) - Get type by qualified name
  • getDependencies(type) - Get type dependencies
  • topologicalSort() - Sort types by dependencies
  • getStats() - Get catalog statistics

FunctionCatalog

Purpose: Manage PostgreSQL functions

Key Methods:

  • loadSchema(name) - Load functions from schema
  • getFunctions() - Get all functions
  • getFunction(name) - Get function by name
  • getFunctionsByReturnType(oid) - Find functions by return type

Type Discovery

Base Types

Query pg_type for built-in types (int4, int8, text, bool, etc.)

Enum Types

Query for enum types and their values using pg_enum join

Composite Types

Query for composite types and attributes using pg_attribute join

Domain Types

Query for domain types with constraints using pg_constraint join

Array Types

Query for array types and their element types

Function Discovery

Function Metadata

Query pg_proc for:

  • Function name and schema
  • Parameter count and types
  • Parameter names and modes
  • Return type
  • Documentation comments

Parameter Information

Extract parameter details including:

  • Parameter names
  • Parameter types (OIDs)
  • Parameter modes (IN, OUT, INOUT)

Return Type Information

Determine return type semantics:

  • Single value vs SETOF
  • Base type vs composite
  • Table-returning functions

Dependency Resolution

Type Dependencies

Build dependency graph:

  • Composites depend on field types
  • Arrays depend on element types
  • Domains depend on base types
  • Ranges depend on subtypes

Topological Sort

Order types for generation so dependencies are generated first

Circular Dependencies

Handle circular references using forward declarations and z.lazy()

Type Categories

Enumeration

enum TypeCategory {
  BASE = 'b',        // Base type
  COMPOSITE = 'c',   // Composite (record)
  DOMAIN = 'd',      // Domain (constrained type)
  ENUM = 'e',        // Enum
  PSEUDO = 'p',      // Pseudo-type
  RANGE = 'r'        // Range type
}

Type Metadata

interface Type {
  oid: number;
  name: string;
  schema: string;
  category: TypeCategory;
  length: number;
  byValue: boolean;
  // Category-specific fields
  fields?: CompositeField[];      // for composite
  values?: string[];              // for enum
  baseTypeOid?: number;          // for domain
  elementOid?: number;           // for array
  constraint?: string;           // for domain
}

Performance Optimization

Batch Queries

Load multiple types in one query using ANY($1::oid[])

Caching

Cache introspection results to avoid redundant queries

Filtering

Only load types from specified schemas

Examples

For detailed code examples, see:

  • Introspection Examples - Complete code examples
    • Bootstrap usage
    • SQL queries
    • Dependency resolution
    • Performance optimization
    • Error handling

Key Takeaway: Introspection discovers the database schema structure, enabling type-safe code generation.