Hampir setiap aplikasi berorientasi jaringan menyimpan alamat IP: log keamanan mencatat sumber setiap permintaan, sistem akses menyimpan daftar jaringan terpercaya, dan aplikasi absensi menyimpan alamat MAC perangkat karyawan. Kalau alamat-alamat itu disimpan sebagai text, kita kehilangan tiga hal sekaligus: validasi otomatis, perbandingan yang benar, dan operator jaringan seperti “apakah IP ini berada di dalam subnet 10.0.0.0/8?”. PostgreSQL menjawabnya dengan tiga tipe khusus — inet, cidr, dan macaddr — yang kita bedah tuntas di chapter ini.
Tiga Tipe Alamat Jaringan #
Ketiga tipe ini menyimpan data yang tampak seperti teks, tetapi disimpan dan diperlakukan sebagai nilai biner terstruktur. Ringkasan karakternya:
| Tipe | Ukuran | Menyimpan | Contoh nilai |
|---|---|---|---|
inet | 7 atau 19 byte | IPv4/IPv6 host, netmask opsional | 192.168.1.5, 192.168.1.5/24 |
cidr | 7 atau 19 byte | IPv4/IPv6 jaringan | 192.168.1.0/24 |
macaddr | 6 byte | alamat MAC | 08:00:2b:01:02:03 |
macaddr8 | 8 byte | alamat MAC format EUI-64 | 08:00:2b:01:02:03:04:05 |
Perbedaan inet dan cidr sering membingungkan pemula, jadi mari kita luruskan sejak awal: inet menjawab “alamat siapa”, sedangkan cidr menjawab “jaringan yang mana”. Nilai inet boleh menulis netmask setelah garis miring (192.168.1.5/24), tetapi yang disimpan tetap satu host. Nilai cidr mewakili jaringan secara utuh, sehingga bagian host-nya wajib nol — 192.168.1.0/24, bukan 192.168.1.5/24.
Menulis inet, cidr, dan macaddr #
Perbedaan perilaku ketiga tipe paling cepat dipahami lewat contoh. File berikut menyisipkan nilai ke masing-masing tipe dan memperlihatkan bagaimana server menyimpannya kembali:
-- inet menyimpan satu host; netmask opsional setelah garis miring
SELECT '192.168.1.5'::inet AS host_tanpa_netmask;
SELECT '192.168.1.5/24'::inet AS host_dengan_netmask;
-- inet juga menerima IPv6
SELECT '2001:db8::1'::inet AS ipv6;
-- cidr menyimpan jaringan: bagian host wajib nol
SELECT '192.168.1.0/24'::cidr AS jaringan_v4;
SELECT '2001:db8::/32'::cidr AS jaringan_v6;
-- macaddr menyimpan alamat MAC 6 byte, macaddr8 untuk EUI-64 (8 byte)
SELECT '08:00:2b:01:02:03'::macaddr AS mac_enam_byte;
SELECT '08:00:2b:01:02:03:04:05'::macaddr8 AS mac_delapan_byte;Output yang Dihasilkan:
host_tanpa_netmask
--------------------
192.168.1.5
(1 row)
host_dengan_netmask
---------------------
192.168.1.5/24
(1 row)
ipv6
-------------
2001:db8::1
(1 row)
jaringan_v4
----------------
192.168.1.0/24
(1 row)
jaringan_v6
---------------
2001:db8::/32
(1 row)
mac_enam_byte
-------------------
08:00:2b:01:02:03
(1 row)
mac_delapan_byte
-------------------------
08:00:2b:01:02:03:04:05
(1 row)Tiga hal penting dari hasil tersebut. Pertama, inet tanpa netmask ditampilkan apa adanya (192.168.1.5) — secara implisit netmasknya /32, artinya tepat satu host. Kedua, inet dan cidr sama-sama menerima IPv6 tanpa sintaks tambahan; tipe yang sama menampung kedua versi, dan fungsi family() akan membedakannya nanti. Ketiga, macaddr menerima berbagai gaya penulisan (08:00:2b:..., 08002b-010203, 0800.2b01.0203) dan menormalkannya ke satu bentuk standar 08:00:2b:01:02:03 — keunggulan klasik tipe khusus dibanding kolom text yang menyimpan apa adanya gaya masukannya.
Tabel Log dengan Kolom inet #
Kegunaan nyata paling mudah dilihat pada tabel log firewall. Kolom sumber bertipe inet menyimpan IP sumber setiap kejadian, lalu fungsi-fungsi pengurai langsung bekerja pada kolom tersebut:
CREATE TABLE log_firewall (
id integer PRIMARY KEY,
waktu timestamp NOT NULL,
sumber inet NOT NULL,
aksi text NOT NULL
);
INSERT INTO log_firewall (id, waktu, sumber, aksi) VALUES
(1, '2026-03-01 09:15:00', '10.0.2.15', 'IZIN'),
(2, '2026-03-01 09:16:30', '203.0.113.7', 'TOLAK'),
(3, '2026-03-01 09:17:00', '192.168.1.23', 'IZIN'),
(4, '2026-03-01 09:18:45', '172.16.5.9', 'IZIN'),
(5, '2026-03-01 09:20:10', '203.0.113.99', 'TOLAK');
SELECT id, sumber,
host(sumber) AS ip_murni,
family(sumber) AS versi_ip,
aksi
FROM log_firewall
ORDER BY id;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 5
id | sumber | ip_murni | versi_ip | aksi
----+--------------+--------------+----------+-------
1 | 10.0.2.15 | 10.0.2.15 | 4 | IZIN
2 | 203.0.113.7 | 203.0.113.7 | 4 | TOLAK
3 | 192.168.1.23 | 192.168.1.23 | 4 | IZIN
4 | 172.16.5.9 | 172.16.5.9 | 4 | IZIN
5 | 203.0.113.99 | 203.0.113.99 | 4 | TOLAK
(5 rows)Dua fungsi bekerja di query penutup. host(sumber) mengupas netmask dan mengembalikan alamat murni — berguna saat kolomnya menyimpan nilai ber-netmask tetapi laporan hanya butuh IP-nya. family(sumber) mengembalikan 4 atau 6 sesuai versi IP, sehingga migrasi bertahap ke IPv6 bisa dipantau dengan query sederhana. Perhatikan juga bahwa lima baris log berasal dari jaringan berbeda-beda; di bagian berikutnya kita menguji keanggotaan IP-IP tersebut terhadap daftar jaringan terpercaya.
Operator Keanggotaan dan Tumpang Tindih #
Kekuatan sesungguhnya tipe jaringan ada pada operatornya. Empat operator berikut adalah yang paling sering dipakai:
| Operator | Makna | Contoh yang bernilai benar |
|---|---|---|
<< | IP/jaringan kiri terkandung di kanan | '192.168.1.5'::inet << '192.168.1.0/24'::cidr |
<<= | terkandung atau sama persis | '203.0.113.7'::inet <<= '203.0.113.7/32'::cidr |
>> / >>= | kebalikan: kanan terkandung di kiri | '192.168.1.0/24'::cidr >> '192.168.1.5'::inet |
&& | kedua jaringan tumpang tindih | '192.168.1.0/24' && '192.168.1.128/25' |
Berikut peragannya bersama fungsi-fungsi pengurai jaringan yang lain:
-- Operator keanggotaan dan tumpang tindih jaringan
SELECT '192.168.1.5'::inet << '192.168.1.0/24'::cidr AS ip_dalam_jaringan;
SELECT '192.168.1.0/24'::cidr >> '192.168.1.5'::inet AS jaringan_berisi_ip;
SELECT '192.168.1.0/24'::cidr && '192.168.1.128/25'::cidr AS jaringan_bertumpang;
SELECT '192.168.1.0/24'::cidr && '192.168.2.0/24'::cidr AS jaringan_terpisah;
-- Fungsi pengurai alamat dalam satu baris
SELECT family('192.168.1.5/24'::inet) AS versi,
host('192.168.1.5/24'::inet) AS hostnya,
masklen('192.168.1.5/24'::inet) AS prefix,
network('192.168.1.5/24'::inet) AS jaringan,
netmask('192.168.1.0/24'::cidr) AS subnet,
broadcast('192.168.1.0/24'::cidr) AS alamat_broadcast;Output yang Dihasilkan:
ip_dalam_jaringan
-------------------
t
(1 row)
jaringan_berisi_ip
--------------------
t
(1 row)
jaringan_bertumpang
---------------------
t
(1 row)
jaringan_terpisah
-------------------
f
(1 row)
versi | hostnya | prefix | jaringan | subnet | alamat_broadcast
-------+-------------+--------+----------------+---------------+------------------
4 | 192.168.1.5 | 24 | 192.168.1.0/24 | 255.255.255.0 | 192.168.1.255/24
(1 row)Query terakhir memperlihatkan perangkat lengkap pengurai alamat: family() untuk versi IP, host() untuk alamat host, masklen() untuk panjang prefix (jumlah bit 1 di netmask), network() untuk jaringan tempat host berada, netmask() untuk bentuk titik-desimal subnet, dan broadcast() untuk alamat broadcast jaringan. Seluruh hitungan bit ini dikerjakan server — pekerjaan yang kalau dikerjakan manual di atas kolom text berarti memparsing string dan menghitung bitmask sendiri di setiap query.
Studi Kasus: Allowlist dengan JOIN #
Skenario klasik tipe jaringan: tabel allowlist berisi daftar jaringan terpercaya, dan kita ingin tahu baris log mana saja yang berasal dari jaringan tersebut. Ini pekerjaan satu JOIN:
CREATE TABLE allowlist (
nama text PRIMARY KEY,
jaringan cidr NOT NULL
);
INSERT INTO allowlist (nama, jaringan) VALUES
('Kantor Pusat', '10.0.0.0/8'),
('VPN Mitra', '172.16.5.0/24'),
('Rumah Budi', '203.0.113.7/32');
-- << menolak kesamaan persis; <<= juga menerima IP tunggal /32
SELECT '203.0.113.7'::inet << '203.0.113.7/32'::cidr AS ketat,
'203.0.113.7'::inet <<= '203.0.113.7/32'::cidr AS longgar;
-- IP mana saja di log_firewall yang masuk allowlist?
SELECT l.id, l.sumber, l.aksi, a.nama AS cocok_dengan
FROM log_firewall l
JOIN allowlist a ON l.sumber <<= a.jaringan
ORDER BY l.id;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 3
ketat | longgar
-------+---------
f | t
(1 row)
id | sumber | aksi | cocok_dengan
----+-------------+-------+--------------
1 | 10.0.2.15 | IZIN | Kantor Pusat
2 | 203.0.113.7 | TOLAK | Rumah Budi
4 | 172.16.5.9 | IZIN | VPN Mitra
(3 rows)Perhatikan perbedaan hasil kolom ketat dan longgar pada query kedua: operator << menolak kesamaan persis — IP 203.0.113.7 tidak dianggap “terkandung di dalam” jaringan /32 yang beranggotakan satu IP itu sendiri. Operator <<= menutup celah tersebut, dan itulah alasan JOIN memakai <<=: entri allowlist /32 seperti Rumah Budi ikut tercocokkan. Hasil JOIN menampilkan tiga baris — dua aksi IZIN yang memang berasal dari jaringan kantor dan VPN, plus satu aksi TOLAK dari IP rumah Budi yang cocok dengan entri /32.
Perhatikan juga bagaimana demo ini memakai tabel log_firewall dari bagian sebelumnya tanpa membuatnya ulang — seluruh file SQL dalam satu chapter berjalan pada database yang sama, sehingga state menumpuk secara alami.
Validasi Otomatis Menolak Input Rusak #
Sama seperti bytea menolak digit hex palsu, tipe jaringan menolak alamat yang tidak sah — dan pesan errornya cukup spesifik untuk memandu perbaikan:
SELECT '999.168.1.5'::inet;Output yang Dihasilkan:
psql:ip-invalid.sql:1: ERROR: invalid input syntax for type inet: "999.168.1.5"
LINE 1: SELECT '999.168.1.5'::inet;
^SELECT '192.168.1.5/24'::cidr;Output yang Dihasilkan:
psql:cidr-invalid.sql:1: ERROR: invalid cidr value: "192.168.1.5/24"
LINE 1: SELECT '192.168.1.5/24'::cidr;
^
DETAIL: Value has bits set to right of mask.Error pertama terjadi karena 999 melebihi nilai maksimum satu oktet IPv4 (255). Error kedua adalah aturan khas cidr: nilai 192.168.1.5/24 memiliki bit host yang tidak nol di sisi kanan netmask /24, dan cidr menolaknya mentah-mentah. Kalau maksud Anda memang menyimpan host dengan netmask, tipe yang tepat adalah inet; kalau menyimpan jaringan, nol-kan bagian hostnya seperti 192.168.1.0/24.
Kapan Memakai Tipe Jaringan #
| Kebutuhan | Tipe yang dipakai | Alasan |
|---|---|---|
| Log akses/keamanan per IP | inet | Satu host per kejadian, mendukung IPv6 dan fungsi family() |
| Allowlist/blocklist jaringan | cidr | Entri berupa subnet utuh, dari /8 sampai /32 |
| Analisis “IP ini subnet mana?” | inet + cidr | Operator <</<<= menggantikan parsing manual |
| Data perangkat jaringan | macaddr / macaddr8 | Identifikasi NIC, normalisasi format otomatis |
Untuk kebutuhan di luar sana — misalnya mencari lokasi geografis dari IP — tipe ini tidak menyediakannya; itu pekerjaan tabel data geolokasi eksternal yang di-JOIN biasa.
Alamat jaringan memetakan data ke posisi di internet. Bab berikutnya menggeser fokus ke ruang fisik: tipe-tipe geometris point, line, box, path, polygon, dan circle yang menyimpan titik koordinat sampai bentuk dua dimensi lengkap dengan operator jarak dan kontainmen — topik chapter Geometric Types.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
allowlist.sql
Unduh berkasCREATE TABLE allowlist (
nama text PRIMARY KEY,
jaringan cidr NOT NULL
);
INSERT INTO allowlist (nama, jaringan) VALUES
('Kantor Pusat', '10.0.0.0/8'),
('VPN Mitra', '172.16.5.0/24'),
('Rumah Budi', '203.0.113.7/32');
-- << menolak kesamaan persis; <<= juga menerima IP tunggal /32
SELECT '203.0.113.7'::inet << '203.0.113.7/32'::cidr AS ketat,
'203.0.113.7'::inet <<= '203.0.113.7/32'::cidr AS longgar;
-- IP mana saja di log_firewall yang masuk allowlist?
SELECT l.id, l.sumber, l.aksi, a.nama AS cocok_dengan
FROM log_firewall l
JOIN allowlist a ON l.sumber <<= a.jaringan
ORDER BY l.id;
cidr-invalid.sql
Unduh berkasSELECT '192.168.1.5/24'::cidr;
ip-invalid.sql
Unduh berkasSELECT '999.168.1.5'::inet;
operator-jaringan.sql
Unduh berkas-- Operator keanggotaan dan tumpang tindih jaringan
SELECT '192.168.1.5'::inet << '192.168.1.0/24'::cidr AS ip_dalam_jaringan;
SELECT '192.168.1.0/24'::cidr >> '192.168.1.5'::inet AS jaringan_berisi_ip;
SELECT '192.168.1.0/24'::cidr && '192.168.1.128/25'::cidr AS jaringan_bertumpang;
SELECT '192.168.1.0/24'::cidr && '192.168.2.0/24'::cidr AS jaringan_terpisah;
-- Fungsi pengurai alamat dalam satu baris
SELECT family('192.168.1.5/24'::inet) AS versi,
host('192.168.1.5/24'::inet) AS hostnya,
masklen('192.168.1.5/24'::inet) AS prefix,
network('192.168.1.5/24'::inet) AS jaringan,
netmask('192.168.1.0/24'::cidr) AS subnet,
broadcast('192.168.1.0/24'::cidr) AS alamat_broadcast;
tabel-log.sql
Unduh berkasCREATE TABLE log_firewall (
id integer PRIMARY KEY,
waktu timestamp NOT NULL,
sumber inet NOT NULL,
aksi text NOT NULL
);
INSERT INTO log_firewall (id, waktu, sumber, aksi) VALUES
(1, '2026-03-01 09:15:00', '10.0.2.15', 'IZIN'),
(2, '2026-03-01 09:16:30', '203.0.113.7', 'TOLAK'),
(3, '2026-03-01 09:17:00', '192.168.1.23', 'IZIN'),
(4, '2026-03-01 09:18:45', '172.16.5.9', 'IZIN'),
(5, '2026-03-01 09:20:10', '203.0.113.99', 'TOLAK');
SELECT id, sumber,
host(sumber) AS ip_murni,
family(sumber) AS versi_ip,
aksi
FROM log_firewall
ORDER BY id;
tipe-dasar.sql
Unduh berkas-- inet menyimpan satu host; netmask opsional setelah garis miring
SELECT '192.168.1.5'::inet AS host_tanpa_netmask;
SELECT '192.168.1.5/24'::inet AS host_dengan_netmask;
-- inet juga menerima IPv6
SELECT '2001:db8::1'::inet AS ipv6;
-- cidr menyimpan jaringan: bagian host wajib nol
SELECT '192.168.1.0/24'::cidr AS jaringan_v4;
SELECT '2001:db8::/32'::cidr AS jaringan_v6;
-- macaddr menyimpan alamat MAC 6 byte, macaddr8 untuk EUI-64 (8 byte)
SELECT '08:00:2b:01:02:03'::macaddr AS mac_enam_byte;
SELECT '08:00:2b:01:02:03:04:05'::macaddr8 AS mac_delapan_byte;