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 dumping or creating tables, pgschema produces tables in the following canonical format. All constraints (PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK) are defined as separate named table-level constraints for consistency and explicit naming. Note: When generating migration SQL forALTER TABLE ADD COLUMN operations, single-column constraints may be inlined to reduce the number of statements (see ALTER TABLE documentation).
Simple Table
Complex Table
Identity and Serial Columns
nextval() defaults) to their canonical SERIAL forms (SMALLSERIAL, SERIAL, BIGSERIAL) in CREATE TABLE statements.
Constraint Handling Rules
CREATE TABLE Format - All constraints are defined as separate named table-level constraints:- PRIMARY KEY: Both single-column and composite (
CONSTRAINT table_pkey PRIMARY KEY (col1, ...)) - UNIQUE: Both single-column and composite (
CONSTRAINT table_col_key UNIQUE (col1, ...)) - FOREIGN KEY: Both single-column and composite (
CONSTRAINT table_col_fkey FOREIGN KEY (col1, ...) REFERENCES...) - CHECK: All CHECK constraints (
CONSTRAINT table_col_check CHECK (expression))
- PRIMARY KEY:
ALTER TABLE t ADD COLUMN id INTEGER CONSTRAINT t_pkey PRIMARY KEY - UNIQUE:
ALTER TABLE t ADD COLUMN email TEXT CONSTRAINT t_email_key UNIQUE - FOREIGN KEY:
ALTER TABLE t ADD COLUMN user_id INTEGER CONSTRAINT t_user_id_fkey REFERENCES users(id) - CHECK:
ALTER TABLE t ADD COLUMN age INTEGER CONSTRAINT t_age_check CHECK (age > 0) - Composite constraints still require separate ALTER TABLE ADD CONSTRAINT statements
- 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:
{table}_pkey - UNIQUE:
{table}_{column}_key - FOREIGN KEY:
{table}_{column}_fkey - CHECK:
{table}_{column}_check
- Always uses
CREATE TABLE IF NOT EXISTSfor safety and idempotency - All constraints are table-level with explicit names in CREATE TABLE output
- Constraints are ordered: PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK
- Column elements appear in order: data_type, NOT NULL, DEFAULT, GENERATED…AS IDENTITY
- 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;

