Skip to content

Hooks

Hooks let you run your own PostgreSQL SQL at four controlled points in the migration.

PhaseWhen it runsTypical use
before_dataAfter table creation, before data COPYCreate extensions, helper functions, or temporary settings
after_dataAfter data COPY, before validation and constraintsANALYZE, data cleanup, normalization
before_fkAfter PKs and indexes, before FK creationOrphan cleanup, data fixes that must happen before foreign keys
after_allAfter FKs, sequences, and optional triggersViews, materialized views, validation queries, application-specific finishing work
[hooks]
before_data = ["sql/extensions.sql"]
after_data = ["sql/analyze.sql"]
before_fk = ["sql/cleanup.sql"]
after_all = ["sql/views.sql", "sql/validate.sql"]

Files run in the order listed within each phase.

Hook file paths are resolved relative to the directory containing migration.toml, not the current shell working directory.

Example:

project/
config/
migration.toml
sql/
before_data.sql

If migration.toml references sql/before_data.sql, pgferry will find that file correctly no matter where you run the command from.

All occurrences of {{schema}} are replaced with the configured PostgreSQL schema name before execution.

ANALYZE {{schema}};

If schema = "app", pgferry executes ANALYZE app;.

This is a plain string replacement, not a SQL parser, so keep quoted-schema edge cases in mind if you use unusual schema names.

Each hook file is split on ; and executed statement by statement. pgferry handles:

  • line comments with --
  • block comments with /* ... */
  • nested block comments
  • quoted strings and quoted identifiers
  • PostgreSQL dollar-quoted blocks such as $$...$$ and $tag$...$tag$
CREATE EXTENSION IF NOT EXISTS pgcrypto;
ANALYZE {{schema}};
DELETE FROM {{schema}}.child c
WHERE c.parent_id IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM {{schema}}.parent p
WHERE p.id = c.parent_id
);
CREATE OR REPLACE VIEW {{schema}}.active_users AS
SELECT id, email
FROM {{schema}}.users
WHERE deleted_at IS NULL;
PhaseFullschema_onlydata_only
before_dataYesNoYes
after_dataYesNoYes
before_fkYesYesNo
after_allYesYesYes

Use hooks when pgferry intentionally reports but does not recreate something automatically:

  • views
  • functions or procedures
  • source triggers
  • custom validation SQL
  • data repairs specific to your application

Use Migration Pipeline to see exactly where each phase lands in the full run.