Skip to main content

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples in this guide work on VillageSQL. Install Now →
Finding all rows where an IP address falls within a subnet requires comparing numeric IP values against a range. MySQL’s standard approach uses bitwise arithmetic with 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.
Both work for IPv4. The 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’s vsql_network_address extension provides inet_network(), inet_set_masklen(), and inet_compare() — enough to do subnet containment checks 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:
For a parameterized version where the subnet comes from a variable:

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 same inet_network() approach works for IPv6 without any changes:

Networks table pattern

A common use case: a networks table with authorized CIDRs, and checking whether an incoming IP is in any of them.

Standard vs VillageSQL

Frequently Asked Questions

Why doesn’t VillageSQL have a contains operator like PostgreSQL’s <<?

The current version provides functions rather than operators. The inet_network() + inet_set_masklen() pattern covers the same logic. Operator support may come in a future release.

Does the subnet query use the index?

The cidr_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 the host address in an INET column and the authorized subnet in a CIDR column. Compare them using inet_network(inet_set_masklen(host_ip, inet_masklen(subnet))) = cidr_to_string(subnet).

Troubleshooting

See also