Data di dunia nyata sering tersebar: data master karyawan tinggal di database pusat, transaksi harian di database cabang, arsip lama di server terpisah. PostgreSQL menuntut satu koneksi memilih satu database dan tidak menyediakan sintaks nama_database.public.nama_tabel untuk melompat antar database — pembatasan yang mengejutkan pendatang baru dari MySQL. Extension postgres_fdw menjembatani celah itu: extension ini membuat tabel milik database PostgreSQL mana pun — di server yang sama maupun di seberang jaringan — tampak sebagai tabel lokal biasa yang bisa di-SELECT, di-join, bahkan di-UPDATE. Chapter ini membangun demo dua database lengkap di satu server: memasang extension, mendefinisikan objek koneksinya, membaca dan menulis data lintas database, lalu membedah rencana eksekusinya di balik layar.

Menyiapkan Dua Database #

Demo kita membutuhkan dua database di satu server: database kerja chapter ini berperan sebagai kantor cabang, dan database kedua sebagai kantor pusat pemilik data master. Perintah CREATE DATABASE tidak bisa dijalankan dari dalam database yang sedang tersambung, sehingga file pertama dijalankan dari database maintenance postgres, lalu berpindah ke database baru itu lewat meta-command \connect untuk membangun tabel masternya:

-- Demo postgres_fdw butuh DUA database: database kerja chapter ini berperan
-- sebagai "kantor cabang", database kedua sebagai "kantor pusat" pemilik
-- data master. CREATE DATABASE harus dijalankan dari database lain
-- (postgres), karena PostgreSQL tidak mengizinkan pembuatan database
-- dari dalam database yang sedang dipakai.
DROP DATABASE IF EXISTS kantor_pusat_fdw;

CREATE DATABASE kantor_pusat_fdw;

-- Pindah ke database pusat, lalu bangun tabel master-nya.
\connect kantor_pusat_fdw

CREATE TABLE karyawan (
    id   integer PRIMARY KEY,
    nama text    NOT NULL,
    kota text    NOT NULL,
    gaji integer NOT NULL
);

INSERT INTO karyawan VALUES
    (1, 'Budi Santoso',  'Jakarta',  12500000),
    (2, 'Siti Rahayu',   'Bandung',   9800000),
    (3, 'Agus Wibowo',   'Surabaya', 10200000),
    (4, 'Dewi Lestari',  'Jakarta',   8700000),
    (5, 'Rina Marlina',  'Medan',     7600000),
    (6, 'Tono Sutrisno', 'Bandung',   6900000);

CREATE TABLE cabang (
    kode    text PRIMARY KEY,
    kota    text    NOT NULL,
    manager text    NOT NULL
);

INSERT INTO cabang VALUES
    ('JKT', 'Jakarta',  'Budi Santoso'),
    ('BDG', 'Bandung',  'Siti Rahayu'),
    ('SBY', 'Surabaya', 'Agus Wibowo');

Output yang Dihasilkan:

text
DROP DATABASE
CREATE DATABASE
You are now connected to database "kantor_pusat_fdw" as user "postgres".
CREATE TABLE
INSERT 0 6
CREATE TABLE
INSERT 0 3

Baris You are now connected to database "kantor_pusat_fdw" membuktikan psql berpindah database di tengah file; seluruh CREATE TABLE dan INSERT setelahnya dieksekusi di database pusat, bukan di postgres. Database pusat kini memegang dua tabel master — karyawan dengan enam baris dan cabang dengan tiga baris — yang selama sisa chapter akan kita baca dan tulis dari database cabang tanpa pernah keluar dari sesi SQL kita.

Server, User Mapping, dan Foreign Table #

postgres_fdw mengimplementasikan konsep foreign data wrapper dari standar SQL/MED. Konsepnya membagi koneksi menjadi tiga objek terpisah, masing-masing menjawab satu pertanyaan:

ObjekMenjawab pertanyaanIsi utamanya
CREATE SERVERKe mana menyambung?host, port, dbname
CREATE USER MAPPINGSebagai siapa menyambung?user, password — per role lokal
CREATE FOREIGN TABLETabel mana yang dipetakan?Kolom plus schema_name, table_name remote

