Wierzę, że znalazłem rozwiązanie. Polega na modyfikacji danych najpierw, a następnie masowaniu danych wejściowych. Oto, co zadziałało.
Najpierw dane muszą zostać przekonwertowane, aby wszystkie adresy były pełne, bez skracania, z usuniętymi separatorami średnikowymi. Przykładowe dane pokazane w moim pytaniu są konwertowane na:
t_start | t_end | t_country_code
----------------------------------+----------------------------------+----------------
00000000000000000000000000000000 | 00ffffffffffffffffffffffffffffff | ZZ
01000000000000000000000000000000 | 01ffffffffffffffffffffffffffffff | ZZ
...
20000000000000000000000000000000 | 2000ffffffffffffffffffffffffffff | ZZ
...
20011200000000000000000000000000 | 20011200ffffffffffffffffffffffff | MX
...
To jest to, co jest przechowywane w bazie danych.
Kolejnym krokiem było przekonwertowanie adresu IP otrzymanego w kodzie na ten sam format. Odbywa się to w PHP za pomocą następującego kodu (załóżmy, że $ip_address
to przychodzący adres IPv6):
$addr_bin = inet_pton($ip_address);
$bytes = unpack('n*', $addr_bin);
$ip_address = implode('', array_map(function ($b) {return sprintf("%04x", $b); }, $bytes));
Teraz zmienna $ip_adress
będzie zawierać pełny adres IPv6, na przykład
:: => 00000000000000000000000000000000
2001:1200::ab => 200112000000000000000000000000ab
i tak dalej.
Teraz możesz po prostu porównać ten pełny adres z zakresami w bazie danych. Do bazy danych dodałem drugą funkcję zajmującą się adresami IPv6, która wygląda tak:
CREATE OR REPLACE FUNCTION get_country_for_ipv6(character varying)
RETURNS character varying AS
$BODY$
declare
ip ALIAS for $1;
ccode varchar;
begin
select into ccode t_country_code from ipv6_to_country where addr between n_from and n_to limit 1;
if ccode is null then
ccode := '';
end if;
return ccode;
end;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Wreszcie w moim kodzie php dodałem kod, który wywołuje jedną lub drugą funkcję Postgres na podstawie wejściowego adresu ip.