In this lesson, you will learn how to leverage PostgreSQL extension lifecycle hooks to treat schema evolution as a first-class, programmable component of your database deployment.
The Problem with Manual Migrations
Typically, schema migrations are handled by external tools that track version numbers in a metadata table. While effective, this creates a "split brain" where your database schema logic exists outside the database itself. By building migration logic directly into a custom PostgreSQL extension, you can ensure that schema updates are bundled, versioned, and executed consistently alongside your extension's C functions or specialized procedural logic.
Leveraging Extension Hooks
PostgreSQL extensions use control files (.control) to define their versions. When you execute ALTER EXTENSION ... UPDATE, the database engine looks for update scripts (e.g., my_extension--1.0--1.1.sql).
However, for complex migrations—such as transforming data stored in a custom global state or migrating rows between tables while handling dependencies—you can use the SPI (Server Programming Interface) within these scripts to run procedural logic.
Example: Procedural Migration Script
Suppose you need to rename a column and populate it based on a calculation that requires complex row-by-row logic. You can include this logic directly in your versioned update script:
-- migration file: my_extension--1.0--1.1.sql
DO $$
BEGIN
-- Use SPI via dynamic SQL to handle logic that DDL cannot
IF NOT EXISTS (SELECT 1 FROM pg_attribute WHERE attname = 'new_data') THEN
ALTER TABLE my_table ADD COLUMN new_data TEXT;
-- Execute procedural data migration
EXECUTE 'UPDATE my_table SET new_data = compute_complex_value(old_data)';
END IF;
END $$;
Integrating Lifecycle Logic
Because this script is bundled within the extension, the database acts as the single source of truth. When you ship a new version of your extension, you include the update scripts that handle both the DDL (the schema) and the DML (the data transformation). By using procedural blocks (DO $$ ... $$) inside these scripts, you gain the full power of PL/pgSQL, allowing you to wrap migrations in error-handling blocks, perform conditional checks, and ensure that your database schema remains consistent across every deployment.
Key Takeaway: By embedding migration logic within extension update scripts, you consolidate schema changes and data transformations into a single, versioned unit that travels with your database extension.