Skip to content

Commit 0d880f4

Browse files
authored
Merge pull request #19 from hatamiarash7/more-ip-functions
feat: Add more IP functions
2 parents 439b9fc + 0eebe2e commit 0d880f4

File tree

15 files changed

+1866
-8
lines changed

15 files changed

+1866
-8
lines changed

README.md

Lines changed: 154 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ Table of Contents
3131
- [Get Tranco Ranking](#get-tranco-ranking)
3232
- [IP Address Functions](#ip-address-functions)
3333
- [IP Calculator](#ip-calculator)
34+
- [Validate IP Address](#validate-ip-address)
35+
- [Check Private IP](#check-private-ip)
36+
- [IP Version](#ip-version)
37+
- [IP to Integer / Integer to IP](#ip-to-integer--integer-to-ip)
3438
- [Get Extension Version](#get-extension-version)
3539
- [Build Requirements](#build-requirements)
3640
- [Debugging](#debugging)
@@ -427,6 +431,155 @@ D SELECT i.IP,
427431
└────────────────┴───────┘
428432
```
429433

434+
#### Validate IP Address
435+
436+
The `is_valid_ip` function checks whether a string is a valid IPv4 or IPv6 address. Returns a `BOOLEAN`.
437+
438+
```sql
439+
D SELECT is_valid_ip('192.168.1.1');
440+
┌────────────────────────────┐
441+
│ is_valid_ip('192.168.1.1') │
442+
boolean
443+
├────────────────────────────┤
444+
│ true │
445+
└────────────────────────────┘
446+
447+
D SELECT is_valid_ip('2001:db8::1');
448+
┌────────────────────────────┐
449+
│ is_valid_ip('2001:db8::1') │
450+
boolean
451+
├────────────────────────────┤
452+
│ true │
453+
└────────────────────────────┘
454+
455+
D SELECT is_valid_ip('not-an-ip');
456+
┌──────────────────────────┐
457+
│ is_valid_ip('not-an-ip') │
458+
boolean
459+
├──────────────────────────┤
460+
│ false │
461+
└──────────────────────────┘
462+
```
463+
464+
#### Check Private IP
465+
466+
The `is_private_ip` function checks whether an IP address belongs to a private or reserved range. Supports both IPv4 and IPv6. Returns `NULL` for invalid addresses.
467+
468+
IPv4 ranges covered:
469+
470+
- RFC 1918 (10/8, 172.16/12, 192.168/16)
471+
- loopback (127/8)
472+
- link-local (169.254/16)
473+
- carrier-grade NAT (100.64/10)
474+
- documentation (TEST-NET)
475+
- benchmarking (198.18/15)
476+
- multicast (224/4)
477+
- reserved (240/4)
478+
479+
IPv6 ranges covered:
480+
481+
- loopback (::1)
482+
- unspecified (::)
483+
- link-local (fe80::/10)
484+
- ULA (fc00::/7)
485+
- multicast (ff00::/8)
486+
- documentation (2001:db8::/32)
487+
- discard (100::/64)
488+
489+
```sql
490+
D SELECT is_private_ip('192.168.1.1');
491+
┌──────────────────────────────┐
492+
│ is_private_ip('192.168.1.1') │
493+
boolean
494+
├──────────────────────────────┤
495+
│ true │
496+
└──────────────────────────────┘
497+
498+
D SELECT is_private_ip('8.8.8.8');
499+
┌──────────────────────────┐
500+
│ is_private_ip('8.8.8.8') │
501+
boolean
502+
├──────────────────────────┤
503+
│ false │
504+
└──────────────────────────┘
505+
506+
D SELECT is_private_ip('fe80::c028:8eff:fe34:6e5f');
507+
┌────────────────────────────────────────────┐
508+
│ is_private_ip('fe80::c028:8eff:fe34:6e5f') │
509+
boolean
510+
├────────────────────────────────────────────┤
511+
│ true │
512+
└────────────────────────────────────────────┘
513+
```
514+
515+
#### IP Version
516+
517+
The `ip_version` function returns `4` for IPv4, `6` for IPv6, or `NULL` for invalid addresses.
518+
519+
```sql
520+
D SELECT ip_version('192.168.1.1');
521+
┌───────────────────────────┐
522+
│ ip_version('192.168.1.1') │
523+
│ int8 │
524+
├───────────────────────────┤
525+
4
526+
└───────────────────────────┘
527+
528+
D SELECT ip_version('::1');
529+
┌───────────────────┐
530+
│ ip_version('::1') │
531+
│ int8 │
532+
├───────────────────┤
533+
6
534+
└───────────────────┘
535+
```
536+
537+
#### IP to Integer / Integer to IP
538+
539+
The `ip_to_int` function converts an IPv4 address to its 32-bit unsigned integer representation. The `int_to_ip` function converts back. Returns `NULL` for invalid or IPv6 input (IPv6 requires 128-bit support).
540+
541+
```sql
542+
D SELECT ip_to_int('192.168.1.1');
543+
┌──────────────────────────┐
544+
│ ip_to_int('192.168.1.1') │
545+
│ uint64 │
546+
├──────────────────────────┤
547+
3232235777
548+
│ (3.23 billion) │
549+
└──────────────────────────┘
550+
551+
D SELECT int_to_ip(3232235777::UBIGINT);
552+
┌────────────────────────────────────────┐
553+
│ int_to_ip(CAST(3232235777 AS UBIGINT)) │
554+
varchar
555+
├────────────────────────────────────────┤
556+
192.168.1.1
557+
└────────────────────────────────────────┘
558+
559+
D SELECT int_to_ip('3232235777');
560+
┌─────────────────────────┐
561+
│ int_to_ip('3232235777') │
562+
varchar
563+
├─────────────────────────┤
564+
192.168.1.1
565+
└─────────────────────────┘
566+
```
567+
568+
These functions are useful for sorting IPs numerically or performing range comparisons:
569+
570+
```sql
571+
D SELECT ip, ip_to_int(ip) FROM ipv4_ips ORDER BY ip_to_int(ip);
572+
┌─────────────┬───────────────┐
573+
│ ip │ ip_to_int(ip) │
574+
varchar │ uint64 │
575+
├─────────────┼───────────────┤
576+
8.8.8.8134744072
577+
10.0.0.1167772161
578+
172.16.0.12886729729
579+
192.168.1.13232235777
580+
└─────────────┴───────────────┘
581+
```
582+
430583
### Get Extension Version
431584

432585
You can use the `netquack_version` function to get the extension version.
@@ -437,7 +590,7 @@ D SELECT * FROM netquack_version();
437590
│ version │
438591
varchar
439592
├─────────┤
440-
v1.8.1
593+
v1.9.0
441594
└─────────┘
442595
```
443596

@@ -473,11 +626,7 @@ Also, there will be stdout errors for background tasks like CURL.
473626
- [ ] Implement `normalize_url` function - Canonicalize URLs (lowercase scheme/host, remove default ports, sort query params, remove trailing slashes)
474627
- [ ] Implement `is_valid_url` function - Return whether a string is a well-formed URL
475628
- [ ] Implement `url_encode` / `url_decode` functions - Standalone percent-encoding and decoding
476-
- [ ] Implement `is_valid_ip` function - Return whether a string is a valid IPv4 or IPv6 address
477-
- [ ] Implement `is_private_ip` function - Check if an IP is in a private/reserved range (RFC 1918, loopback, link-local)
478-
- [ ] Implement `ip_to_int` / `int_to_ip` functions - Convert between dotted-quad notation and integer representation
479629
- [ ] Implement `ip_in_range` function - Check if an IP falls within a given CIDR block
480-
- [ ] Implement `ip_version` function - Return `4` or `6` for the IP version of a given address
481630
- [ ] Support internationalized domain names (IDNs)
482631
- [ ] Implement `punycode_encode` / `punycode_decode` functions - Convert internationalized domain names to/from ASCII-compatible encoding
483632
- [ ] Implement `is_valid_domain` function - Validate a domain name against RFC rules

docs/SUMMARY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828

2929
* [IP Address](ip-address/README.md)
3030
* [IP Calculator](ip-address/ip-calculator.md)
31+
* [Validate IP Address](ip-address/is-valid-ip.md)
32+
* [Check Private IP](ip-address/is-private-ip.md)
33+
* [IP Version](ip-address/ip-version.md)
34+
* [IP to Integer / Integer to IP](ip-address/ip-to-int.md)
3135

3236
## Collaboration
3337

docs/getting-started/debugging.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ D SELECT * FROM netquack_version();
3535
│ version │
3636
varchar
3737
├─────────┤
38-
v1.8.1
38+
v1.9.0
3939
└─────────┘
4040
```

docs/getting-started/quickstart.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ The compatibility between Netquack and DuckDB varies across versions.
2929

3030
| Version of Netquack | Version of DuckDB |
3131
| ------------------- | ----------------- |
32+
| v1.9.0 | v1.4.4 |
3233
| v1.8.1 | v1.4.4 |
3334
| v1.8.0 | v1.4.4 |
3435
| v1.4.0 | v1.4.0 |

docs/ip-address/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@ layout:
1515
# IP Address
1616

1717
This extension provides various functions for manipulating and analyzing IP addresses, including calculating networks, hosts, and subnet masks.
18+
19+
* [**IP Calculator**](ip-calculator.md) — Calculate network, broadcast, host range, and subnet masks from an IP/CIDR
20+
* [**Validate IP Address**](is-valid-ip.md) — Check if a string is a valid IPv4 or IPv6 address
21+
* [**Check Private IP**](is-private-ip.md) — Determine if an IP belongs to a private or reserved range
22+
* [**IP Version**](ip-version.md) — Detect whether an address is IPv4 or IPv6
23+
* [**IP to Integer / Integer to IP**](ip-to-int.md) — Convert between dotted-quad notation and integer representation

docs/ip-address/ip-to-int.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
layout:
3+
title:
4+
visible: true
5+
description:
6+
visible: false
7+
tableOfContents:
8+
visible: true
9+
outline:
10+
visible: true
11+
pagination:
12+
visible: true
13+
---
14+
15+
# IP to Integer / Integer to IP
16+
17+
Two functions for converting between IPv4 addresses and their 32-bit unsigned integer representation.
18+
19+
## ip\_to\_int
20+
21+
Converts an IPv4 address string to a `UBIGINT` (unsigned 64-bit integer, but the value will always fit in 32 bits). Returns `NULL` for invalid or IPv6 input.
22+
23+
```sql
24+
D SELECT ip_to_int('192.168.1.1');
25+
┌───────────────────────────┐
26+
│ ip_to_int('192.168.1.1') │
27+
│ uint64 │
28+
├───────────────────────────┤
29+
3232235777
30+
└───────────────────────────┘
31+
32+
D SELECT ip_to_int('10.0.0.1');
33+
┌───────────────────────┐
34+
│ ip_to_int('10.0.0.1') │
35+
│ uint64 │
36+
├───────────────────────┤
37+
167772161
38+
└───────────────────────┘
39+
40+
D SELECT ip_to_int('255.255.255.255');
41+
┌───────────────────────────────────┐
42+
│ ip_to_int('255.255.255.255') │
43+
│ uint64 │
44+
├───────────────────────────────────┤
45+
4294967295
46+
└───────────────────────────────────┘
47+
```
48+
49+
## int\_to\_ip
50+
51+
Converts a `UBIGINT` integer back to an IPv4 dotted-quad string. Returns `NULL` if the value exceeds the IPv4 range (`> 4294967295`).
52+
53+
```sql
54+
D SELECT int_to_ip(3232235777::UBIGINT);
55+
┌──────────────────────────────────┐
56+
│ int_to_ip(3232235777::UBIGINT) │
57+
varchar
58+
├──────────────────────────────────┤
59+
192.168.1.1
60+
└──────────────────────────────────┘
61+
62+
D SELECT int_to_ip(0::UBIGINT);
63+
┌──────────────────────┐
64+
│ int_to_ip(0::UBIGINT)│
65+
varchar
66+
├──────────────────────┤
67+
0.0.0.0
68+
└──────────────────────┘
69+
```
70+
71+
## Roundtrip
72+
73+
The two functions are inverses of each other:
74+
75+
```sql
76+
D SELECT int_to_ip(ip_to_int('192.168.1.1'));
77+
┌─────────────┐
78+
│ result │
79+
varchar
80+
├─────────────┤
81+
192.168.1.1
82+
└─────────────┘
83+
```
84+
85+
## Use Cases
86+
87+
**Sort IPs numerically** instead of lexicographically:
88+
89+
```sql
90+
D SELECT ip FROM ips ORDER BY ip_to_int(ip);
91+
┌─────────────┐
92+
│ ip │
93+
varchar
94+
├─────────────┤
95+
8.8.8.8
96+
10.0.0.1
97+
172.16.0.1
98+
192.168.1.1
99+
└─────────────┘
100+
```
101+
102+
**Range queries** using integer comparison:
103+
104+
```sql
105+
D SELECT ip FROM ips
106+
WHERE ip_to_int(ip) BETWEEN ip_to_int('10.0.0.0') AND ip_to_int('10.255.255.255');
107+
```

docs/ip-address/ip-version.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
layout:
3+
title:
4+
visible: true
5+
description:
6+
visible: false
7+
tableOfContents:
8+
visible: true
9+
outline:
10+
visible: true
11+
pagination:
12+
visible: true
13+
---
14+
15+
# IP Version
16+
17+
The `ip_version` function detects the version of an IP address. Returns `4` for IPv4, `6` for IPv6, or `NULL` for invalid input.
18+
19+
```sql
20+
D SELECT ip_version('192.168.1.1');
21+
┌───────────────────────────┐
22+
│ ip_version('192.168.1.1') │
23+
│ tinyint │
24+
├───────────────────────────┤
25+
4
26+
└───────────────────────────┘
27+
28+
D SELECT ip_version('::1');
29+
┌────────────────────┐
30+
│ ip_version('::1') │
31+
│ tinyint │
32+
├────────────────────┤
33+
6
34+
└────────────────────┘
35+
36+
D SELECT ip_version('not-an-ip');
37+
┌───────────────────────────┐
38+
│ ip_version('not-an-ip') │
39+
│ tinyint │
40+
├───────────────────────────┤
41+
NULL
42+
└───────────────────────────┘
43+
```
44+
45+
This is useful for filtering or branching logic based on IP type:
46+
47+
```sql
48+
D SELECT ip, ip_version(ip) AS version
49+
FROM my_ips
50+
WHERE ip_version(ip) = 4;
51+
```

0 commit comments

Comments
 (0)