Alur ketiganya berjalan berlapis: query menyentuh foreign table, foreign table menunjuk server, server membaca user mapping milik role yang menjalankan query, lalu postgres_fdw membuka koneksi libpq ke database tujuan:

File berikut memasang extension dan mendefinisikan ketiga objek itu di database cabang:

-- postgres_fdw dipasang di database cabang (database kerja chapter ini).
-- Pemasangannya menuntut superuser karena extension ini membuka koneksi
-- keluar dari server.
CREATE EXTENSION postgres_fdw;

-- SERVER menjawab "ke mana menyambung": socket, port, dan database tujuan.
CREATE SERVER kantor_server FOREIGN DATA WRAPPER postgres_fdw
    OPTIONS (host '/home/z/my-project/pgrun', port '54329', dbname 'kantor_pusat_fdw');

-- USER MAPPING menjawab "sebagai siapa": kredensial untuk koneksi keluar.
CREATE USER MAPPING FOR postgres SERVER kantor_server
    OPTIONS (user 'postgres');

-- FOREIGN TABLE memetakan tabel pusat menjadi tabel yang tampak lokal.
-- OPTIONS schema_name dan table_name menunjuk tabel aslinya di pusat.
CREATE FOREIGN TABLE karyawan_pusat (
    id   integer NOT NULL,
    nama text    NOT NULL,
    kota text    NOT NULL,
    gaji integer NOT NULL
) SERVER kantor_server
    OPTIONS (schema_name 'public', table_name 'karyawan');

-- Katalog mencatat ketiga objek koneksi tersebut.
SELECT s.srvname AS server, w.fdwname AS wrapper, s.srvoptions AS opsi_server
FROM pg_foreign_server s
JOIN pg_foreign_data_wrapper w ON w.oid = s.srvfdw
ORDER BY s.srvname;

SELECT srvname AS server, umoptions AS opsi_user
FROM pg_user_mappings
ORDER BY srvname;

Output yang Dihasilkan:

text
CREATE EXTENSION
CREATE SERVER
CREATE USER MAPPING
CREATE FOREIGN TABLE
    server     |   wrapper    |                            opsi_server                             
---------------+--------------+--------------------------------------------------------------------
 kantor_server | postgres_fdw | {host=/home/z/my-project/pgrun,port=54329,dbname=kantor_pusat_fdw}
(1 row)

    server     |    opsi_user    
---------------+-----------------
 kantor_server | {user=postgres}
(1 row)

Mari kita bedah objek-objeknya satu per satu:

  1. CREATE EXTENSION postgres_fdw memasang wrapper-nya. Extension ini menuntut superuser untuk dipasang — berbeda dengan pgcrypto atau pg_trgm yang trusted — karena objek yang dibuatnya dapat membuka koneksi keluar atas nama server.
  2. CREATE SERVER kantor_server mencatat alamat tujuan. Opsi host di server kursus berupa direktori socket /home/z/my-project/pgrun; di produksi nilainya biasanya hostname atau IP server lain, dan dbname boleh berbeda dari database lokal.
  3. CREATE USER MAPPING FOR postgres mengikat kredensial ke role lokal. Server kursus memakai autentikasi trust sehingga cukup user 'postgres' tanpa password.
  4. CREATE FOREIGN TABLE karyawan_pusat memetakan public.karyawan di pusat. Definisi kolomnya ditulis tangan dan tidak divalidasi terhadap tabel aslinya — tipe data yang tidak cocok baru terasa saat query gagal mem-parse hasil, bukan saat foreign table dibuat.
  5. Dua query katalog menutup file: pg_foreign_server menyimpan opsi server persis seperti output {host=/home/z/my-project/pgrun,port=54329,dbname=kantor_pusat_fdw}, dan view pg_user_mappings menyimpan opsi user mapping {user=postgres}.

Membaca Data Pusat dan Join dengan Tabel Lokal #

Setelah pemetaan berdiri, foreign table dipakai persis seperti tabel biasa — filter, urutan, dan join bekerja tanpa sintaks khusus. Contoh berikut sekaligus memperlihatkan federasi: tabel bonus dibuat lokal di database cabang, lalu di-join dengan karyawan_pusat yang datanya tinggal di database pusat:

