PostgreSQL Mastery
PostgreSQL Mastery lessons / Module 31: Extensions

How extensions work

Level 6: Design and Security20 min

Extensions package new types, functions, operators, index methods and even background workers into a unit you install with one command. Much of what people love about PostgreSQL (PostGIS, pgvector, pg_stat_statements, TimescaleDB, Citus) is an extension.

This lesson is interactive. Run every example on a real PostgreSQL 18 server in your browser, read EXPLAIN ANALYZE plans, and take the quiz.
Open the interactive lesson
You will learn to
  • Install, inspect, update and remove extensions
  • Tell trusted extensions from superuser-only ones and know what needs shared_preload_libraries
  • Plan extensions for managed services and major upgrades

conceptThe concept

An extension is a control file plus SQL scripts (and often a shared library) on the server. CREATE EXTENSION name runs the install script and records every object it creates as a member of the extension, so DROP EXTENSION removes them together and pg_dump emits just the CREATE EXTENSION line.

  • pg_available_extensions lists what the server has files for; pg_extension (or \dx) what is installed in this database.
  • Versions: ALTER EXTENSION name UPDATE runs the upgrade scripts after new binaries are installed.
  • Trusted extensions (since 13) can be installed by non-superusers with CREATE privilege on the database.
  • Extensions that hook into the server, such as pg_stat_statements, auto_explain or pg_cron, must also be listed in shared_preload_libraries, which needs a restart.

whyWhy it matters

Extensions decide what you can run where. A managed service supports a fixed list; a major-version upgrade needs every extension available for the new version; and a missing extension on a restore target makes pg_restore fail.

Use caseReal-world scenario

Before moving to a managed service, the team runs SELECT extname, extversion FROM pg_extension on every database and checks each one against the provider's supported list. One unsupported extension changes the migration plan.

Try itExamples

Run and edit these in the interactive lesson.

Available and installed extensions
SELECT name, default_version, installed_version, left(comment, 60) AS comment
FROM pg_available_extensions
WHERE name IN ('pg_trgm', 'pg_stat_statements', 'pageinspect', 'vector', 'pgcrypto', 'citext', 'hstore', 'ltree', 'btree_gist', 'amcheck')
ORDER BY installed_version IS NULL, name;
Install one and see what it added
BEGIN;
CREATE EXTENSION citext;
SELECT e.extname, e.extversion, n.nspname AS schema FROM pg_extension e JOIN pg_namespace n ON n.oid = e.extnamespace WHERE e.extname = 'citext';
SELECT count(*) AS member_objects FROM pg_depend
WHERE refobjid = (SELECT oid FROM pg_extension WHERE extname = 'citext') AND deptype = 'e';
ROLLBACK;
Trusted or superuser-only
SELECT DISTINCT name, trusted, superuser
FROM pg_available_extension_versions
WHERE name IN ('citext', 'pgcrypto', 'hstore', 'pageinspect', 'pg_stat_statements', 'vector')
ORDER BY name;
psql view
\dx

internalsUnder the hood

Extension objects are linked to the extension in pg_depend with deptype e. That is how pg_dump knows to skip them and how DROP EXTENSION finds them. Configuration tables an extension wants dumped are registered with pg_extension_config_dump.

Avoid / preferDo this, not that

Avoid
CREATE EXTENSION pgcrypto;
-- installed into whatever schema is first
-- on the search_path
Prefer
CREATE SCHEMA extensions;
CREATE EXTENSION pgcrypto SCHEMA extensions;

A dedicated schema keeps extension objects out of application schemas, makes privileges simple and avoids name clashes with your own functions.

Avoid
-- upgrade binaries, then forget
SELECT extversion FROM pg_extension;  -- still old
Prefer
ALTER EXTENSION vector UPDATE;
SELECT extversion FROM pg_extension WHERE extname = 'vector';

New package files do nothing until ALTER EXTENSION UPDATE runs in each database that uses the extension.

PitfallsCommon mistakes

  • Assuming an extension installed in one database is available in all: extensions are per database.
  • Forgetting shared_preload_libraries for pg_stat_statements: CREATE EXTENSION succeeds but the views error until restart.
  • Upgrading the major version without checking that every extension has a build for the new version.

LimitsWhen not to use it

Avoid extensions with no maintainer, no support on your target platforms, or that duplicate core features (for example uuid-ossp when gen_random_uuid() and uuidv7() are built in).

SecuritySecurity notes

Extensions run code inside the server process; a C extension has the same access as PostgreSQL itself. Only install packages from trusted sources, prefer trusted extensions for application roles, and audit pg_extension regularly. CREATE EXTENSION scripts run with elevated rights, so a writable schema on the search_path during installation is a risk: install into a dedicated schema.

ChallengeTry it yourself

Inside a transaction, install hstore into a new schema ext, then list the schema, version and the number of functions the extension created.

Show one solution
One solution
BEGIN;
CREATE SCHEMA ext;
CREATE EXTENSION hstore SCHEMA ext;
SELECT e.extname, n.nspname, e.extversion,
       (SELECT count(*) FROM pg_depend d JOIN pg_proc p ON p.oid = d.objid
        WHERE d.refobjid = e.oid AND d.deptype = 'e' AND d.classid = 'pg_proc'::regclass) AS functions
FROM pg_extension e JOIN pg_namespace n ON n.oid = e.extnamespace
WHERE e.extname = 'hstore';
ROLLBACK;

InterviewQuestions you should be able to answer

Why does pg_stat_statements need a restart but citext does not?
pg_stat_statements hooks into the executor and allocates shared memory at server start, so its library must be loaded through shared_preload_libraries, which requires a restart. citext only adds a data type, operators and functions; its library is loaded on demand the first time one of its functions is called, so CREATE EXTENSION is enough.
What do you check about extensions before a major upgrade?
The list of installed extensions and versions in every database, whether each has a build for the target version (and on managed services whether it is supported), whether pg_upgrade --check passes, and whether any extension stores data in formats that change (for example PostGIS or TimescaleDB have their own upgrade steps). After the upgrade, run ALTER EXTENSION ... UPDATE in each database.

QuizCheck your understanding

4 questions with instant feedback in the interactive lesson; a wrong answer goes into your review deck.

Go deeperOfficial documentation