Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how does one extract octets from an inet value in postgres sql?

I would like to convert an inet formatted IPv4 address into the integer components.

For example, turn '101.255.30.40' into oct1=101, oct2=255, oct3=30, and oct4=40.

There are regex expressions that should do this if I cast the inet as a varchar, but that seems inelegant. Is there a 1-line function for returning the nth octet of an inet?

select inet_to_octet('101.255.30.40', 4) as temp;  -- returns temp=40?
like image 916
Glenn Strycker Avatar asked Feb 17 '23 23:02

Glenn Strycker


2 Answers

I finally got an excellent answer from a co-worker...

For some flavors of sql, use "split_part" along with host(inet) to get the text field.

select split_part(host('101.255.30.40'::inet), '.', 1);
select split_part(host('101.255.30.40'::inet), '.', 2);
select split_part(host('101.255.30.40'::inet), '.', 3);
select split_part(host('101.255.30.40'::inet), '.', 4);

results in

101
255
30
40

If you want to get trickier and handle IPv6, use a mask to speed up the operation along with case statements to get the IP version:

select
   (case
      when family('101.255.30.40'::inet) = 4 then split_part(host(broadcast(set_masklen('101.255.30.40'::inet, 32))), '.', 4)::varchar
      when family('101.255.30.40'::inet) = 6 then split_part(host(broadcast(set_masklen('101.255.30.40'::inet, 64))), ':', 4)::varchar
      else null end)::varchar as octet4;

select
   (case
      when family('2604:8f00:4:80b0:3925:c69c:458:3f7b'::inet) = 4 then split_part(host(broadcast(set_masklen('2604:8f00:4:80b0:3925:c69c:458:3f7b'::inet, 32))), '.', 4)::varchar
      when family('2604:8f00:4:80b0:3925:c69c:458:3f7b'::inet) = 6 then split_part(host(broadcast(set_masklen('2604:8f00:4:80b0:3925:c69c:458:3f7b'::inet, 64))), ':', 4)::varchar
      else null end)::varchar as octet4;

results in

40
80b0

you can then add in a hex-to-int conversion into the case statement if you want to cast the IPv6 as a number instead.

like image 71
Glenn Strycker Avatar answered May 13 '23 19:05

Glenn Strycker


Unless you want to try to contribute the function to the inet datatype, you'll need to rely on a string based version. Maybe put something like this (but with some error checking) into an SQL function for easy access?:

CREATE OR REPLACE FUNCTION extract_octet(inet, integer) RETURNS integer AS $$
    SELECT ((regexp_split_to_array(host($1), E'\\.'))[$2])::int;
$$ LANGUAGE SQL;



select extract_octet(inet '192.26.22.2', 2)

Output: 26
like image 27
C. Ramseyer Avatar answered May 13 '23 18:05

C. Ramseyer