Syntax
CREATE TABLE
features:
- Schema-qualified names: Tables can be created in specific schemas
- Columns:
- All PostgreSQL data types including user-defined types
- NULL/NOT NULL constraints
- DEFAULT values with expressions and function calls
- IDENTITY columns with GENERATED ALWAYS or BY DEFAULT
- Serial types (SMALLSERIAL, SERIAL, BIGSERIAL)
- Constraints:
- PRIMARY KEY (single or composite)
- UNIQUE constraints (single or composite)
- FOREIGN KEY references with referential actions (CASCADE, RESTRICT, SET NULL, SET DEFAULT)
- CHECK constraints with arbitrary expressions
- DEFERRABLE constraints with INITIALLY DEFERRED option
- Partitioning: PARTITION BY RANGE, LIST, or HASH
- Row-level security: RLS policies (handled separately)
- Indexes: Created via separate CREATE INDEX statements
- Triggers: Created via separate CREATE TRIGGER statements
Canonical Format
When generating migration SQL, pgschema produces tables in the following canonical format. pgschema intelligently decides when to inline constraints with column definitions versus defining them as separate table constraints.Simple Table with Inline Constraints
Complex Table with Mixed Inline and Separate Constraints
Identity and Serial Columns
nextval()
defaults) to their canonical SERIAL forms (SMALLSERIAL, SERIAL, BIGSERIAL) in CREATE TABLE statements.
Constraint Handling Rules
Inline Constraints (Column-Level) - Added directly to the column definition:- PRIMARY KEY: Single-column primary keys only (
id INTEGER PRIMARY KEY
) - UNIQUE: Single-column unique constraints only (
email VARCHAR(255) UNIQUE
) - FOREIGN KEY: Single-column foreign key references only (
user_id INTEGER REFERENCES users(id)
) - CHECK: Single-column check constraints only (
age INTEGER CHECK (age >= 0)
)
- PRIMARY KEY: Multi-column (composite) primary keys (
CONSTRAINT pk_name PRIMARY KEY (col1, col2)
) - UNIQUE: Multi-column unique constraints (
CONSTRAINT uk_name UNIQUE (col1, col2)
) - FOREIGN KEY: Multi-column foreign key references (
CONSTRAINT fk_name FOREIGN KEY (col1, col2) REFERENCES...
) - CHECK: Table-level check constraints involving multiple columns or complex logic (
CONSTRAINT chk_name CHECK (col1 <= col2)
)
- Column name and data type
- NOT NULL (omitted if PRIMARY KEY, IDENTITY, or SERIAL)
- DEFAULT value (omitted for SERIAL/IDENTITY columns)
- GENERATED…AS IDENTITY
- PRIMARY KEY (single-column only)
- UNIQUE (single-column only, omitted if PRIMARY KEY present)
- REFERENCES (single-column foreign key)
- CHECK (single-column check constraints)
- Always uses
CREATE TABLE IF NOT EXISTS
for safety and idempotency - Columns with inline constraints: single-column PRIMARY KEY, UNIQUE, REFERENCES, and CHECK
- Table-level constraints: multi-column PRIMARY KEY, UNIQUE, FOREIGN KEY, and complex CHECK constraints
- Constraints are ordered: PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK (both inline and separate)
- Column elements appear in order: data_type, PRIMARY KEY, IDENTITY, DEFAULT, NOT NULL, UNIQUE, REFERENCES, CHECK
- SERIAL types are normalized to uppercase canonical forms (SMALLSERIAL, SERIAL, BIGSERIAL)
- Data types include appropriate precision/scale modifiers (e.g., VARCHAR(255), NUMERIC(10,2))
- Schema prefixes are stripped from user-defined types when they match the target schema
- Foreign key constraints include full referential action specifications when present
- IDENTITY columns preserve their generation mode and options
- NOT NULL is omitted for PRIMARY KEY, IDENTITY, and SERIAL columns (implicit)
- DEFAULT is omitted for SERIAL and IDENTITY columns
- CHECK constraints are simplified to developer-friendly format (e.g.,
IN('val1', 'val2')
instead of= ANY (ARRAY[...])
) - Partitioned tables include the PARTITION BY clause
- Proper indentation with 4 spaces for readability
- All table creation operations can run within transactions
- For DROP operations:
DROP TABLE IF EXISTS table_name CASCADE;