1- -- check IP syntax exactly in your PostgreSQL version
2- create or replace function is_inet (str text , is_notice boolean default false)
3- RETURNS boolean
1+ create or replace function public .is_inet(str text , is_notice boolean default false)
2+ returns boolean
43 returns null on null input
54 parallel unsafe -- (ERROR: cannot start subtransactions during a parallel operation)
65 stable
76 language plpgsql
7+ cost 5
88as
9- $$
9+ $func $
1010DECLARE
1111 exception_sqlstate text ;
1212 exception_message text ;
1313 exception_context text ;
14+ oct_length constant int default octet_length(str);
15+ 1416BEGIN
17+ -- https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
18+ 19+ -- try to detect IPv4
20+ if oct_length between 7 -- 0.0.0.0
21+ and 18 -- 255.255.255.255/32
22+ and str like ' %.%.%.%'
23+ and exists(
24+ select
25+ from regexp_matches(str,
26+ $regexp$
27+ ^
28+ (\d{1 ,3 }) \. (\d{1 ,3 }) \. (\d{1 ,3 }) \. (\d{1 ,3 }) # 1-4 addr 1..255
29+ (?:
30+ / (\d{1 ,2 }) # 5 mask 0..32
31+ )?
32+ $
33+ $regexp,ドル ' x' ) as t(m)
34+ where not exists(select
35+ from unnest(m[1 :4 ]) u(e)
36+ where e::int > 255 )
37+ and (m[5 ] is null or m[5 ]::int < 33 )
38+ )
39+ then
40+ return true;
41+ end if;
42+ 43+ -- try to detect IPv6
44+ -- https://stackoverflow.com/questions/166132/maximum-length-of-the-textual-representation-of-an-ipv6-address
45+ -- https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
46+ if not (oct_length between 2 -- ::
47+ and 45 -- 0000:0000:0000:0000:0000:ffff:255.255.255.255
48+ and str like ' %:%:%' ) then
49+ return false;
50+ end if;
51+ 52+ -- slow exception block
1553 BEGIN
16- RETURN (str::inet is not null );
54+ return (str::inet is not null );
1755 EXCEPTION WHEN others THEN
1856 IF is_notice THEN
1957 GET STACKED DIAGNOSTICS
@@ -27,18 +65,45 @@ BEGIN
2765 RETURN FALSE;
2866 END;
2967END;
30- $$;
68+ $func$;
69+ 70+ comment on function public.is_inet(str text, is_notice boolean) is ' Check IPv4 or IPv6 host address, and optionally its subnet' ;
3171
3272-- TEST
33- do $$
73+ do $do $
3474 begin
35- -- positive
36- assert is_inet(' 0.0.0.0' );
37- assert is_inet(' 255.255.255.255' );
38- assert is_inet(' 192.168.0.1/24' );
39- -- negative
40- assert not is_inet(' 0.0.0' );
41- assert not is_inet(' 255.255.255.256' );
42- assert not is_inet(' 192.168.0.1/244' );
75+ -- positive IPv4
76+ assert public .is_inet (' 0.0.0.0' );
77+ assert public .is_inet (' 1.2.3.4' );
78+ assert public .is_inet (' 11.22.33.44' );
79+ assert public .is_inet (' 255.255.255.255' );
80+ assert public .is_inet (' 1.2.3.4/0' );
81+ assert public .is_inet (' 1.2.3.4/32' );
82+ 83+ -- positive IPv6
84+ assert public .is_inet (' ::' );
85+ assert public .is_inet (' 1::' );
86+ assert public .is_inet (' ::1' );
87+ assert public .is_inet (' 1:2:3:4:5:6:7:8' );
88+ assert public .is_inet (' ::255.255.255.255' );
89+ assert public .is_inet (' ::ffff:0:255.255.255.255' );
90+ assert public .is_inet (' 0000:0000:0000:0000:0000:ffff:255.255.255.255' );
91+ assert public .is_inet (' 1:2:3:4:5:6:7:8/0' );
92+ assert public .is_inet (' 1:2:3:4:5:6:7:8/128' );
93+ 94+ -- negative IPv4
95+ assert not public .is_inet (' 1.2.3.4.' );
96+ assert not public .is_inet (' .1.2.3.4' );
97+ assert not public .is_inet (' 1:2:3.4' );
98+ assert not public .is_inet (' 0.0.0' );
99+ assert not public .is_inet (' 255.255.255.256' );
100+ assert not public .is_inet (' 192.168.0.1/-1' );
101+ assert not public .is_inet (' 192.168.0.1/33' );
102+ 103+ -- negative IPv6
104+ assert not public .is_inet (' :1:2:3:4:5:6:7:8' );
105+ assert not public .is_inet (' 1:2:3:4:5:6:7:8:' );
106+ assert not public .is_inet (' 1:2:3:4:5:6:7:8/-1' );
107+ assert not public .is_inet (' 1:2:3:4:5:6:7:8/129' );
43108 end
44- $$;
109+ $do $;
0 commit comments