VillageSQL is a drop-in replacement for MySQL with extensions.
All examples in this guide work on VillageSQL. Install Now →
INET_ATON(). VillageSQL’s INET type provides network functions that make the same query more readable and work for both IPv4 and IPv6.
The Standard Approach: Bitwise Range Checks
For IPv4,INET_ATON() converts an address to an unsigned 32-bit integer. A subnet membership check is a bitwise AND: if (ip & netmask) == network_address, the IP is in the subnet.
BETWEEN approach is readable. The bitwise form is more composable when subnet parameters are variables.
Neither approach works for IPv6 — INET_ATON() only handles IPv4.
With VillageSQL: Network Functions on INET Columns
VillageSQL’svsql_network_address extension provides containment predicates that answer “is this address inside that subnet” directly — inet_contained_by(), inet_contains(), and inet_overlaps() — plus inet_network(), inet_set_masklen(), and inet_compare() for building the check by hand. All of them work on both IPv4 and IPv6 INET columns.
Subnet containment query
To check whether a stored IP belongs to a subnet, mask the address to the subnet’s prefix length and compare the result to the target network:Containment predicates
The extension ships PostgreSQL’s five containment predicates as functions. They take twoINET values and return 1, 0, or NULL:
The subnet query above becomes a single call:
192.168.1.5/24 and 192.168.1.9/24 are the same network,
and the strict forms return 0 for them while the _or_equals forms return 1.
Two addresses of different families never match: comparing an IPv4 value with an
IPv6 one returns 0 rather than raising an error, matching PostgreSQL.
Both arguments must be INET. A CIDR value is not coerced:
inet_from_string() on the subnet instead, as the example above does. These
functions were added in vsql_network_address 0.0.5.
Range-based containment with inet_compare
An alternative that avoids recomputing the network on every row — useful when you have the broadcast address available:IPv6 subnet queries (same pattern)
The sameinet_network() approach works for IPv6 without any changes:
Networks table pattern
A common use case: anetworks table with authorized CIDRs, and checking whether an incoming IP is in any of them.
Standard vs VillageSQL
Frequently Asked Questions
Does VillageSQL have PostgreSQL’s << containment operator?
The behaviour is there, as functions rather than operators: inet_contained_by() for <<, inet_contains() for >>, the _or_equals variants for <<= and >>=, and inet_overlaps() for &&. See Containment predicates.
Does the subnet query use the index?
Thecidr_to_string(inet_network(inet_set_masklen(...))) pattern does not use an index scan — it recomputes the network address for every row. For large tables, the inet_compare range approach is more index-friendly since it expresses the query as a range on the ip_address column.
Can I store subnets and query containment at the same time?
Yes. Store both the host address and the subnet inINET columns and compare them with inet_contained_by(host_ip, subnet). If the subnet is in a CIDR column, convert it first — the containment predicates take INET on both sides and do not coerce CIDR.
Troubleshooting
See also
- Storing IP Addresses in MySQL — the INET type used in subnet queries
- IPv6 Storage in MySQL — subnet queries work the same for IPv6 with INET
- Geolocation Lookups by IP in MySQL — going further with IP data beyond subnets

