Schema Organization
You can put your entire schema in one file, or split it across many. pgmt doesn’t care - it reads all .sql files in your schema directory and figures out the dependencies.
Single File vs Multi-File
Section titled “Single File vs Multi-File”For small projects, one file works fine:
schema/└── schema.sqlFor larger projects, split by object type:
schema/├── 01_schemas/│ └── app.sql├── 02_types/│ ├── priority.sql│ └── status.sql├── 03_tables/│ ├── users.sql│ └── orders.sql├── 04_views/│ └── reports.sql└── 05_functions/ └── helpers.sqlThe numbered prefixes give you alphabetical ordering as a baseline. You can also organize by business domain (auth/, catalog/, orders/) or mix both approaches.
Dependencies with -- require:
Section titled “Dependencies with -- require:”When one file depends on another, declare it explicitly:
-- require: 01_schemas/app.sql, 02_types/user_role.sqlCREATE TABLE app.users ( id SERIAL PRIMARY KEY, email TEXT UNIQUE NOT NULL, role app.user_role DEFAULT 'customer');pgmt loads files in this order:
- Alphabetically by default
- Adjusted by
-- require:declarations - Topologically sorted so dependencies come first
File paths are relative to your schema directory. The .sql extension is optional.
Complete Example
Section titled “Complete Example”Here’s an e-commerce schema split across files:
01_schemas/app.sql:
CREATE SCHEMA app;02_types/order_status.sql:
-- require: 01_schemas/app.sqlCREATE TYPE app.order_status AS ENUM ('pending', 'confirmed', 'shipped', 'delivered');03_tables/users.sql:
-- require: 01_schemas/app.sqlCREATE TABLE app.users ( id SERIAL PRIMARY KEY, email TEXT UNIQUE NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW());03_tables/orders.sql:
-- require: 01_schemas/app.sql, 02_types/order_status.sql, 03_tables/users.sqlCREATE TABLE app.orders ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES app.users(id), status app.order_status DEFAULT 'pending', total DECIMAL(10,2) NOT NULL);03_tables/order_items.sql:
-- require: 03_tables/orders.sqlCREATE TABLE app.order_items ( id SERIAL PRIMARY KEY, order_id INTEGER NOT NULL REFERENCES app.orders(id) ON DELETE CASCADE, product_name TEXT NOT NULL, quantity INTEGER NOT NULL CHECK (quantity > 0), unit_price DECIMAL(10,2) NOT NULL);04_views/order_summary.sql:
-- require: 03_tables/orders.sql, 03_tables/order_items.sql, 03_tables/users.sqlCREATE VIEW app.order_summary ASSELECT o.id as order_id, u.email as customer_email, o.status, o.total, COUNT(oi.id) as item_countFROM app.orders oJOIN app.users u ON o.user_id = u.idLEFT JOIN app.order_items oi ON o.id = oi.order_idGROUP BY o.id, u.email, o.status, o.total;pgmt resolves this to: app.sql → order_status.sql → users.sql → orders.sql → order_items.sql → order_summary.sql
Troubleshooting
Section titled “Troubleshooting”Circular dependency:
Error: Circular dependency detected: A requires B, B requires ARestructure your dependencies or merge the files.
Missing file:
Error: Missing dependency: orders.sql requires missing_file.sqlCheck the path - it’s relative to your schema directory.
Wrong load order:
Error: relation "users" does not existAdd a -- require: statement to the file that needs users.
Debug with dry-run:
pgmt apply --dry-runShows what pgmt will do without touching your database. Use this to verify load order.