-- Data pusat dibaca persis seperti tabel lokal biasa.
SELECT id, nama, kota, gaji
FROM karyawan_pusat
WHERE kota = 'Jakarta'
ORDER BY id;

-- Federasi sesungguhnya: tabel LOKAL (bonus cabang) di-join dengan tabel
-- REMOTE (karyawan pusat) dalam satu query. PostgreSQL menggabungkan
-- keduanya secara transparan.
CREATE TABLE bonus (
    karyawan_id integer PRIMARY KEY,
    bonus       integer NOT NULL
);

INSERT INTO bonus VALUES
    (1, 1500000),
    (2,  900000),
    (4,  750000);

SELECT k.nama, k.kota, b.bonus
FROM karyawan_pusat k
JOIN bonus b ON b.karyawan_id = k.id
ORDER BY k.id;

Output yang Dihasilkan:

text
 id |     nama     |  kota   |   gaji   
----+--------------+---------+----------
  1 | Budi Santoso | Jakarta | 12500000
  4 | Dewi Lestari | Jakarta |  8700000
(2 rows)

CREATE TABLE
INSERT 0 3
     nama     |  kota   |  bonus  
--------------+---------+---------
 Budi Santoso | Jakarta | 1500000
 Siti Rahayu  | Bandung |  900000
 Dewi Lestari | Jakarta |  750000
(3 rows)

Query pertama mengembalikan dua karyawan Jakarta tanpa satu baris pun disalin manual — postgres_fdw membuka koneksi, mengirim query ke pusat, dan menukar hasilnya menjadi baris lokal. Query join kemudian menggabungkan dua sumber sekaligus: tiga baris hasil memuat nama dan kota dari database pusat berdampingan dengan bonus dari database cabang. Inilah federasi yang oleh PostgreSQL native mustahil dilakukan, karena satu koneksi memang tidak boleh membaca dua database sekaligus.

postgres_fdw tidak berhenti di membaca: foreign table-nya writable. Statement INSERT, UPDATE, dan DELETE pada foreign table diteruskan ke database pusat sebagai statement remote, dan klausa RETURNING tetap bekerja karena baris hasilnya dikirim balik ke sesi lokal:

-- Foreign table postgres_fdw bersifat writable: INSERT diteruskan ke
-- database pusat. RETURNING bekerja seperti pada tabel biasa.
INSERT INTO karyawan_pusat VALUES (7, 'Farhan Gunawan', 'Jakarta', 9300000)
RETURNING id, nama;

-- UPDATE juga diteruskan; hanya baris yang cocok filter yang berubah di pusat.
UPDATE karyawan_pusat SET gaji = 8100000 WHERE id = 4
RETURNING id, gaji;

-- Baca ulang seluruh tabel: baris 7 yang baru dan gaji baris 4 yang
-- diubah kini terlihat dari sisi cabang.
SELECT id, nama, gaji
FROM karyawan_pusat
ORDER BY id;

Output yang Dihasilkan:

text
 id |      nama      
----+----------------
  7 | Farhan Gunawan
(1 row)

INSERT 0 1
 id |  gaji   
----+---------
  4 | 8100000
(1 row)

UPDATE 1
 id |      nama      |   gaji   
----+----------------+----------
  1 | Budi Santoso   | 12500000
  2 | Siti Rahayu    |  9800000
  3 | Agus Wibowo    | 10200000
  4 | Dewi Lestari   |  8100000
  5 | Rina Marlina   |  7600000
  6 | Tono Sutrisno  |  6900000
  7 | Farhan Gunawan |  9300000
(7 rows)

Tanda INSERT 0 1 dan UPDATE 1 pada output adalah tag dari eksekusi di sisi pusat yang dilaporkan kembali. SELECT penutup memperlihatkan hasil akhirnya dari mata cabang: baris 7 (Farhan Gunawan) hadir dan gaji Dewi Lestari turun menjadi 8100000 — dua perubahan yang menyimpan diri di database pusat, bukan di database tempat query dijalankan. Kejujuran data ini kita buktikan kembali di bagian pembersihan dengan menyambung langsung ke database pusat.

