The ip4r extension provides optimized data types for storing and querying IPv4 and IPv6 addresses in PolarDB for PostgreSQL.
Applicability
The following versions of PolarDB for PostgreSQL support ip4r:
| PostgreSQL version | Minimum minor engine version |
|---|---|
| PostgreSQL 17 | 2.0.17.6.4.0 |
| PostgreSQL 16 | 2.0.16.9.9.0 |
| PostgreSQL 15 | 2.0.15.14.6.0 |
| PostgreSQL 14 | 2.0.14.9.13.0 |
| PostgreSQL 11 | 2.0.11.9.36.0 |
View the minor engine version in the console or by running SHOW polardb_version;. If your minor engine version does not meet the requirements, upgrade the minor engine version.Why use ip4r
PostgreSQL's built-in inet and cidr types cover basic network address storage, but ip4r addresses two limitations that matter for production workloads:
Index support for containment queries. The built-in types have poor support for queries of the form column >>= parameter—finding which IP address ranges contain a given IP address. ip4r uses GiST indexes to make these containment lookups efficient.
Clearer semantics and lower overhead. The built-in inet type combines two distinct concepts: a network block and a specific IP address within that block. ip4r separates these clearly. It also uses fixed-length data types for single IP addresses, reducing the overhead that variable-length types impose when you only need to store IPv4 data.
Data types
ip4r provides six data types:
| Data type | Description |
|---|---|
ip4 | A single IPv4 address |
ip4r | An arbitrary IPv4 address range |
ip6 | A single IPv6 address |
ip6r | An arbitrary IPv6 address range |
ipaddress | A single IPv4 or IPv6 address |
iprange | An arbitrary IPv4 or IPv6 address range |
Single-address types
Three types store a single IP address:
ip4: Accepts input innnn.nnn.nnn.nnnformat. Stored as a 32-bit unsigned integer.ip6: Accepts input in standard hexadecimal format. Stored as two 64-bit values.ipaddress: Accepts eitherip4orip6format.
In the conversion table below, ipX represents any of these three types.
| Source type | Target type | Format |
|---|---|---|
ipX | text | text(ipX) or ipX::text (explicit) |
text | ipX | ipX(text) or text::ipX (explicit) |
ipX | cidr | cidr(ipX) or ipX::cidr (assignment) |
inet | ipX | ipX(inet) or inet::ipX (assignment) |
ipX | numeric | to_numeric(ipX) or ipX::numeric (explicit) |
numeric | ipX | ipX(numeric) or bigint::ipX (explicit) |
ip4 | bigint | to_bigint(ip4) or ip4::bigint (explicit) |
bigint | ip4 | ip4(bigint) or bigint::ip4 (explicit) |
ip4 | float8 | to_double(ip4) or ip4::float8 (explicit) |
float8 | ip4 | ip4(float8) or float8::ip4 (explicit) |
ipX | varbit | to_bit(ipX) or ipX::varbit (explicit) |
bit(32) | ip4 | ip4(bit) or bit::ip4 (explicit) |
bit(128) | ip6 | ip6(bit) or bit::ip6 (explicit) |
varbit | ipX | ipX(varbit) or varbit::ipX (explicit) |
ipX | bytea | to_bytea(ipX) or ipX::bytea (explicit) |
bytea | ipX | ipX(bytea) or bytea::ipX (explicit) |
ipX | ipXr | ipXr(ipX) or ipX::ipXr (implicit) |
ip4 | ipaddress | ipaddress(ip4) or ip4::ipaddress (implicit) |
ip6 | ipaddress | ipaddress(ip6) or ip6::ipaddress (implicit) |
ipaddress | ip4 | ip4(ipaddress) or ipaddress::ip4 (assignment) |
ipaddress | ip6 | ip6(ipaddress) or ipaddress::ip6 (assignment) |
Address range types
Three types store a range of IP addresses:
ip4r: Stores an IPv4 address range. For example,192.0.2.100-192.0.2.200. The CIDR notation192.0.2.0/24is equivalent to192.0.2.0-192.0.2.255.ip6r: Stores an IPv6 address range. For example,2001::1234-2001::2000:0000. The CIDR notation2001::/112is equivalent to2001::-2001::ffff.iprange: Accepts eitherip4rorip6rformat.
In the conversion table below, ipXr represents any of these three types.
| Source type | Target type | Format |
|---|---|---|
ipX | ipXr | ipXr(ipX) or ipX::ipXr (implicit) |
ipXr | text | text(ipXr) or ipXr::text (explicit) |
text | ipXr | ipXr(text) or text::ipXr (explicit) |
ipXr | cidr | cidr(ipXr) or ipXr::cidr (explicit) |
cidr | ipXr | ipXr(cidr) or cidr::ipXr (assignment) |
ipXr | varbit | to_bit(ipXr) or ipXr::varbit (explicit) |
varbit | ip4r | ip4r(varbit) or varbit::ip4r (explicit) |
varbit | ip6r | ip6r(varbit) or varbit::ip6r (explicit) |
Usage
Install the extension
CREATE EXTENSION ip4r;Create a table and load test data
CREATE TABLE ipranges (r iprange, r4 ip4r, r6 ip6r);
INSERT INTO ipranges
SELECT r, null, r
FROM (
SELECT ip6r(regexp_replace(ls, E'(....(?!$))', E'\\1:', 'g')::ip6,
regexp_replace(substring(ls FOR n + 1) || substring(us FROM n + 2),
E'(....(?!$))', E'\\1:', 'g')::ip6) AS r
FROM (
SELECT md5(i || ' lower 1') AS ls,
md5(i || ' upper 1') AS us,
(i % 11) + (i/11 % 11) + (i/121 % 11) AS n
FROM generate_series(1,13310) i) s1) s2;Create a GiST index
CREATE INDEX ipranges_r ON ipranges USING gist (r);Query with the contains operator
The following query uses the GiST index to find all ranges that contain the address 5555:::
EXPLAIN (COSTS OFF) SELECT * FROM ipranges WHERE r >>= '5555::' ORDER BY r;The query plan confirms index use:
QUERY PLAN
-----------------------------------------------------
Sort
Sort Key: r
-> Bitmap Heap Scan on ipranges
Recheck Cond: (r >>= '5555::'::iprange)
-> Bitmap Index Scan on ipranges_r
Index Cond: (r >>= '5555::'::iprange)
(6 rows)Remove the extension
DROP EXTENSION ip4r;