Chapter sebelumnya memperlihatkan extension sebagai bundel control file, script SQL, dan shared library. Sekarang kita masuk ke praktik pengelolaannya. Pemasangan extension terjadi di dua lapisan yang berbeda: paket harus tersedia di sistem operasi server lebih dulu, baru kemudian CREATE EXTENSION menjalankan script-nya di level SQL. Kedua lapisan itu sering membingungkan pemula — error yang sama sekali tidak informatif muncul bila urutannya terbalik. Chapter ini menutup seluruh siklus hidup: memeriksa ketersediaan, memasang dengan atau tanpa versi tertentu, memperbarui versi, menempatkan objek di schema khusus, dan melepas extension dengan segala konsekuensi CASCADE-nya.
Dua Langkah Pemasangan: Paket OS, Lalu SQL #
Langkah pertama berlangsung di luar PostgreSQL. Di Debian/Ubuntu, extension buatan komunitas PGDG didistribusikan lewat paket postgresql-contrib-17 untuk extension bawaan, atau paket terpisah seperti postgresql-17-postgis-3 untuk PostGIS; di RHEL keluarganya bernama postgresql-contrib. Setelah paket terpasang dan server di-restart bila perlu, control file baru akan terbaca dan extension muncul di katalog. Langkah kedua — CREATE EXTENSION — baru bisa berhasil setelah langkah pertama. File berikut mencoba melompati langkah pertama untuk extension yang paketnya tidak ada di server kursus:
-- pgbuffercache tidak ada di pg_available_extensions server kursus
-- karena paketnya tidak ikut dipasang di sistem operasi.
CREATE EXTENSION pgbuffercache;Output yang Dihasilkan:
psql:error-paket-hilang.sql:3: ERROR: extension "pgbuffercache" is not available
DETAIL: Could not open extension control file "/home/z/my-project/tools/dl/pgdeb/usr/share/postgresql/17/extension/pgbuffercache.control": No such file or directory.
HINT: The extension must first be installed on the system where PostgreSQL is running.Pesan errornya justru paling informatif untuk kasus ini: DETAIL-nya memperlihatkan path control file yang gagal dibuka, dan HINT-nya menyebut tegas bahwa extension harus dipasang lebih dulu di sistem tempat PostgreSQL berjalan — bukan di mesin klien, bukan lewat SQL. Bila suatu saat Anda menemui error serupa untuk PostGIS atau pg_partman, penanganannya satu: pasang paketnya di server, lalu ulangi CREATE EXTENSION.
Memeriksa yang Tersedia vs yang Terpasang #
Dua katalog bekerja bersama di sini: pg_available_extensions berisi semua extension yang bisa dipasang (dibaca dari control file), sedangkan pg_extension berisi yang sedang terpasang di database aktif. Kolom installed_version di view pertama menjembatani keduanya — NULL berarti belum terpasang. Perintah meta \dx di psql menampilkan ringkasan yang sama dalam format siap baca:
-- Extension yang TERPASANG di database ini (katalog pg_extension).
\dx
-- Bentuk SQL-nya: katalog pg_extension berisi extension aktif.
SELECT extname, extversion, extnamespace::regnamespace AS skema
FROM pg_extension
ORDER BY extname;
-- Extension yang TERSEDIA namun belum terpasang: kolom installed_version NULL.
SELECT name, default_version, installed_version
FROM pg_available_extensions
WHERE name IN ('pgcrypto', 'pg_trgm', 'citext', 'amcheck')
ORDER BY name;Output yang Dihasilkan:
List of installed extensions
Name | Version | Schema | Description
---------+---------+------------+------------------------------
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
(1 row)
extname | extversion | skema
---------+------------+------------
plpgsql | 1.0 | pg_catalog
(1 row)
name | default_version | installed_version
----------+-----------------+-------------------
amcheck | 1.4 |
citext | 1.6 |
pg_trgm | 1.6 |
pgcrypto | 1.3 |
(4 rows)Database segar hanya membawa plpgsql — bahasa prosedural bawaan yang memang terpasang otomatis di setiap database. Empat extension lainnya tersedia namun belum aktif: kolom installed_version kosong sampai CREATE EXTENSION dijalankan. Perhatikan juga bahwa katalog ini per database: memasang pgcrypto di satu database tidak memasangnya di database lain, meskipun paketnya sudah ada untuk seluruh server.
CREATE EXTENSION, Versi, dan Pembaruan #
Bentuk paling sederhana memakai versi default dari control file. Untuk skrip migrasi yang bisa dijalankan ulang, tambahkan IF NOT EXISTS; untuk mengunci versi tertentu, sebutkan klausa VERSION. File berikut mempraktikkan ketiganya, lalu menutup dengan ALTER EXTENSION ... UPDATE yang menaikkan amcheck dari versi 1.3 ke 1.4 lewat update path:
-- Pemasangan standar: versi default dari control file.
CREATE EXTENSION pgcrypto;
-- IF NOT EXISTS menjadikan skrip aman dijalankan ulang.
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Katalog pg_extension kini mencatat pgcrypto di database ini.
SELECT extname, extversion, extnamespace::regnamespace AS skema
FROM pg_extension
ORDER BY extname;
-- Mengunci versi lama secara eksplisit, lalu memperbaruinya.
CREATE EXTENSION amcheck VERSION '1.3';
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'amcheck';
-- UPDATE mengikuti update path: 1.3 -> 1.4.
ALTER EXTENSION amcheck UPDATE;
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'amcheck';
-- amcheck selesai dipakai untuk demonstrasi; dilepas kembali.
DROP EXTENSION amcheck;Output yang Dihasilkan:
CREATE EXTENSION
CREATE EXTENSION
extname | extversion | skema
----------+------------+------------
pgcrypto | 1.3 | public
plpgsql | 1.0 | pg_catalog
(2 rows)
CREATE EXTENSION
extname | extversion
---------+------------
amcheck | 1.3
(1 row)
ALTER EXTENSION
extname | extversion
---------+------------
amcheck | 1.4
(1 row)
DROP EXTENSIONMari kita bedah perilaku versinya baris per baris:
CREATE EXTENSION pgcryptomemasang versi 1.3 — nilaidefault_versiondari control file yang kita baca di chapter sebelumnya. Katalogpg_extensionmencatat pasangan nama dan versi ini.CREATE EXTENSION IF NOT EXISTS pgcryptotidak mengubah apa pun; psql tetap mencetak tagCREATE EXTENSION, sementaraNOTICE: extension "pgcrypto" already exists, skippingditulis ke stderr sehingga tidak tampak di blok output. Inilah bentuk yang tepat untuk skrip migrasi.CREATE EXTENSION amcheck VERSION '1.3'memaksa versi lama secara eksplisit — pola yang berguna saat aplikasi belum kompatibel dengan versi terbaru.ALTER EXTENSION amcheck UPDATEmencari update path dari 1.3 menuju versi tertinggi (fileamcheck--1.3--1.4.sql), menjalankannya, dan memperbarui katalog menjadi 1.4.
Menempatkan Extension di Schema Khusus #
Secara default objek extension jatuh ke schema public. Server produksi yang rapi sering memilih schema tersendiri — misalnya ekstensi — agar objek tambahan mudah diaudit, di-backup, dan tidak berbaur dengan tabel aplikasi. Klausa SCHEMA pada CREATE EXTENSION mengatur penempatan itu, dengan satu konsekuensi: nama objeknya kini harus memenuhi aturan search_path seperti objek schema lain:
-- Schema khusus menjaga objek extension tetap terkelompok dan mudah diaudit.
CREATE SCHEMA ekstensi;
-- Objek pg_trgm akan dibuat di dalam schema ekstensi, bukan public.
CREATE EXTENSION pg_trgm SCHEMA ekstensi;
SELECT extname, extversion, extnamespace::regnamespace AS skema
FROM pg_extension
WHERE extname IN ('pg_trgm', 'plpgsql')
ORDER BY extname;
-- Di luar search_path, pemanggilan harus menyebut nama skema penuh.
SELECT ekstensi.similarity('budi', 'budi santoso') AS skor_skema;
-- Masukkan schema ke search_path, lalu pemanggilan singkat kembali bekerja.
SET search_path = public, ekstensi;
SELECT similarity('budi', 'budi santoso') AS skor_search_path;
-- Kembalikan search_path ke nilai bawaan sesi.
RESET search_path;
-- Bersih-bersih: lepas extension, lalu schema kosongnya.
DROP EXTENSION pg_trgm;
DROP SCHEMA ekstensi;Output yang Dihasilkan:
CREATE SCHEMA
CREATE EXTENSION
extname | extversion | skema
---------+------------+------------
pg_trgm | 1.6 | ekstensi
plpgsql | 1.0 | pg_catalog
(2 rows)
skor_skema
------------
0.3846154
(1 row)
SET
skor_search_path
------------------
0.3846154
(1 row)
RESET
DROP EXTENSION
DROP SCHEMAKatalog mengonfirmasi penempatan: pg_trgm kini bernaung di schema ekstensi, bukan public. Fungsi similarity() kemudian dipanggil dua cara — dengan prefiks ekstensi.similarity(...) yang selalu bekerja, dan tanpa prefiks yang hanya bekerja setelah SET search_path memasukkan schema ekstensi. Tanpa keduanya, pemanggilan singkat gagal dengan pesan function similarity(unknown, unknown) does not exist — bukan karena fungsinya hilang, melainkan karena PostgreSQL tidak mencarinya di schema itu. Extension relocatable = true (seperti pg_trgm) bahkan bisa dipindahkan setelah terpasang lewat ALTER EXTENSION ... SET SCHEMA.
Melepas Extension: RESTRICT dan CASCADE #
DROP EXTENSION adalah kebalikan pemasangan — dengan satu pintu pengaman penting. Secara default perintah berjalan mode RESTRICT: bila masih ada objek yang bergantung pada milik extension, pelepasan ditolak. Contoh berikut memasang citext, memakai tipenya sebagai kolom, lalu membuktikan penolakan itu:
-- citext dipasang kembali untuk contoh ketergantungan.
CREATE EXTENSION citext;
CREATE TABLE pelanggan_ci (
id integer PRIMARY KEY,
email citext NOT NULL UNIQUE
);
INSERT INTO pelanggan_ci VALUES
(1, '[email protected]'),
(2, '[email protected]');
-- Pencocokan citext mengabaikan besar-kecil huruf.
SELECT id, email
FROM pelanggan_ci
WHERE email = '[email protected]'
ORDER BY id;Output yang Dihasilkan:
CREATE EXTENSION
CREATE TABLE
INSERT 0 2
id | email
----+------------------
1 | [email protected]
(1 row)-- Kolom email bertipe citext masih bergantung pada extension ini,
-- sehingga pelepasan standar (RESTRICT) ditolak server.
DROP EXTENSION citext;Output yang Dihasilkan:
psql:lepas-gagal.sql:3: ERROR: cannot drop extension citext because other objects depend on it
DETAIL: column email of table pelanggan_ci depends on type citext
HINT: Use DROP ... CASCADE to drop the dependent objects too.Error tersebut adalah pengaman yang bekerja semestinya: kolom email akan kehilangan tipenya bila citext hilang, jadi PostgreSQL menolak dan menawarkan CASCADE lewat HINT. File berikut menempuh jalan itu:
-- CASCADE melepaskan dulu objek yang bergantung, lalu extension-nya.
-- Server mengirim NOTICE "drop cascades to column email of table pelanggan_ci".
DROP EXTENSION citext CASCADE;
-- Struktur setelah CASCADE: kolom email ikut hilang, tabel dan id tetap hidup.
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'pelanggan_ci'
ORDER BY ordinal_position;
-- Data pada kolom yang selamat tidak terganggu.
SELECT id
FROM pelanggan_ci
ORDER BY id;Output yang Dihasilkan:
DROP EXTENSION
column_name | data_type
-------------+-----------
id | integer
(1 row)
id
----
1
2
(2 rows)Perhatikan presisi perilaku CASCADE: PostgreSQL melepas objek bergantung seminimal mungkin — di sini kolom email beserta constraint UNIQUE-nya — sementara tabel pelanggan_ci dan kolom id tetap hidup lengkap dengan datanya. NOTICE yang menyebutkan objek yang dihapus tercetak ke stderr. Bila tipe extension dipakai banyak tabel, efeknya berlipat: setiap kolom yang bersangkutan lenyap, dan tabel yang kehilangan seluruh kolomnya ikut dihapus. Periksa pg_depend atau jalankan DROP EXTENSION tanpa CASCADE lebih dulu untuk melihat daftar objek yang akan terkena dampak.
Siklus hidup extension kini lengkap: tersedia, terpasang, terperbarui, terkelola schemanya, dan terlepas dengan aman. Ekosistem yang sesungguhnya dimulai dari extension pertama yang dipasang untuk alasan nyata — dan tidak ada kandidat yang lebih nyata daripada pg_stat_statements, profiler query standar de facto yang akan kita aktifkan di chapter berikutnya.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
cek-ketersediaan.sql
Unduh berkas-- Extension yang TERPASANG di database ini (katalog pg_extension).
\dx
-- Bentuk SQL-nya: katalog pg_extension berisi extension aktif.
SELECT extname, extversion, extnamespace::regnamespace AS skema
FROM pg_extension
ORDER BY extname;
-- Extension yang TERSEDIA namun belum terpasang: kolom installed_version NULL.
SELECT name, default_version, installed_version
FROM pg_available_extensions
WHERE name IN ('pgcrypto', 'pg_trgm', 'citext', 'amcheck')
ORDER BY name;
error-paket-hilang.sql
Unduh berkas-- pgbuffercache tidak ada di pg_available_extensions server kursus
-- karena paketnya tidak ikut dipasang di sistem operasi.
CREATE EXTENSION pgbuffercache;
ketergantungan.sql
Unduh berkas-- citext dipasang kembali untuk contoh ketergantungan.
CREATE EXTENSION citext;
CREATE TABLE pelanggan_ci (
id integer PRIMARY KEY,
email citext NOT NULL UNIQUE
);
INSERT INTO pelanggan_ci VALUES
(1, '[email protected]'),
(2, '[email protected]');
-- Pencocokan citext mengabaikan besar-kecil huruf.
SELECT id, email
FROM pelanggan_ci
WHERE email = '[email protected]'
ORDER BY id;
lepas-cascade.sql
Unduh berkas-- CASCADE melepaskan dulu objek yang bergantung, lalu extension-nya.
-- Server mengirim NOTICE "drop cascades to column email of table pelanggan_ci".
DROP EXTENSION citext CASCADE;
-- Struktur setelah CASCADE: kolom email ikut hilang, tabel dan id tetap hidup.
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'pelanggan_ci'
ORDER BY ordinal_position;
-- Data pada kolom yang selamat tidak terganggu.
SELECT id
FROM pelanggan_ci
ORDER BY id;
lepas-gagal.sql
Unduh berkas-- Kolom email bertipe citext masih bergantung pada extension ini,
-- sehingga pelepasan standar (RESTRICT) ditolak server.
DROP EXTENSION citext;
pasang-dan-versi.sql
Unduh berkas-- Pemasangan standar: versi default dari control file.
CREATE EXTENSION pgcrypto;
-- IF NOT EXISTS menjadikan skrip aman dijalankan ulang.
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Katalog pg_extension kini mencatat pgcrypto di database ini.
SELECT extname, extversion, extnamespace::regnamespace AS skema
FROM pg_extension
ORDER BY extname;
-- Mengunci versi lama secara eksplisit, lalu memperbaruinya.
CREATE EXTENSION amcheck VERSION '1.3';
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'amcheck';
-- UPDATE mengikuti update path: 1.3 -> 1.4.
ALTER EXTENSION amcheck UPDATE;
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'amcheck';
-- amcheck selesai dipakai untuk demonstrasi; dilepas kembali.
DROP EXTENSION amcheck;
skema.sql
Unduh berkas-- Schema khusus menjaga objek extension tetap terkelompok dan mudah diaudit.
CREATE SCHEMA ekstensi;
-- Objek pg_trgm akan dibuat di dalam schema ekstensi, bukan public.
CREATE EXTENSION pg_trgm SCHEMA ekstensi;
SELECT extname, extversion, extnamespace::regnamespace AS skema
FROM pg_extension
WHERE extname IN ('pg_trgm', 'plpgsql')
ORDER BY extname;
-- Di luar search_path, pemanggilan harus menyebut nama skema penuh.
SELECT ekstensi.similarity('budi', 'budi santoso') AS skor_skema;
-- Masukkan schema ke search_path, lalu pemanggilan singkat kembali bekerja.
SET search_path = public, ekstensi;
SELECT similarity('budi', 'budi santoso') AS skor_search_path;
-- Kembalikan search_path ke nilai bawaan sesi.
RESET search_path;
-- Bersih-bersih: lepas extension, lalu schema kosongnya.
DROP EXTENSION pg_trgm;
DROP SCHEMA ekstensi;