IMPORT FOREIGN SCHEMA #

Menulis CREATE FOREIGN TABLE satu per satu wajar untuk satu-dua tabel, tetapi melelahkan untuk schema berisi belasan tabel. Perintah IMPORT FOREIGN SCHEMA membalik kerjanya: server pusat ditanya daftar tabelnya, lalu foreign table dibuatkan otomatis — lengkap dengan kolom dan tipenya — ke schema tujuan yang kita pilih:

-- CREATE FOREIGN TABLE manual cocok untuk satu-dua tabel. Untuk memetakan
-- seluruh schema sekaligus, IMPORT FOREIGN SCHEMA membaca daftar tabel di
-- sisi pusat dan membuat foreign table-nya otomatis -- di sini ke schema pusat.
CREATE SCHEMA pusat;

IMPORT FOREIGN SCHEMA public
    FROM SERVER kantor_server
    INTO pusat;

-- Meta-command psql \det *.* mendaftar seluruh foreign table di semua schema
-- beserta server yang memilikinya.
\det *.*

-- Tabel hasil import langsung bisa dikueri tanpa definisi manual.
SELECT kode, kota, manager
FROM pusat.cabang
ORDER BY kode;

Output yang Dihasilkan:

text
CREATE SCHEMA
IMPORT FOREIGN SCHEMA
         List of foreign tables
 Schema |     Table      |    Server     
--------+----------------+---------------
 public | karyawan_pusat | kantor_server
 pusat  | cabang         | kantor_server
 pusat  | karyawan       | kantor_server
(3 rows)

 kode |   kota   |   manager    
------+----------+--------------
 BDG  | Bandung  | Siti Rahayu
 JKT  | Jakarta  | Budi Santoso
 SBY  | Surabaya | Agus Wibowo
(3 rows)

Daftar \det *.* memperlihatkan hasil akhir pemetaan: karyawan_pusat di schema public yang kita definisikan manual, plus cabang dan karyawan di schema pusat yang lahir dari import. Perhatikan bahwa import membuat foreign table baru bernama sama dengan tabel aslinya — pusat.karyawan — sehingga penempatan ke schema terpisah mencegah bentrok nama dengan pemetaan manual kita. Klausa LIMIT TO (...) atau EXCEPT (...) dapat memfilter tabel mana saja yang ikut diimpor saat schema pusat berisi lebih banyak tabel.

EXPLAIN: Foreign Scan dan Remote SQL #

Bagaimana executor membaca foreign table? EXPLAIN (VERBOSE, COSTS) menjawabnya: node Foreign Scan menggantikan Seq Scan, dan baris Remote SQL memperlihatkan query yang benar-benar dikirim ke server pusat — termasuk predikat WHERE yang diteruskan ke sana (pushdown), sehingga penyaringan terjadi di pusat dan hanya baris Jakarta yang melintasi koneksi:

-- EXPLAIN memperlihatkan Foreign Scan: baris tidak dibaca dari storage lokal,
-- melainkan ditarik dari database pusat. VERBOSE menampilkan Remote SQL --
-- query yang benar-benar dikirim ke server pusat, lengkap dengan predikat
-- WHERE yang diteruskan (pushdown).
EXPLAIN (VERBOSE, COSTS)
SELECT id, nama
FROM karyawan_pusat
WHERE kota = 'Jakarta'
ORDER BY id;

Output yang Dihasilkan:

text
                                     QUERY PLAN                                      
-------------------------------------------------------------------------------------
 Sort  (cost=128.63..128.65 rows=7 width=36)
   Output: id, nama
   Sort Key: karyawan_pusat.id
   ->  Foreign Scan on public.karyawan_pusat  (cost=100.00..128.53 rows=7 width=36)
         Output: id, nama
         Remote SQL: SELECT id, nama FROM public.karyawan WHERE ((kota = 'Jakarta'))
 Query Identifier: 7252216033711214534
(7 rows)

