Commit e1fbe118 authored by Tomas Vondra's avatar Tomas Vondra

Fix BRIN minmax-multi distance for inet type

The distance calculation ignored the mask, unlike the inet comparator,
which resulted in negative distance in some cases. Fixed by applying the
mask in brin_minmax_multi_distance_inet. I've considered simply calling
inetmi() to calculate the delta, but that does not consider mask either.

Reviewed-by: Zhihong Yu
Discussion: https://postgr.es/m/1a0a7b9d-9bda-e3a2-7fa4-88f15042a051%40enterprisedb.com
parent 7262f242
...@@ -2297,6 +2297,9 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) ...@@ -2297,6 +2297,9 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
inet *ipa = PG_GETARG_INET_PP(0); inet *ipa = PG_GETARG_INET_PP(0);
inet *ipb = PG_GETARG_INET_PP(1); inet *ipb = PG_GETARG_INET_PP(1);
int lena,
lenb;
/* /*
* If the addresses are from different families, consider them to be in * If the addresses are from different families, consider them to be in
* maximal possible distance (which is 1.0). * maximal possible distance (which is 1.0).
...@@ -2304,24 +2307,64 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) ...@@ -2304,24 +2307,64 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
if (ip_family(ipa) != ip_family(ipb)) if (ip_family(ipa) != ip_family(ipb))
PG_RETURN_FLOAT8(1.0); PG_RETURN_FLOAT8(1.0);
/* ipv4 or ipv6 */ addra = (unsigned char *) palloc(ip_addrsize(ipa));
if (ip_family(ipa) == PGSQL_AF_INET) memcpy(addra, ip_addr(ipa), ip_addrsize(ipa));
len = 4;
else addrb = (unsigned char *) palloc(ip_addrsize(ipb));
len = 16; /* NS_IN6ADDRSZ */ memcpy(addrb, ip_addr(ipb), ip_addrsize(ipb));
/*
* The length is calculated from the mask length, because we sort the
* addresses by first address in the range, so A.B.C.D/24 < A.B.C.1
* (the first range starts at A.B.C.0, which is before A.B.C.1). We
* don't want to produce negative delta in this case, so we just cut
* the extra bytes.
*
* XXX Maybe this should be a bit more careful and cut the bits, not
* just whole bytes.
*/
lena = ip_bits(ipa);
lenb = ip_bits(ipb);
len = ip_addrsize(ipa);
/* apply the network mask to both addresses */
for (i = 0; i < len; i++)
{
unsigned char mask;
int nbits;
addra = ip_addr(ipa); nbits = lena - (i * 8);
addrb = ip_addr(ipb); if (nbits < 8)
{
mask = (0xFF << (8 - nbits));
addra[i] = (addra[i] & mask);
}
nbits = lenb - (i * 8);
if (nbits < 8)
{
mask = (0xFF << (8 - nbits));
addrb[i] = (addrb[i] & mask);
}
}
/* Calculate the difference between the addresses. */
delta = 0; delta = 0;
for (i = len - 1; i >= 0; i--) for (i = len - 1; i >= 0; i--)
{ {
delta += (float8) addrb[i] - (float8) addra[i]; unsigned char a = addra[i];
unsigned char b = addrb[i];
delta += (float8) b - (float8) a;
delta /= 256; delta /= 256;
} }
Assert((delta >= 0) && (delta <= 1)); Assert((delta >= 0) && (delta <= 1));
pfree(addra);
pfree(addrb);
PG_RETURN_FLOAT8(delta); PG_RETURN_FLOAT8(delta);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment