Chapter sebelumnya menunjukkan bahwa pg_catalog menyimpan seluruh metadata PostgreSQL — tetapi query ke katalog itu tidak akan jalan di MySQL, SQL Server, atau Oracle: nama kolom dan strukturnya khas PostgreSQL. Standard SQL menyiapkan jalan keluar: information_schema, kumpulan view berbasis standar yang isi dan namanya dijaga sama lintas database. Bagi tim yang menulis tool internal, skrip migrasi, atau laporan audit yang harus hidup di lebih dari satu mesin database, schema inilah antarmuka metadata paling aman. Chapter ini mengelilingi view-view terpentingnya dengan query yang berjalan di schema contoh kita, lalu menimbang kapan information_schema lebih baik dipakai daripada pg_catalog — dan kapan tidak.

Antarmuka Metadata Standar SQL #

information_schema adalah schema otomatis yang hadir di setiap database, berisi view — bukan table — di atas catalog internal. Konsekuensinya dua arah: isinya selalu segar tanpa perawatan, tetapi hanya memuat apa yang bisa dinyatakan dalam standar SQL. Nama-nama view pentingnya:

ViewIsi
tablesSemua tabel dan view, dengan table_type pembeda
columnsSemua kolom: tipe, panjang, nullable, posisi
viewsDaftar view plus flag is_updatable
table_constraintsSemua constraint: PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK
key_column_usageKolom yang menjadi bagian PK/UNIQUE/FK
referential_constraintsDetail foreign key: rujukan dan aturan update/delete
routinesFunction dan procedure
role_table_grantsHak akses tabel per role

Semua view ini menyaring berdasarkan table_schema/constraint_schema — tanpa filter, hasilnya bercampur baris-baris schema sistem.

tables dan columns #

File pertama menyiapkan schema contoh — dua tabel yang saling terhubung, satu view, satu function — dan file kedua membacanya dari dua view paling sering dipakai:

-- Skema contoh untuk dilihat dari information_schema
CREATE TABLE pelanggan (
  id serial PRIMARY KEY,
  nama varchar(50) NOT NULL,
  email varchar(100) UNIQUE
);

CREATE TABLE pesanan (
  id serial PRIMARY KEY,
  pelanggan_id integer NOT NULL REFERENCES pelanggan(id),
  total numeric(12,2) CHECK (total >= 0)
);

CREATE VIEW pesanan_ringkas AS
  SELECT id, pelanggan_id, total FROM pesanan;

CREATE FUNCTION hitung_diskon(total numeric)
RETURNS numeric
LANGUAGE sql
IMMUTABLE
RETURN round(total * 0.1, 2);

INSERT INTO pelanggan (nama, email) VALUES
  ('Budi', '[email protected]'),
  ('Siti', '[email protected]');

INSERT INTO pesanan (pelanggan_id, total) VALUES
  (1, 150000.00),
  (2, 99000.00);

Output yang Dihasilkan:

text
CREATE TABLE
CREATE TABLE
CREATE VIEW
CREATE FUNCTION
INSERT 0 2
INSERT 0 2
sql tabel-kolom.sql
-- Daftar tabel dan view menurut standar SQL
SELECT table_name, table_type
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name;

-- Definisi kolom tabel pelanggan
SELECT column_name, data_type, character_maximum_length, is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
  AND table_name = 'pelanggan'
ORDER BY ordinal_position;

Output yang Dihasilkan:

text
   table_name    | table_type 
-----------------+------------
 pelanggan       | BASE TABLE
 pesanan         | BASE TABLE
 pesanan_ringkas | VIEW
(3 rows)

 column_name |     data_type     | character_maximum_length | is_nullable 
-------------+-------------------+--------------------------+-------------
 id          | integer           |                          | NO
 nama        | character varying |                       50 | NO
 email       | character varying |                      100 | YES
(3 rows)

Dua hasil di atas memperlihatkan ciri khas information_schema. Pertama, nama tipe ditulis dalam istilah standar — character varying, bukan varchar — dan panjangnya terpisah di kolom sendiri, sehingga tool bisa memformatnya kembali ke dialek SQL mana pun. Kedua, jawaban atas “apa saja objek di schema ini?” dan “kolomnya apa saja?” tidak memerlukan join oid seperti di pg_catalog; cukup filter table_schema dan ORDER BY yang menentukan urutan penuh hasilnya.

Constraint Lintas View #

Informasi constraint terpecah ke beberapa view yang dirancang untuk di-join — pola persis seperti ERD kecil di dalam schema metadata:

sql constraint.sql
-- Constraint beserta kolom yang menjadi kuncinya
SELECT tc.table_name, tc.constraint_type, tc.constraint_name, kcu.column_name
FROM information_schema.table_constraints tc
LEFT JOIN information_schema.key_column_usage kcu
  ON kcu.constraint_schema = tc.constraint_schema
 AND kcu.constraint_name = tc.constraint_name
WHERE tc.table_schema = 'public'
  AND tc.constraint_name NOT LIKE '%not_null'
ORDER BY tc.table_name, tc.constraint_name, kcu.column_name NULLS FIRST;

-- Aturan foreign key berpasangan dengan unique constraint tujuannya
SELECT constraint_name, unique_constraint_name, update_rule, delete_rule
FROM information_schema.referential_constraints
WHERE constraint_schema = 'public'
ORDER BY constraint_name;

Output yang Dihasilkan:

text
 table_name | constraint_type |      constraint_name      | column_name  
------------+-----------------+---------------------------+--------------
 pelanggan  | UNIQUE          | pelanggan_email_key       | email
 pelanggan  | PRIMARY KEY     | pelanggan_pkey            | id
 pesanan    | FOREIGN KEY     | pesanan_pelanggan_id_fkey | pelanggan_id
 pesanan    | PRIMARY KEY     | pesanan_pkey              | id
 pesanan    | CHECK           | pesanan_total_check       | 
(5 rows)

      constraint_name      | unique_constraint_name | update_rule | delete_rule 
---------------------------+------------------------+-------------+-------------
 pesanan_pelanggan_id_fkey | pelanggan_pkey         | NO ACTION   | NO ACTION
(1 row)

Query pertama memakai LEFT JOIN karena constraint CHECK tidak punya kolom kunci — kolom column_name-nya kosong, seperti baris pesanan_total_check di hasil. Query kedua menunjukkan hal yang tidak dimiliki pg_constraint dalam bentuk langsung: relasi pesanan_pelanggan_id_fkey menunjuk pelanggan_pkey sebagai constraint tujuan, dengan aturan NO ACTION untuk UPDATE dan DELETE — bawaan yang setara ON UPDATE NO ACTION ON DELETE NO ACTION.

Grant, Routine, dan View #

Tiga view sisanya menjawab pertanyaan operasional sehari-hari — siapa boleh apa, function apa saja yang terpasang, dan view mana yang bisa ditulis:

-- Hak akses tabel untuk role yang sedang berjalan
SELECT table_name, privilege_type
FROM information_schema.role_table_grants
WHERE table_schema = 'public'
  AND grantee = current_user
ORDER BY table_name, privilege_type;

-- Daftar function di schema kita
SELECT routine_name, routine_type, data_type
FROM information_schema.routines
WHERE routine_schema = 'public'
ORDER BY routine_name;

-- View dan status kebaruan datanya
SELECT table_name, is_updatable
FROM information_schema.views
WHERE table_schema = 'public'
ORDER BY table_name;

Output yang Dihasilkan:

text
   table_name    | privilege_type 
-----------------+----------------
 pelanggan       | DELETE
 pelanggan       | INSERT
 pelanggan       | REFERENCES
 pelanggan       | SELECT
 pelanggan       | TRIGGER
 pelanggan       | TRUNCATE
 pelanggan       | UPDATE
 pesanan         | DELETE
 pesanan         | INSERT
 pesanan         | REFERENCES
 pesanan         | SELECT
 pesanan         | TRIGGER
 pesanan         | TRUNCATE
 pesanan         | UPDATE
 pesanan_ringkas | DELETE
 pesanan_ringkas | INSERT
 pesanan_ringkas | REFERENCES
 pesanan_ringkas | SELECT
 pesanan_ringkas | TRIGGER
 pesanan_ringkas | TRUNCATE
 pesanan_ringkas | UPDATE
(21 rows)

 routine_name  | routine_type | data_type 
---------------+--------------+-----------
 hitung_diskon | FUNCTION     | numeric
(1 row)

   table_name    | is_updatable 
-----------------+--------------
 pesanan_ringkas | YES
(1 row)

Tiga belas jenis hak pada dua tabel plus view itu berasal dari status kita sebagai pemilik objek — pemilik otomatis menerima seluruh privilege. View routines menampilkan hitung_diskon sebagai FUNCTION yang mengembalikan numeric, sementara views memberi tahu bahwa pesanan_ringkas adalah simple view yang is_updatable — PostgreSQL mengizinkan INSERT/UPDATE langsung ke view semacam itu tanpa trigger tambahan.