Pembacaan rencananya baris demi baris:

  1. Foreign Scan on public.karyawan_pusat — tidak ada pembacaan storage lokal; node ini memanggil postgres_fdw untuk menarik baris dari kantor_server.
  2. Remote SQL: SELECT id, nama FROM public.karyawan WHERE ((kota = 'Jakarta')) — query yang dikirim ke pusat. Predikat kota = 'Jakarta' berada di sana, bukan difilter lokal, sehingga jaringan hanya mengangkut baris yang relevan.
  3. Sort di atas Foreign Scan — pengurutan ORDER BY id untuk kasus ini dilakukan lokal setelah baris tiba; postgres_fdw meneruskan lebih banyak klausa (join, agregasi, LIMIT) ke remote bila rencananya menguntungkan.
  4. cost=100.00.. — biaya dasar yang tinggi itu sengaja: planner menaruh penalti tetap pada foreign scan karena koneksi jaringan dianggap mahal. Opsi server use_remote_estimate = true meminta planner memakai estimasi EXPLAIN dari server pusat agar biaya dihitung lebih akurat.

Pola Pemakaian di Produksi #

Kapan postgres_fdw layak dipasang? Empat pola berikut mencakup mayoritas kasus nyata:

PolaSkenarioCatatan
Query lintas database (satu server)Aplikasi butuh data dari database lain di server yang samaSatu-satunya jalur resmi — PostgreSQL memang tidak punya sintaks db.schema.tabel
Federasi lintas serverDashboard menyatukan data cabang dari beberapa regionJoin dan agregasi bisa diteruskan ke sisi data bila menguntungkan
Batu loncatan migrasiPindah server bertahap: baca dari server lama, tulis ke server baruForeign table menjembatani dua sisi selama masa transisi
Integrasi tanpa ETLTabel referensi vendor cukup dibaca langsungHemat pipeline; ingat kinerja bergantung pada pushdown filter

Satu batas kinerja perlu ditanam sejak awal: setiap query foreign table adalah perjalanan jaringan. Join antara tabel lokal dan remote dengan jutaan baris akan menarik data lintas koneksi bila penyaringannya tidak bisa diteruskan — pakai EXPLAIN (VERBOSE) seperti di atas untuk memastikan predikat penting benar-benar sampai ke Remote SQL, dan biarkan tabel remote difilter selektif mungkin.

Membersihkan Seluruh Objek #

Pembersihan postgres_fdw berjalan berurutan dari objek teratas. DROP SERVER ... CASCADE menjatuhkan user mapping dan seluruh foreign table yang bergantung pada kantor_server — termasuk dua foreign table hasil import di schema pusat:

-- Satu CASCADE menjatuhkan user mapping dan seluruh foreign table yang
-- bergantung pada server ini, termasuk yang berada di schema pusat.
DROP SERVER kantor_server CASCADE;

-- Schema pusat kini kosong karena foreign table di dalamnya ikut dijatuhkan.
DROP SCHEMA pusat;

-- Extension dilepas terakhir setelah tidak ada objek yang memakainya.
DROP EXTENSION postgres_fdw;

Output yang Dihasilkan:

text
DROP SERVER
DROP SCHEMA
DROP EXTENSION

File terakhir menutup siklus dengan membuktikan klaim “data tinggal di pusat” secara langsung, lalu menjatuhkan database kedua:

-- Bukti data benar-benar tersimpan di database pusat: sambungan langsung ke
-- kantor_pusat_fdw tanpa perantara foreign table.
\connect kantor_pusat_fdw

SELECT id, nama, gaji
FROM karyawan
WHERE id IN (4, 7)
ORDER BY id;

-- Kembali ke database maintenance, lalu jatuhkan database pusat demo.
\connect postgres

DROP DATABASE kantor_pusat_fdw;

Output yang Dihasilkan:

text
You are now connected to database "kantor_pusat_fdw" as user "postgres".
 id |      nama      |  gaji   
----+----------------+---------
  4 | Dewi Lestari   | 8100000
  7 | Farhan Gunawan | 9300000
(2 rows)

You are now connected to database "postgres" as user "postgres".
DROP DATABASE

