PostgreSQL bawaan sudah kaya: puluhan tipe data, ratusan fungsi, lima jenis index. Tetapi cepat atau lambat kita menemui kebutuhan yang berada di luar paket inti — menghitung kemiripan teks, meng-hash password, menelusuri database tetangga, mengelola data geospasial. Jawaban PostgreSQL untuk kebutuhan itu bukan plugin eksternal yang dipasang di sisi aplikasi, melainkan extension: bundel objek database yang dipasang lewat satu perintah SQL dan langsung menjadi bagian dari server itu sendiri. Section ini membahas mekanisme extension dari dasarnya, lalu membedah extension yang paling sering dipakai di produksi — mulai dari profiler pg_stat_statements sampai raksasa geospasial PostGIS. Chapter pertama ini menjawab pertanyaan paling mendasar: apa sebenarnya yang dipasang oleh CREATE EXTENSION, dan bagaimana PostgreSQL melacaknya.
Extension sebagai Bundel Tiga Komponen #
Sebuah extension bukan satu objek, melainkan paket berisi tiga komponen yang terpasang di filesystem server: sebuah control file (metadata: nama, versi default, apakah trusted), script SQL (perintah CREATE FUNCTION, CREATE TYPE, dan kawan-kawan yang dijalankan saat instalasi), dan — untuk extension yang butuh kode C — sebuah shared library (file .so) berisi implementasi aslinya. Perintah CREATE EXTENSION tidak mengunduh apa pun; perintah itu membaca control file, menjalankan script versi yang diminta, lalu mencatat hasilnya ke katalog. File berikut memeriksa ketiga komponen fisik milik pgcrypto di server kursus:
#!/usr/bin/env bash
# Lokasi komponen fisik extension pgcrypto pada server kursus.
EXTDIR=/home/z/my-project/tools/dl/pgdeb/usr/share/postgresql/17/extension
LIBDIR=/home/z/my-project/tools/dl/pgdeb/usr/lib/postgresql/17/lib
echo "== SHAREDIR/extension: control file + script SQL =="
ls "$EXTDIR" | grep '^pgcrypto'
echo ""
echo "== libdir: shared library =="
ls "$LIBDIR" | grep '^pgcrypto'
echo ""
echo "== Isi pgcrypto.control =="
cat "$EXTDIR/pgcrypto.control"Output yang Dihasilkan:
== SHAREDIR/extension: control file + script SQL ==
pgcrypto--1.0--1.1.sql
pgcrypto--1.1--1.2.sql
pgcrypto--1.2--1.3.sql
pgcrypto--1.3.sql
pgcrypto.control
== libdir: shared library ==
pgcrypto.so
== Isi pgcrypto.control ==
# pgcrypto extension
comment = 'cryptographic functions'
default_version = '1.3'
module_pathname = '$libdir/pgcrypto'
relocatable = true
trusted = trueMari kita bedah isi direktori tersebut:
pgcrypto.control— control file yang dibaca PostgreSQL saatCREATE EXTENSION pgcryptodijalankan. Isinya terlihat di bagian akhir output:default_version = '1.3'menentukan versi yang dipasang bila versi tidak disebut,module_pathnamemenunjuk library C,relocatable = truedantrusted = truemengatur perilaku pemasangan yang akan kita bahas di bawah.pgcrypto--1.3.sql— script instalasi versi 1.3: daftarCREATE FUNCTIONyang dijalankan sekali saat pemasangan.pgcrypto--1.0--1.1.sql(dan dua file serupa) — update path: script perpindahan versi dari 1.0 ke 1.1, dari 1.1 ke 1.2, dan seterusnya. Inilah jalur yang dipakaiALTER EXTENSION ... UPDATEsaat memperbarui versi.pgcrypto.so— shared library di direktorilibserver; berisi kode C untuk fungsi-fungsi kriptografi. Extension murni-SQL seperticitexttidak punya komponen ini.
Katalog pg_available_extensions #
PostgreSQL tidak menebak-nebak extension apa saja yang ada. Server membaca seluruh control file di SHAREDIR/extension saat startup dan memaparkannya lewat view pg_available_extensions — daftar extension yang bisa dipasang di server tersebut. Daftar ini deterministik untuk satu instalasi: hanya berisi extension yang paketnya sudah terpasang di sistem operasi. Query berikut menghitung totalnya di server kursus, lalu menampilkan delapan extension yang dibedah satu per satu di section ini:
-- Katalog extension yang tersedia di server tersimpan di view pg_available_extensions.
-- Daftar ini deterministik untuk satu instalasi: ditentukan oleh paket yang terpasang.
SELECT count(*) AS total_extension_tersedia
FROM pg_available_extensions;
-- Delapan extension yang dibedah di section ini, beserta versi default-nya.
SELECT name, default_version
FROM pg_available_extensions
WHERE name IN ('pg_stat_statements', 'pgcrypto', 'postgres_fdw', 'dblink',
'pg_trgm', 'uuid-ossp', 'btree_gist', 'amcheck')
ORDER BY name;Output yang Dihasilkan:
total_extension_tersedia
--------------------------
45
(1 row)
name | default_version
--------------------+-----------------
amcheck | 1.4
btree_gist | 1.7
dblink | 1.2
pg_stat_statements | 1.11
pg_trgm | 1.6
pgcrypto | 1.3
postgres_fdw | 1.1
uuid-ossp | 1.1
(8 rows)Server kursus membawa 45 extension siap pasang — seluruhnya berasal dari paket postgresql-contrib yang memang sudah disertakan sejak persiapan lingkungan. Daftar ini akan berbeda di server lain: extension komunitas seperti PostGIS atau TimescaleDB tidak muncul di pg_available_extensions sampai paketnya dipasang di sistem operasi. Perbedaan dua dunia itu — paket di level sistem operasi versus CREATE EXTENSION di level SQL — dibedah tuntas di chapter berikutnya.
Objek yang Ditambahkan Sebuah Extension #
Kekuatan extension terletak pada rentang objek yang bisa didaftarkannya. Extension dapat menambah hampir semua jenis objek katalog — bukan cuma function, melainkan juga tipe data, operator, operator class untuk index, cast, table, view, sampai akses method index yang benar-benar baru. Jumlah objek per extension bervariasi: extension kecil bisa hanya berisi satu function, sementara extension besar mendaftarkan ratusan objek. Query berikut memasang citext (tipe text yang mengabaikan besar-kecil huruf) lalu menghitung objek apa saja yang baru saja terdaftar:
-- Satu perintah CREATE EXTENSION citext mendaftarkan puluhan objek sekaligus.
CREATE EXTENSION citext;
-- pg_depend dengan deptype 'e' mencatat semua objek milik sebuah extension.
SELECT CASE classid
WHEN 'pg_proc'::regclass THEN 'function'
WHEN 'pg_operator'::regclass THEN 'operator'
WHEN 'pg_type'::regclass THEN 'type'
WHEN 'pg_cast'::regclass THEN 'cast'
WHEN 'pg_opclass'::regclass THEN 'operator class'
WHEN 'pg_opfamily'::regclass THEN 'operator family'
END AS jenis_objek,
count(*) AS jumlah
FROM pg_depend
WHERE deptype = 'e'
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'citext')
GROUP BY 1
ORDER BY 1;
-- Tipe citext langsung hidup sebagai tipe kolom biasa.
CREATE TABLE kontak (
id integer PRIMARY KEY,
email citext NOT NULL UNIQUE
);
INSERT INTO kontak VALUES
(1, '[email protected]'),
(2, '[email protected]');
-- citext membandingkan teks tanpa pedulikan besar-kecil huruf.
SELECT id, email
FROM kontak
WHERE email = '[email protected]';
-- Selesai: tabel dibuang dulu agar extension bisa dilepas tanpa CASCADE.
DROP TABLE kontak;
DROP EXTENSION citext;Output yang Dihasilkan:
CREATE EXTENSION
jenis_objek | jumlah
-----------------+--------
cast | 8
function | 47
operator | 26
operator class | 3
operator family | 3
type | 2
(6 rows)
CREATE TABLE
INSERT 0 2
id | email
----+------------------
1 | [email protected]
(1 row)
DROP TABLE
DROP EXTENSIONSensus pg_depend memperlihatkan skala sesungguhnya: satu baris CREATE EXTENSION citext mendaftarkan 89 objek — 2 tipe, 47 function, 26 operator, 8 cast, 3 operator class, dan 3 operator family. Setelah terpasang, objek-objek itu tidak dibedakan dari objek bawaan: kolom email bertipe citext dipakai seperti text biasa, dan query pencocokan [email protected] menemukan [email protected] tanpa lower() manual. Hubungan kepemilikan ini dua arah: katalog tahu bahwa tabel kontak bergantung pada tipe citext, sehingga melepas extension yang masih dipakai akan ditolak — mekanisme proteksi yang dibuktikan live di chapter Memasang dan Mengaktifkan Extension
.
Trusted vs Untrusted Extension #
Sejak PostgreSQL 13, control file boleh menandai extension sebagai trusted. Extension trusted hanya boleh membuat objek “aman” di schema — function, tipe, operator — sehingga PostgreSQL mengizinkan non-superuser yang punya hak CREATE pada database untuk memasangnya sendiri. Extension tanpa tanda itu (untrusted) tetap eksklusif superuser, biasanya karena library C-nya menjalankan operasi sensitif seperti koneksi keluar atau akses filesystem. Status setiap extension bisa dibaca dari kolom trusted di view pg_available_extension_versions; di server kursus, pgcrypto, pg_trgm, citext, dan uuid-ossp bertanda trusted, sedangkan dblink, postgres_fdw, dan pg_stat_statements tidak.
Tiga file berikut membuktikan perbedaan itu secara nyata lewat role biasa bernama pengembang. File pertama memasang pg_trgm (trusted) dalam mode SET ROLE:
-- Role biasa tanpa hak superuser, untuk mensimulasikan non-superuser.
CREATE ROLE pengembang NOLOGIN;
-- Extension bertanda trusted boleh dipasang non-superuser
-- asalkan role punya hak CREATE pada database tempatnya bekerja.
-- :DBNAME adalah variabel psql yang berisi nama database koneksi aktif.
GRANT CREATE ON DATABASE :DBNAME TO pengembang;
SET ROLE pengembang;
SHOW is_superuser;
CREATE EXTENSION pg_trgm;
RESET ROLE;
SELECT extname, extnamespace::regnamespace AS skema
FROM pg_extension
WHERE extname = 'pg_trgm';
-- Objek milik extension terpakai seperti fungsi bawaan.
SELECT similarity('budi', 'budi santoso') AS skor;
-- pg_trgm dilepas dulu; role demo masih dipakai contoh berikutnya.
DROP EXTENSION pg_trgm;Output yang Dihasilkan:
CREATE ROLE
GRANT
SET
is_superuser
--------------
off
(1 row)
CREATE EXTENSION
RESET
extname | skema
---------+--------
pg_trgm | public
(1 row)
skor
-----------
0.3846154
(1 row)
DROP EXTENSIONSHOW is_superuser di tengah output bernilai off — kita benar-benar berperan sebagai role biasa — dan CREATE EXTENSION pg_trgm tetap berhasil. File kedua mencoba hal yang sama untuk dblink yang tidak bertanda trusted:
-- dblink tidak bertanda trusted: hanya superuser yang boleh memasangnya.
SET ROLE pengembang;
CREATE EXTENSION dblink;Output yang Dihasilkan:
psql:untrusted-gagal.sql:4: ERROR: permission denied to create extension "dblink"
HINT: Must be superuser to create this extension.Server menolak dengan tegas: permission denied to create extension “dblink” — hanya superuser yang boleh. Pola produksinya pun jelas dari file ketiga: administrator superuser memasang sekali untuk seluruh pengguna, lalu semua role cukup memakai fungsinya:
-- Kembali sebagai superuser: dblink terpasang dalam satu perintah.
CREATE EXTENSION dblink;
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'dblink';
-- Bersih-bersih role demo beserta seluruh hak yang melekat padanya.
DROP OWNED BY pengembang;
DROP ROLE pengembang;
-- Extension ikut dibersihkan; chapter dblink memasangnya kembali sendiri.
DROP EXTENSION dblink;Output yang Dihasilkan:
CREATE EXTENSION
extname | extversion
---------+------------
dblink | 1.2
(1 row)
DROP OWNED
DROP ROLE
DROP EXTENSIONKapan Extension, Kapan Kode Aplikasi #
Extension bukan pengganti semua kode aplikasi. Ukurannya sederhana: bila logika butuh akses data langsung, konsistensi lintas klien, atau performa eksekusi di dalam server, extension menang; bila logika berubah setiap minggu dan dikuasai tim aplikasi, kode aplikasi lebih sehat. Tabel ringkas berikut memetakan keputusan itu:
| Pertimbangan | Extension | Kode aplikasi |
|---|---|---|
| Kecepatan eksekusi di dekat data | Eksekusi di proses server, tanpa jaringan | Setiap query bolak-balik jaringan |
| Konsistensi lintas bahasa | Satu definisi dipakai semua klien | Setiap bahasa menulis ulang logika |
| Siklus perubahan | Ikut jadwal rilis server dan migrasi | Deploy kapan saja lewat pipeline aplikasi |
| Risiko operasional | Menambah permukaan upgrade server | Terisolasi dari versi PostgreSQL |
| Bahasa pemrograman | C atau SQL/PLpgSQL | Bebas: PHP, Go, Python, JavaScript |
Konsep, katalog, dan batas keamanan extension kini sudah di tangan kita. Langkah berikutnya adalah praktik pemasangannya: dari memasang paket di sistem operasi, CREATE EXTENSION dengan versi tertentu, penempatan schema, sampai risiko DROP EXTENSION CASCADE — semuanya di chapter Memasang dan Mengaktifkan Extension
.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
anatomi-paket.sh
Unduh berkas#!/usr/bin/env bash
# Lokasi komponen fisik extension pgcrypto pada server kursus.
EXTDIR=/home/z/my-project/tools/dl/pgdeb/usr/share/postgresql/17/extension
LIBDIR=/home/z/my-project/tools/dl/pgdeb/usr/lib/postgresql/17/lib
echo "== SHAREDIR/extension: control file + script SQL =="
ls "$EXTDIR" | grep '^pgcrypto'
echo ""
echo "== libdir: shared library =="
ls "$LIBDIR" | grep '^pgcrypto'
echo ""
echo "== Isi pgcrypto.control =="
cat "$EXTDIR/pgcrypto.control"
daftar-tersedia.sql
Unduh berkas-- Katalog extension yang tersedia di server tersimpan di view pg_available_extensions.
-- Daftar ini deterministik untuk satu instalasi: ditentukan oleh paket yang terpasang.
SELECT count(*) AS total_extension_tersedia
FROM pg_available_extensions;
-- Delapan extension yang dibedah di section ini, beserta versi default-nya.
SELECT name, default_version
FROM pg_available_extensions
WHERE name IN ('pg_stat_statements', 'pgcrypto', 'postgres_fdw', 'dblink',
'pg_trgm', 'uuid-ossp', 'btree_gist', 'amcheck')
ORDER BY name;
objek-yang-ditambahkan.sql
Unduh berkas-- Satu perintah CREATE EXTENSION citext mendaftarkan puluhan objek sekaligus.
CREATE EXTENSION citext;
-- pg_depend dengan deptype 'e' mencatat semua objek milik sebuah extension.
SELECT CASE classid
WHEN 'pg_proc'::regclass THEN 'function'
WHEN 'pg_operator'::regclass THEN 'operator'
WHEN 'pg_type'::regclass THEN 'type'
WHEN 'pg_cast'::regclass THEN 'cast'
WHEN 'pg_opclass'::regclass THEN 'operator class'
WHEN 'pg_opfamily'::regclass THEN 'operator family'
END AS jenis_objek,
count(*) AS jumlah
FROM pg_depend
WHERE deptype = 'e'
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'citext')
GROUP BY 1
ORDER BY 1;
-- Tipe citext langsung hidup sebagai tipe kolom biasa.
CREATE TABLE kontak (
id integer PRIMARY KEY,
email citext NOT NULL UNIQUE
);
INSERT INTO kontak VALUES
(1, '[email protected]'),
(2, '[email protected]');
-- citext membandingkan teks tanpa pedulikan besar-kecil huruf.
SELECT id, email
FROM kontak
WHERE email = '[email protected]';
-- Selesai: tabel dibuang dulu agar extension bisa dilepas tanpa CASCADE.
DROP TABLE kontak;
DROP EXTENSION citext;
pasang-oleh-admin.sql
Unduh berkas-- Kembali sebagai superuser: dblink terpasang dalam satu perintah.
CREATE EXTENSION dblink;
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'dblink';
-- Bersih-bersih role demo beserta seluruh hak yang melekat padanya.
DROP OWNED BY pengembang;
DROP ROLE pengembang;
-- Extension ikut dibersihkan; chapter dblink memasangnya kembali sendiri.
DROP EXTENSION dblink;
trusted-demo.sql
Unduh berkas-- Role biasa tanpa hak superuser, untuk mensimulasikan non-superuser.
CREATE ROLE pengembang NOLOGIN;
-- Extension bertanda trusted boleh dipasang non-superuser
-- asalkan role punya hak CREATE pada database tempatnya bekerja.
-- :DBNAME adalah variabel psql yang berisi nama database koneksi aktif.
GRANT CREATE ON DATABASE :DBNAME TO pengembang;
SET ROLE pengembang;
SHOW is_superuser;
CREATE EXTENSION pg_trgm;
RESET ROLE;
SELECT extname, extnamespace::regnamespace AS skema
FROM pg_extension
WHERE extname = 'pg_trgm';
-- Objek milik extension terpakai seperti fungsi bawaan.
SELECT similarity('budi', 'budi santoso') AS skor;
-- pg_trgm dilepas dulu; role demo masih dipakai contoh berikutnya.
DROP EXTENSION pg_trgm;
untrusted-gagal.sql
Unduh berkas-- dblink tidak bertanda trusted: hanya superuser yang boleh memasangnya.
SET ROLE pengembang;
CREATE EXTENSION dblink;