Understanding pg_available_extensions vs Installed Extensions: Catalog State vs Runtime Reality
The distinction between pg_available_extensions and pg_extension defines the operational boundary between filesystem availability and database runtime state. Misinterpreting these system catalogs triggers silent CI/CD pipeline failures, broken dependency chains, and unsafe in-place upgrades. This reference isolates exact diagnostic patterns, version reconciliation workflows, and production-safe automation strategies for PostgreSQL DBAs, platform engineers, and database SREs.
Catalog vs. Runtime at a Glance
pg_available_extensions reflects what can be installed (filesystem candidates), while pg_extension reflects what is installed (runtime state).
flowchart LR
subgraph FS["Filesystem candidates"]
A1["control files<br/>pg_config --sharedir/extension"] --> A[("pg_available_extensions")]
end
subgraph RT["Database runtime state"]
B[("pg_extension")] --> B1["loaded library<br/>+ catalog objects"]
end
A -- "CREATE EXTENSION" --> B
B -. "reconcile / drift check" .-> A
The Operational Boundary: Filesystem Candidates vs. Database Reality
pg_available_extensions is a read-only system view that parses the directory returned by pg_config --sharedir/extension/ at server startup or on-demand. It reads .control files to report candidate versions, relocatability flags, default schemas, and installation comments. The view is populated dynamically and does not require a restart when new .control files are dropped into the directory. For a deeper breakdown of how PostgreSQL resolves these control files during initialization, see PostgreSQL Extension Architecture & Lifecycle Fundamentals.
pg_extension is a persistent system catalog that tracks extensions actively loaded in the current database. It binds each extension to a specific schema, owner, exact version string, and configuration parameters. When an extension is created, PostgreSQL records its state here and loads the corresponding shared library from the directory returned by pg_config --pkglibdir. Conflating these two catalogs during deployment orchestration produces false-positive assertions, unmet runtime dependencies, and phantom upgrade paths.
Diagnostic Patterns & State Reconciliation
To audit the delta between available and installed states without relying on external tooling, execute a targeted reconciliation query. The following pattern surfaces version drift, missing installations, and alignment status in a single pass:
SELECT
a.name AS available_ext,
a.default_version AS available_ver,
e.extname AS installed_ext,
e.extversion AS installed_ver,
CASE
WHEN e.extname IS NULL THEN 'NOT_INSTALLED'
WHEN a.default_version != e.extversion THEN 'VERSION_DRIFT'
ELSE 'ALIGNED'
END AS state
FROM pg_available_extensions a
LEFT JOIN pg_extension e ON a.name = e.extname
ORDER BY a.name;
This query surfaces version drift before it triggers CREATE EXTENSION or ALTER EXTENSION UPDATE failures. For dependency-heavy stacks, cross-referencing this output against Dependency Tree Analysis prevents cascading ERROR: extension "X" requires extension "Y" during automated provisioning.
Critical Implementation Notes:
- PostgreSQL treats version strings as
text. Lexicographical comparison (!=) works for aligned versions but fails for semantic versioning edge cases (e.g.,1.10.0vs1.9.0). Precise semver comparison requires application-level parsing or thepg_semverextension. pg_available_extensionsis cluster-wide in availability but database-scoped in installation. An extension available in the catalog can be installed in multiple databases independently, each maintaining its ownpg_extensionrow.- The official documentation for pg_available_extensions confirms that the view lists every extension whose
.controlfile can be parsed; an extension whose control file is unreadable, malformed, or lacks adefault_versionis omitted.
Edge Cases & Step-by-Step Resolution
Missing .control Files
pg_available_extensions omits extensions even if shared libraries exist in the pkglibdir. This occurs when package managers install binaries but fail to deploy control files, or when custom extensions are compiled with incorrect PG_CONFIG paths.
Resolution:
- Verify filesystem state:
ls -la "$(pg_config --sharedir)/extension/"*.control - Confirm the running server’s
pg_config --sharedirmatches the package manager’s installation prefix. - If files exist but are ignored, check permissions (
chmod 644) and ensure the.controlfile contains validcomment,default_version, andmodule_pathnamekeys.
Version Pinning Drift
If pg_extension.extversion shows 1.2.1 but pg_available_extensions.default_version shows 1.3.0, automatic upgrade commands will fail if the intermediate migration script (extension--1.2.1--1.3.0.sql) is absent.
Resolution:
- Query
pg_available_extension_versionsto inspect the exact upgrade path:SELECT version, superuser, relocatable, schema, requires FROM pg_available_extension_versions WHERE name = 'extension_name' ORDER BY version; - Apply explicit version targeting:
ALTER EXTENSION name UPDATE TO '1.3.0'; - If the path is broken, manually execute missing SQL scripts within a transaction, then run
ALTER EXTENSION name UPDATE TO '1.3.0';to sync the catalog. Refer to ALTER EXTENSION for transactional safety guarantees.
Schema Relocation Traps
Extensions marked relocatable = true in their .control file can be moved post-installation. However, pg_extension.extnamespace locks the schema at runtime. Attempting ALTER EXTENSION SET SCHEMA fails if the extension contains non-relocatable objects (e.g., hardcoded schema references in SQL functions).
Resolution:
- Check relocatability:
SELECT default_version, relocatable FROM pg_available_extensions WHERE name = 'ext_name'; - If
relocatable = false, schema relocation requires a fullDROP EXTENSIONfollowed byCREATE EXTENSION ... SCHEMA new_schema;. Ensure dependent objects are dropped first or usepg_dump/pg_restorewith schema remapping.
Production-Safe Automation Strategies
Automating extension lifecycle management requires pre-flight validation, idempotent execution, and strict error boundaries.
Pre-Flight Validation Block
Wrap extension provisioning in a DO $$ ... $$ block that validates state before execution:
DO $$
DECLARE
v_available_ver TEXT;
v_installed_ver TEXT;
BEGIN
SELECT default_version INTO v_available_ver
FROM pg_available_extensions WHERE name = 'my_extension';
SELECT extversion INTO v_installed_ver
FROM pg_extension WHERE extname = 'my_extension';
IF v_available_ver IS NULL THEN
RAISE EXCEPTION 'Extension my_extension not found in filesystem catalog';
ELSIF v_installed_ver IS NULL THEN
EXECUTE format('CREATE EXTENSION my_extension VERSION %L', v_available_ver);
ELSIF v_installed_ver != v_available_ver THEN
EXECUTE format('ALTER EXTENSION my_extension UPDATE TO %L', v_available_ver);
END IF;
END $$;
Idempotent CI/CD Integration
- State-First Approach: Always query
pg_extensionbefore attemptingCREATEorUPDATE. Never assume filesystem presence implies runtime readiness. - Transaction Wrapping: Execute all extension operations inside explicit transactions. PostgreSQL rolls back catalog changes on failure, leaving the database in a consistent state.
- Version Pinning in IaC: Terraform, Ansible, or Kubernetes operators should pin exact versions (
VERSION '1.3.0') rather than relying ondefault_version. This prevents uncontrolled upgrades during package manager updates. - Audit Logging: Capture
pg_available_extensionssnapshots pre-deployment andpg_extensionstates post-deployment. Diff outputs serve as immutable proof of compliance and rollback baselines.
Conclusion
The boundary between pg_available_extensions and pg_extension is not merely academic; it is the fault line between predictable deployments and production incidents. By treating filesystem availability as a candidate state and runtime catalogs as the source of truth, teams can eliminate false-positive assertions, resolve version drift deterministically, and automate extension lifecycles with surgical precision. Rigorous pre-flight validation, explicit version targeting, and strict adherence to PostgreSQL’s transactional guarantees form the foundation of resilient database platform engineering.