Baris 7 (Farhan Gunawan) dan gaji 8100000 milik Dewi Lestari muncul dari public.karyawan yang dibaca langsung di database pusat — bukti final bahwa INSERT dan UPDATE lewat foreign table memang mendarat di sana. Kedua database demo kini bersih, dan seluruh objek koneksi sudah tidak tersisa.

postgres_fdw menuntut persiapan: server, user mapping, dan foreign table dideklarasikan dulu agar bisa dipakai ribuan kali. Namun kebutuhan lintas database kadang hanya sesaat — menarik satu angka dari database tetangga tanpa meninggalkan jejak objek permanen. Untuk kebutuhan itulah extension dblink menyediakan API koneksi ad-hoc yang lebih tua dan lebih mentah; chapter berikutnya membedahnya lalu membandingkan keduanya dengan jujur.

Referensi #

Berkas Sumber

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

bersih-bersih.sql

Unduh berkas
sql
-- Satu CASCADE menjatuhkan user mapping dan seluruh foreign table yang
-- bergantung pada server ini, termasuk yang berada di schema pusat.
DROP SERVER kantor_server CASCADE;

-- Schema pusat kini kosong karena foreign table di dalamnya ikut dijatuhkan.
DROP SCHEMA pusat;

-- Extension dilepas terakhir setelah tidak ada objek yang memakainya.
DROP EXTENSION postgres_fdw;

buat-database-pusat.sql

Unduh berkas
sql
-- Demo postgres_fdw butuh DUA database: database kerja chapter ini berperan
-- sebagai "kantor cabang", database kedua sebagai "kantor pusat" pemilik
-- data master. CREATE DATABASE harus dijalankan dari database lain
-- (postgres), karena PostgreSQL tidak mengizinkan pembuatan database
-- dari dalam database yang sedang dipakai.
DROP DATABASE IF EXISTS kantor_pusat_fdw;

CREATE DATABASE kantor_pusat_fdw;

-- Pindah ke database pusat, lalu bangun tabel master-nya.
\connect kantor_pusat_fdw

CREATE TABLE karyawan (
    id   integer PRIMARY KEY,
    nama text    NOT NULL,
    kota text    NOT NULL,
    gaji integer NOT NULL
);

INSERT INTO karyawan VALUES
    (1, 'Budi Santoso',  'Jakarta',  12500000),
    (2, 'Siti Rahayu',   'Bandung',   9800000),
    (3, 'Agus Wibowo',   'Surabaya', 10200000),
    (4, 'Dewi Lestari',  'Jakarta',   8700000),
    (5, 'Rina Marlina',  'Medan',     7600000),
    (6, 'Tono Sutrisno', 'Bandung',   6900000);

CREATE TABLE cabang (
    kode    text PRIMARY KEY,
    kota    text    NOT NULL,
    manager text    NOT NULL
);

INSERT INTO cabang VALUES
    ('JKT', 'Jakarta',  'Budi Santoso'),
    ('BDG', 'Bandung',  'Siti Rahayu'),
    ('SBY', 'Surabaya', 'Agus Wibowo');

explain-foreign-scan.sql

Unduh berkas
sql
-- EXPLAIN memperlihatkan Foreign Scan: baris tidak dibaca dari storage lokal,
-- melainkan ditarik dari database pusat. VERBOSE menampilkan Remote SQL --
-- query yang benar-benar dikirim ke server pusat, lengkap dengan predikat
-- WHERE yang diteruskan (pushdown).
EXPLAIN (VERBOSE, COSTS)
SELECT id, nama
FROM karyawan_pusat
WHERE kota = 'Jakarta'
ORDER BY id;

hapus-database-pusat.sql

Unduh berkas
sql
-- Bukti data benar-benar tersimpan di database pusat: sambungan langsung ke
-- kantor_pusat_fdw tanpa perantara foreign table.
\connect kantor_pusat_fdw

SELECT id, nama, gaji
FROM karyawan
WHERE id IN (4, 7)
ORDER BY id;

-- Kembali ke database maintenance, lalu jatuhkan database pusat demo.
\connect postgres

DROP DATABASE kantor_pusat_fdw;

import-foreign-schema.sql