information_schema atau pg_catalog #

Aspekinformation_schemapg_catalog
StatusStandar SQL, tersedia lintas databaseKhas PostgreSQL
Portabilitas queryTinggi — jalan di MySQL, SQL Server, dllNol — ganti database berarti ganti query
Cakupan objekYang bisa dinyatakan standar: tabel, kolom, constraint, grantSemua: index, sequence, toast, extension, publication, dll
Detail teknisTidak ada — tanpa oid, tanpa relpages, tanpa storageLengkap — oid, relkind, relpages, reloptions, am
Stabilitas namaDijaga standard antar versiBisa berubah antar major version
Kebutuhan joinRingkas — sedikit kolom kunci teksSering lewat oid antar katalog

Aturan praktisnya sederhana. Untuk tool yang harus portabel — laporan skema, generator kode, skrip migrasi awal — pilih information_schema dan bersabar dengan cakupannya yang terbatas. Untuk pekerjaan administrasi PostgreSQL yang butuh detail teknis — ukuran relasi, definisi index, statistik planner — pg_catalog adalah satu-satunya sumber lengkap, dan itu sudah kita kuasai di chapter sebelumnya. Keduanya tidak saling menggantikan; keduanya membaca sumber kebenaran yang sama dari dua sudut yang berbeda.

Ada satu sumber kebenaran lagi yang selama ini hanya kita intip sepintas: log server — saksi bisu setiap query lambat, error, dan restart. Chapter Server Log membuka lembaran itu.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

constraint.sql

Unduh berkas
sql
-- Constraint beserta kolom yang menjadi kuncinya
SELECT tc.table_name, tc.constraint_type, tc.constraint_name, kcu.column_name
FROM information_schema.table_constraints tc
LEFT JOIN information_schema.key_column_usage kcu
  ON kcu.constraint_schema = tc.constraint_schema
 AND kcu.constraint_name = tc.constraint_name
WHERE tc.table_schema = 'public'
  AND tc.constraint_name NOT LIKE '%not_null'
ORDER BY tc.table_name, tc.constraint_name, kcu.column_name NULLS FIRST;

-- Aturan foreign key berpasangan dengan unique constraint tujuannya
SELECT constraint_name, unique_constraint_name, update_rule, delete_rule
FROM information_schema.referential_constraints
WHERE constraint_schema = 'public'
ORDER BY constraint_name;

hak-dan-routine.sql

Unduh berkas
sql
-- Hak akses tabel untuk role yang sedang berjalan
SELECT table_name, privilege_type
FROM information_schema.role_table_grants
WHERE table_schema = 'public'
  AND grantee = current_user
ORDER BY table_name, privilege_type;

-- Daftar function di schema kita
SELECT routine_name, routine_type, data_type
FROM information_schema.routines
WHERE routine_schema = 'public'
ORDER BY routine_name;

-- View dan status kebaruan datanya
SELECT table_name, is_updatable
FROM information_schema.views
WHERE table_schema = 'public'
ORDER BY table_name;

siapkan-skema.sql

Unduh berkas
sql
-- Skema contoh untuk dilihat dari information_schema
CREATE TABLE pelanggan (
  id serial PRIMARY KEY,
  nama varchar(50) NOT NULL,
  email varchar(100) UNIQUE
);

CREATE TABLE pesanan (
  id serial PRIMARY KEY,
  pelanggan_id integer NOT NULL REFERENCES pelanggan(id),
  total numeric(12,2) CHECK (total >= 0)
);

CREATE VIEW pesanan_ringkas AS
  SELECT id, pelanggan_id, total FROM pesanan;

CREATE FUNCTION hitung_diskon(total numeric)
RETURNS numeric
LANGUAGE sql
IMMUTABLE
RETURN round(total * 0.1, 2);

INSERT INTO pelanggan (nama, email) VALUES
  ('Budi', '[email protected]'),
  ('Siti', '[email protected]');

INSERT INTO pesanan (pelanggan_id, total) VALUES
  (1, 150000.00),
  (2, 99000.00);

tabel-kolom.sql

Unduh berkas
sql
-- Daftar tabel dan view menurut standar SQL
SELECT table_name, table_type
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name;

-- Definisi kolom tabel pelanggan
SELECT column_name, data_type, character_maximum_length, is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
  AND table_name = 'pelanggan'
ORDER BY ordinal_position;