The plan command generates a migration plan to apply a desired schema state to a target database schema. It compares the desired state (from a file) with the current state of a specific schema and shows what changes would be applied.
The plan command follows infrastructure-as-code principles similar to Terraform:
Read the desired state from a SQL file (with include directive support)
Apply the desired state SQL to a temporary PostgreSQL instance (embedded by default, or external via --plan-* flags)
Connect to the target database and analyze current state of the specified schema
Compare the two states
Generate a detailed migration plan with proper dependency ordering
Display the plan without making any changes
By default, pgschema uses an embedded PostgreSQL instance to validate your desired state SQL. For schemas using PostgreSQL extensions or cross-schema references, you can use an external database instead. See External Plan Database for details.
By default, the plan command uses an embedded PostgreSQL instance to validate your desired state SQL. For schemas that require PostgreSQL extensions or have cross-schema references, you can provide an external database. See External Plan Database for complete documentation.
You can exclude specific database objects from migration planning using a .pgschemaignore file. See Ignore (.pgschemaignore) for complete documentation.
pgschema plan --host localhost --db myapp --user postgres --file schema.sql
Copy
Plan: 2 to add, 1 to modify, 1 to drop.Summary by type: tables: 2 to add, 1 to modify, 1 to drop indexes: 1 to add functions: 1 to addTables: + users + posts ~ products + discount_rate (column) - old_price (column) - legacy_dataIndexes: + idx_users_emailFunctions: + update_timestamp()Transaction: trueDDL to be executed:--------------------------------------------------CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(255) UNIQUE);CREATE TABLE IF NOT EXISTS posts ( id SERIAL PRIMARY KEY, title TEXT NOT NULL, user_id INTEGER REFERENCES users(id));CREATE INDEX idx_users_email ON users(email);CREATE FUNCTION update_timestamp() RETURNS TRIGGER AS $$BEGIN NEW.updated_at = NOW(); RETURN NEW;END;$$ LANGUAGE plpgsql;ALTER TABLE products ADD COLUMN discount_rate numeric(5,2);ALTER TABLE products DROP COLUMN old_price;DROP TABLE legacy_data;
CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(255) UNIQUE);CREATE TABLE IF NOT EXISTS posts ( id SERIAL PRIMARY KEY, title TEXT NOT NULL, user_id INTEGER REFERENCES users(id));CREATE INDEX idx_users_email ON users(email);CREATE FUNCTION update_timestamp() RETURNS TRIGGER AS $$BEGIN NEW.updated_at = NOW(); RETURN NEW;END;$$ LANGUAGE plpgsql;ALTER TABLE products ADD COLUMN discount_rate numeric(5,2);ALTER TABLE products DROP COLUMN old_price;DROP TABLE legacy_data;
# Generate plan and save for auditDATE=$(date +%Y%m%d_%H%M%S)pgschema plan \ --host localhost \ --db myapp \ --user postgres \ --file schema.sql \ --output-sql "migrations/plan_${DATE}.sql"
You can generate multiple output formats simultaneously:
Copy
# Save all formats for comprehensive documentationpgschema plan \ --host localhost \ --db myapp \ --user postgres \ --file schema.sql \ --output-human plan.txt \ --output-json plan.json \ --output-sql migration.sql# Display human-readable format and save JSON for automationpgschema plan \ --host localhost \ --db myapp \ --user postgres \ --file schema.sql \ --output-human stdout \ --output-json automation/plan.json \ --no-color
Note: Only one output format can use stdout. If no output flags are specified, the command defaults to human-readable output to stdout with colors enabled.