Unduh berkas
sql
-- CREATE FOREIGN TABLE manual cocok untuk satu-dua tabel. Untuk memetakan
-- seluruh schema sekaligus, IMPORT FOREIGN SCHEMA membaca daftar tabel di
-- sisi pusat dan membuat foreign table-nya otomatis -- di sini ke schema pusat.
CREATE SCHEMA pusat;

IMPORT FOREIGN SCHEMA public
    FROM SERVER kantor_server
    INTO pusat;

-- Meta-command psql \det *.* mendaftar seluruh foreign table di semua schema
-- beserta server yang memilikinya.
\det *.*

-- Tabel hasil import langsung bisa dikueri tanpa definisi manual.
SELECT kode, kota, manager
FROM pusat.cabang
ORDER BY kode;

kueri-join-lokal.sql

Unduh berkas
sql
-- Data pusat dibaca persis seperti tabel lokal biasa.
SELECT id, nama, kota, gaji
FROM karyawan_pusat
WHERE kota = 'Jakarta'
ORDER BY id;

-- Federasi sesungguhnya: tabel LOKAL (bonus cabang) di-join dengan tabel
-- REMOTE (karyawan pusat) dalam satu query. PostgreSQL menggabungkan
-- keduanya secara transparan.
CREATE TABLE bonus (
    karyawan_id integer PRIMARY KEY,
    bonus       integer NOT NULL
);

INSERT INTO bonus VALUES
    (1, 1500000),
    (2,  900000),
    (4,  750000);

SELECT k.nama, k.kota, b.bonus
FROM karyawan_pusat k
JOIN bonus b ON b.karyawan_id = k.id
ORDER BY k.id;

pasang-dan-server.sql

Unduh berkas
sql
-- postgres_fdw dipasang di database cabang (database kerja chapter ini).
-- Pemasangannya menuntut superuser karena extension ini membuka koneksi
-- keluar dari server.
CREATE EXTENSION postgres_fdw;

-- SERVER menjawab "ke mana menyambung": socket, port, dan database tujuan.
CREATE SERVER kantor_server FOREIGN DATA WRAPPER postgres_fdw
    OPTIONS (host '/home/z/my-project/pgrun', port '54329', dbname 'kantor_pusat_fdw');

-- USER MAPPING menjawab "sebagai siapa": kredensial untuk koneksi keluar.
CREATE USER MAPPING FOR postgres SERVER kantor_server
    OPTIONS (user 'postgres');

-- FOREIGN TABLE memetakan tabel pusat menjadi tabel yang tampak lokal.
-- OPTIONS schema_name dan table_name menunjuk tabel aslinya di pusat.
CREATE FOREIGN TABLE karyawan_pusat (
    id   integer NOT NULL,
    nama text    NOT NULL,
    kota text    NOT NULL,
    gaji integer NOT NULL
) SERVER kantor_server
    OPTIONS (schema_name 'public', table_name 'karyawan');

-- Katalog mencatat ketiga objek koneksi tersebut.
SELECT s.srvname AS server, w.fdwname AS wrapper, s.srvoptions AS opsi_server
FROM pg_foreign_server s
JOIN pg_foreign_data_wrapper w ON w.oid = s.srvfdw
ORDER BY s.srvname;

SELECT srvname AS server, umoptions AS opsi_user
FROM pg_user_mappings
ORDER BY srvname;

tulis-ke-remote.sql

Unduh berkas
sql
-- Foreign table postgres_fdw bersifat writable: INSERT diteruskan ke
-- database pusat. RETURNING bekerja seperti pada tabel biasa.
INSERT INTO karyawan_pusat VALUES (7, 'Farhan Gunawan', 'Jakarta', 9300000)
RETURNING id, nama;

-- UPDATE juga diteruskan; hanya baris yang cocok filter yang berubah di pusat.
UPDATE karyawan_pusat SET gaji = 8100000 WHERE id = 4
RETURNING id, gaji;

-- Baca ulang seluruh tabel: baris 7 yang baru dan gaji baris 4 yang
-- diubah kini terlihat dari sisi cabang.
SELECT id, nama, gaji
FROM karyawan_pusat
ORDER BY id;