From 94e983a0120c8dda71140d7a23d1b730ee656341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20D=C3=B6tsch?= Date: Mon, 21 Sep 2026 11:38:13 +0000 Subject: [PATCH] db: faster ptr() as an inlinable SQL function Same body as serveradmin_extras migration 0006_ptr_function: expands the address from the inet_send() bytes instead of a plpgsql regex loop. Returns identical text for every address in server and server_inet_attribute, runs 4.8x faster (465 ms vs 2251 ms over 41845 inet attributes) and is inlined by the planner, which makes the powerdns_records view's PTR branches cheap. The expression indexes below stay valid since the results do not change. --- db/ptr.sql | 46 +++++++++++++++------------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/db/ptr.sql b/db/ptr.sql index c7d022c73..0952aec85 100644 --- a/db/ptr.sql +++ b/db/ptr.sql @@ -1,36 +1,20 @@ +-- Reverse DNS name of an address. Plain SQL (no STRICT, no sub-SELECT, only +-- immutable functions) so Postgres inlines it into the calling query; it +-- expands the address from the inet_send() wire bytes (4 header bytes, then +-- the address), so the netmask is ignored. Kept in sync with +-- serveradmin_extras/serveradmin_powerdns/migrations/0006_ptr_function.py. create or replace function public.ptr(intern_ip inet) returns text -immutable strict language plpgsql as $$ -declare - ip_part text; - ip_text text; - ip_arr text[]; - ip_filled boolean; -begin - if family(intern_ip) = 4 then - return ( - select string_agg(part, '.' order by ord desc) || '.in-addr.arpa' - from regexp_split_to_table(host(intern_ip), '\.') with ordinality as s(part, ord) - ); +language sql immutable parallel safe as $$ +select case family(intern_ip) + when 4 then + get_byte(inet_send(intern_ip), 7)::text || '.' || + get_byte(inet_send(intern_ip), 6)::text || '.' || + get_byte(inet_send(intern_ip), 5)::text || '.' || + get_byte(inet_send(intern_ip), 4)::text || '.in-addr.arpa' else - ip_text := ''; - ip_arr := regexp_split_to_array(host(intern_ip), ':'); - ip_filled = false; - - foreach ip_part in array ip_arr loop - if length(ip_part) = 0 and not ip_filled then - ip_text := ip_text || repeat('0', 32 - (array_length(ip_arr, 1) - 1) * 4); - ip_filled = true; - else - ip_text := ip_text || lpad(ip_part, 4, '0'); - end if; - end loop; - - return ( - select string_agg(part[1], '.' order by ord desc) || '.ip6.arpa' - from regexp_matches(ip_text, '.', 'g') with ordinality as s(part, ord) - ); - end if; -end; + regexp_replace(reverse(encode(substring(inet_send(intern_ip) from 5), 'hex')), '(.)', '\1.', 'g') + || 'ip6.arpa' +end $$; create index concurrently server_ptr_idx on public.server(public.ptr(intern_ip) text_pattern_ops);