postgresql/src/backend/utils/adt/network_gist.c

811 lines
22 KiB
C

/*-------------------------------------------------------------------------
*
* network_gist.c
* GiST support for network types.
*
* The key thing to understand about this code is the definition of the
* "union" of a set of INET/CIDR values. It works like this:
* 1. If the values are not all of the same IP address family, the "union"
* is a dummy value with family number zero, minbits zero, commonbits zero,
* address all zeroes. Otherwise:
* 2. The union has the common IP address family number.
* 3. The union's minbits value is the smallest netmask length ("ip_bits")
* of all the input values.
* 4. Let C be the number of leading address bits that are in common among
* all the input values (C ranges from 0 to ip_maxbits for the family).
* 5. The union's commonbits value is C.
* 6. The union's address value is the same as the common prefix for its
* first C bits, and is zeroes to the right of that. The physical width
* of the address value is ip_maxbits for the address family.
*
* In a leaf index entry (representing a single key), commonbits is equal to
* ip_maxbits for the address family, minbits is the same as the represented
* value's ip_bits, and the address is equal to the represented address.
* Although it may appear that we're wasting a byte by storing the union
* format and not just the represented INET/CIDR value in leaf keys, the
* extra byte is actually "free" because of alignment considerations.
*
* Note that this design tracks minbits and commonbits independently; in any
* given union value, either might be smaller than the other. This does not
* help us much when descending the tree, because of the way inet comparison
* is defined: at non-leaf nodes we can't compare more than minbits bits
* even if we know them. However, it greatly improves the quality of split
* decisions. Preliminary testing suggests that searches are as much as
* twice as fast as for a simpler design in which a single field doubles as
* the common prefix length and the minimum ip_bits value.
*
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/utils/adt/network_gist.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <sys/socket.h>
#include "access/gist.h"
#include "access/stratnum.h"
#include "utils/builtins.h"
#include "utils/inet.h"
#include "varatt.h"
/*
* Operator strategy numbers used in the GiST inet_ops opclass
*/
#define INETSTRAT_OVERLAPS RTOverlapStrategyNumber
#define INETSTRAT_EQ RTEqualStrategyNumber
#define INETSTRAT_NE RTNotEqualStrategyNumber
#define INETSTRAT_LT RTLessStrategyNumber
#define INETSTRAT_LE RTLessEqualStrategyNumber
#define INETSTRAT_GT RTGreaterStrategyNumber
#define INETSTRAT_GE RTGreaterEqualStrategyNumber
#define INETSTRAT_SUB RTSubStrategyNumber
#define INETSTRAT_SUBEQ RTSubEqualStrategyNumber
#define INETSTRAT_SUP RTSuperStrategyNumber
#define INETSTRAT_SUPEQ RTSuperEqualStrategyNumber
/*
* Representation of a GiST INET/CIDR index key. This is not identical to
* INET/CIDR because we need to keep track of the length of the common address
* prefix as well as the minimum netmask length. However, as long as it
* follows varlena header rules, the core GiST code won't know the difference.
* For simplicity we always use 1-byte-header varlena format.
*/
typedef struct GistInetKey
{
uint8 va_header; /* varlena header --- don't touch directly */
unsigned char family; /* PGSQL_AF_INET, PGSQL_AF_INET6, or zero */
unsigned char minbits; /* minimum number of bits in netmask */
unsigned char commonbits; /* number of common prefix bits in addresses */
unsigned char ipaddr[16]; /* up to 128 bits of common address */
} GistInetKey;
#define DatumGetInetKeyP(X) ((GistInetKey *) DatumGetPointer(X))
#define InetKeyPGetDatum(X) PointerGetDatum(X)
/*
* Access macros; not really exciting, but we use these for notational
* consistency with access to INET/CIDR values. Note that family-zero values
* are stored with 4 bytes of address, not 16.
*/
#define gk_ip_family(gkptr) ((gkptr)->family)
#define gk_ip_minbits(gkptr) ((gkptr)->minbits)
#define gk_ip_commonbits(gkptr) ((gkptr)->commonbits)
#define gk_ip_addr(gkptr) ((gkptr)->ipaddr)
#define ip_family_maxbits(fam) ((fam) == PGSQL_AF_INET6 ? 128 : 32)
/* These require that the family field has been set: */
#define gk_ip_addrsize(gkptr) \
(gk_ip_family(gkptr) == PGSQL_AF_INET6 ? 16 : 4)
#define gk_ip_maxbits(gkptr) \
ip_family_maxbits(gk_ip_family(gkptr))
#define SET_GK_VARSIZE(dst) \
SET_VARSIZE_SHORT(dst, offsetof(GistInetKey, ipaddr) + gk_ip_addrsize(dst))
/*
* The GiST query consistency check
*/
Datum
inet_gist_consistent(PG_FUNCTION_ARGS)
{
GISTENTRY *ent = (GISTENTRY *) PG_GETARG_POINTER(0);
inet *query = PG_GETARG_INET_PP(1);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
GistInetKey *key = DatumGetInetKeyP(ent->key);
int minbits,
order;
/* All operators served by this function are exact. */
*recheck = false;
/*
* Check 0: different families
*
* If key represents multiple address families, its children could match
* anything. This can only happen on an inner index page.
*/
if (gk_ip_family(key) == 0)
{
Assert(!GIST_LEAF(ent));
PG_RETURN_BOOL(true);
}
/*
* Check 1: different families
*
* Matching families do not help any of the strategies.
*/
if (gk_ip_family(key) != ip_family(query))
{
switch (strategy)
{
case INETSTRAT_LT:
case INETSTRAT_LE:
if (gk_ip_family(key) < ip_family(query))
PG_RETURN_BOOL(true);
break;
case INETSTRAT_GE:
case INETSTRAT_GT:
if (gk_ip_family(key) > ip_family(query))
PG_RETURN_BOOL(true);
break;
case INETSTRAT_NE:
PG_RETURN_BOOL(true);
}
/* For all other cases, we can be sure there is no match */
PG_RETURN_BOOL(false);
}
/*
* Check 2: network bit count
*
* Network bit count (ip_bits) helps to check leaves for sub network and
* sup network operators. At non-leaf nodes, we know every child value
* has ip_bits >= gk_ip_minbits(key), so we can avoid descending in some
* cases too.
*/
switch (strategy)
{
case INETSTRAT_SUB:
if (GIST_LEAF(ent) && gk_ip_minbits(key) <= ip_bits(query))
PG_RETURN_BOOL(false);
break;
case INETSTRAT_SUBEQ:
if (GIST_LEAF(ent) && gk_ip_minbits(key) < ip_bits(query))
PG_RETURN_BOOL(false);
break;
case INETSTRAT_SUPEQ:
case INETSTRAT_EQ:
if (gk_ip_minbits(key) > ip_bits(query))
PG_RETURN_BOOL(false);
break;
case INETSTRAT_SUP:
if (gk_ip_minbits(key) >= ip_bits(query))
PG_RETURN_BOOL(false);
break;
}
/*
* Check 3: common network bits
*
* Compare available common prefix bits to the query, but not beyond
* either the query's netmask or the minimum netmask among the represented
* values. If these bits don't match the query, we have our answer (and
* may or may not need to descend, depending on the operator). If they do
* match, and we are not at a leaf, we descend in all cases.
*
* Note this is the final check for operators that only consider the
* network part of the address.
*/
minbits = Min(gk_ip_commonbits(key), gk_ip_minbits(key));
minbits = Min(minbits, ip_bits(query));
order = bitncmp(gk_ip_addr(key), ip_addr(query), minbits);
switch (strategy)
{
case INETSTRAT_SUB:
case INETSTRAT_SUBEQ:
case INETSTRAT_OVERLAPS:
case INETSTRAT_SUPEQ:
case INETSTRAT_SUP:
PG_RETURN_BOOL(order == 0);
case INETSTRAT_LT:
case INETSTRAT_LE:
if (order > 0)
PG_RETURN_BOOL(false);
if (order < 0 || !GIST_LEAF(ent))
PG_RETURN_BOOL(true);
break;
case INETSTRAT_EQ:
if (order != 0)
PG_RETURN_BOOL(false);
if (!GIST_LEAF(ent))
PG_RETURN_BOOL(true);
break;
case INETSTRAT_GE:
case INETSTRAT_GT:
if (order < 0)
PG_RETURN_BOOL(false);
if (order > 0 || !GIST_LEAF(ent))
PG_RETURN_BOOL(true);
break;
case INETSTRAT_NE:
if (order != 0 || !GIST_LEAF(ent))
PG_RETURN_BOOL(true);
break;
}
/*
* Remaining checks are only for leaves and basic comparison strategies.
* See network_cmp_internal() in network.c for the implementation we need
* to match. Note that in a leaf key, commonbits should equal the address
* length, so we compared the whole network parts above.
*/
Assert(GIST_LEAF(ent));
/*
* Check 4: network bit count
*
* Next step is to compare netmask widths.
*/
switch (strategy)
{
case INETSTRAT_LT:
case INETSTRAT_LE:
if (gk_ip_minbits(key) < ip_bits(query))
PG_RETURN_BOOL(true);
if (gk_ip_minbits(key) > ip_bits(query))
PG_RETURN_BOOL(false);
break;
case INETSTRAT_EQ:
if (gk_ip_minbits(key) != ip_bits(query))
PG_RETURN_BOOL(false);
break;
case INETSTRAT_GE:
case INETSTRAT_GT:
if (gk_ip_minbits(key) > ip_bits(query))
PG_RETURN_BOOL(true);
if (gk_ip_minbits(key) < ip_bits(query))
PG_RETURN_BOOL(false);
break;
case INETSTRAT_NE:
if (gk_ip_minbits(key) != ip_bits(query))
PG_RETURN_BOOL(true);
break;
}
/*
* Check 5: whole address
*
* Netmask bit counts are the same, so check all the address bits.
*/
order = bitncmp(gk_ip_addr(key), ip_addr(query), gk_ip_maxbits(key));
switch (strategy)
{
case INETSTRAT_LT:
PG_RETURN_BOOL(order < 0);
case INETSTRAT_LE:
PG_RETURN_BOOL(order <= 0);
case INETSTRAT_EQ:
PG_RETURN_BOOL(order == 0);
case INETSTRAT_GE:
PG_RETURN_BOOL(order >= 0);
case INETSTRAT_GT:
PG_RETURN_BOOL(order > 0);
case INETSTRAT_NE:
PG_RETURN_BOOL(order != 0);
}
elog(ERROR, "unknown strategy for inet GiST");
PG_RETURN_BOOL(false); /* keep compiler quiet */
}
/*
* Calculate parameters of the union of some GistInetKeys.
*
* Examine the keys in elements m..n inclusive of the GISTENTRY array,
* and compute these output parameters:
* *minfamily_p = minimum IP address family number
* *maxfamily_p = maximum IP address family number
* *minbits_p = minimum netmask width
* *commonbits_p = number of leading bits in common among the addresses
*
* minbits and commonbits are forced to zero if there's more than one
* address family.
*/
static void
calc_inet_union_params(GISTENTRY *ent,
int m, int n,
int *minfamily_p,
int *maxfamily_p,
int *minbits_p,
int *commonbits_p)
{
int minfamily,
maxfamily,
minbits,
commonbits;
unsigned char *addr;
GistInetKey *tmp;
int i;
/* Must be at least one key. */
Assert(m <= n);
/* Initialize variables using the first key. */
tmp = DatumGetInetKeyP(ent[m].key);
minfamily = maxfamily = gk_ip_family(tmp);
minbits = gk_ip_minbits(tmp);
commonbits = gk_ip_commonbits(tmp);
addr = gk_ip_addr(tmp);
/* Scan remaining keys. */
for (i = m + 1; i <= n; i++)
{
tmp = DatumGetInetKeyP(ent[i].key);
/* Determine range of family numbers */
if (minfamily > gk_ip_family(tmp))
minfamily = gk_ip_family(tmp);
if (maxfamily < gk_ip_family(tmp))
maxfamily = gk_ip_family(tmp);
/* Find minimum minbits */
if (minbits > gk_ip_minbits(tmp))
minbits = gk_ip_minbits(tmp);
/* Find minimum number of bits in common */
if (commonbits > gk_ip_commonbits(tmp))
commonbits = gk_ip_commonbits(tmp);
if (commonbits > 0)
commonbits = bitncommon(addr, gk_ip_addr(tmp), commonbits);
}
/* Force minbits/commonbits to zero if more than one family. */
if (minfamily != maxfamily)
minbits = commonbits = 0;
*minfamily_p = minfamily;
*maxfamily_p = maxfamily;
*minbits_p = minbits;
*commonbits_p = commonbits;
}
/*
* Same as above, but the GISTENTRY elements to examine are those with
* indices listed in the offsets[] array.
*/
static void
calc_inet_union_params_indexed(GISTENTRY *ent,
OffsetNumber *offsets, int noffsets,
int *minfamily_p,
int *maxfamily_p,
int *minbits_p,
int *commonbits_p)
{
int minfamily,
maxfamily,
minbits,
commonbits;
unsigned char *addr;
GistInetKey *tmp;
int i;
/* Must be at least one key. */
Assert(noffsets > 0);
/* Initialize variables using the first key. */
tmp = DatumGetInetKeyP(ent[offsets[0]].key);
minfamily = maxfamily = gk_ip_family(tmp);
minbits = gk_ip_minbits(tmp);
commonbits = gk_ip_commonbits(tmp);
addr = gk_ip_addr(tmp);
/* Scan remaining keys. */
for (i = 1; i < noffsets; i++)
{
tmp = DatumGetInetKeyP(ent[offsets[i]].key);
/* Determine range of family numbers */
if (minfamily > gk_ip_family(tmp))
minfamily = gk_ip_family(tmp);
if (maxfamily < gk_ip_family(tmp))
maxfamily = gk_ip_family(tmp);
/* Find minimum minbits */
if (minbits > gk_ip_minbits(tmp))
minbits = gk_ip_minbits(tmp);
/* Find minimum number of bits in common */
if (commonbits > gk_ip_commonbits(tmp))
commonbits = gk_ip_commonbits(tmp);
if (commonbits > 0)
commonbits = bitncommon(addr, gk_ip_addr(tmp), commonbits);
}
/* Force minbits/commonbits to zero if more than one family. */
if (minfamily != maxfamily)
minbits = commonbits = 0;
*minfamily_p = minfamily;
*maxfamily_p = maxfamily;
*minbits_p = minbits;
*commonbits_p = commonbits;
}
/*
* Construct a GistInetKey representing a union value.
*
* Inputs are the family/minbits/commonbits values to use, plus a pointer to
* the address field of one of the union inputs. (Since we're going to copy
* just the bits-in-common, it doesn't matter which one.)
*/
static GistInetKey *
build_inet_union_key(int family, int minbits, int commonbits,
unsigned char *addr)
{
GistInetKey *result;
/* Make sure any unused bits are zeroed. */
result = (GistInetKey *) palloc0(sizeof(GistInetKey));
gk_ip_family(result) = family;
gk_ip_minbits(result) = minbits;
gk_ip_commonbits(result) = commonbits;
/* Clone appropriate bytes of the address. */
if (commonbits > 0)
memcpy(gk_ip_addr(result), addr, (commonbits + 7) / 8);
/* Clean any unwanted bits in the last partial byte. */
if (commonbits % 8 != 0)
gk_ip_addr(result)[commonbits / 8] &= ~(0xFF >> (commonbits % 8));
/* Set varlena header correctly. */
SET_GK_VARSIZE(result);
return result;
}
/*
* The GiST union function
*
* See comments at head of file for the definition of the union.
*/
Datum
inet_gist_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GISTENTRY *ent = entryvec->vector;
int minfamily,
maxfamily,
minbits,
commonbits;
unsigned char *addr;
GistInetKey *tmp,
*result;
/* Determine parameters of the union. */
calc_inet_union_params(ent, 0, entryvec->n - 1,
&minfamily, &maxfamily,
&minbits, &commonbits);
/* If more than one family, emit family number zero. */
if (minfamily != maxfamily)
minfamily = 0;
/* Initialize address using the first key. */
tmp = DatumGetInetKeyP(ent[0].key);
addr = gk_ip_addr(tmp);
/* Construct the union value. */
result = build_inet_union_key(minfamily, minbits, commonbits, addr);
PG_RETURN_POINTER(result);
}
/*
* The GiST compress function
*
* Convert an inet value to GistInetKey.
*/
Datum
inet_gist_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval;
if (entry->leafkey)
{
retval = palloc(sizeof(GISTENTRY));
if (DatumGetPointer(entry->key) != NULL)
{
inet *in = DatumGetInetPP(entry->key);
GistInetKey *r;
r = (GistInetKey *) palloc0(sizeof(GistInetKey));
gk_ip_family(r) = ip_family(in);
gk_ip_minbits(r) = ip_bits(in);
gk_ip_commonbits(r) = gk_ip_maxbits(r);
memcpy(gk_ip_addr(r), ip_addr(in), gk_ip_addrsize(r));
SET_GK_VARSIZE(r);
gistentryinit(*retval, PointerGetDatum(r),
entry->rel, entry->page,
entry->offset, false);
}
else
{
gistentryinit(*retval, (Datum) 0,
entry->rel, entry->page,
entry->offset, false);
}
}
else
retval = entry;
PG_RETURN_POINTER(retval);
}
/*
* We do not need a decompress function, because the other GiST inet
* support functions work with the GistInetKey representation.
*/
/*
* The GiST fetch function
*
* Reconstruct the original inet datum from a GistInetKey.
*/
Datum
inet_gist_fetch(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GistInetKey *key = DatumGetInetKeyP(entry->key);
GISTENTRY *retval;
inet *dst;
dst = (inet *) palloc0(sizeof(inet));
ip_family(dst) = gk_ip_family(key);
ip_bits(dst) = gk_ip_minbits(key);
memcpy(ip_addr(dst), gk_ip_addr(key), ip_addrsize(dst));
SET_INET_VARSIZE(dst);
retval = palloc(sizeof(GISTENTRY));
gistentryinit(*retval, InetPGetDatum(dst), entry->rel, entry->page,
entry->offset, false);
PG_RETURN_POINTER(retval);
}
/*
* The GiST page split penalty function
*
* Charge a large penalty if address family doesn't match, or a somewhat
* smaller one if the new value would degrade the union's minbits
* (minimum netmask width). Otherwise, penalty is inverse of the
* new number of common address bits.
*/
Datum
inet_gist_penalty(PG_FUNCTION_ARGS)
{
GISTENTRY *origent = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *newent = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
GistInetKey *orig = DatumGetInetKeyP(origent->key),
*new = DatumGetInetKeyP(newent->key);
int commonbits;
if (gk_ip_family(orig) == gk_ip_family(new))
{
if (gk_ip_minbits(orig) <= gk_ip_minbits(new))
{
commonbits = bitncommon(gk_ip_addr(orig), gk_ip_addr(new),
Min(gk_ip_commonbits(orig),
gk_ip_commonbits(new)));
if (commonbits > 0)
*penalty = 1.0f / commonbits;
else
*penalty = 2;
}
else
*penalty = 3;
}
else
*penalty = 4;
PG_RETURN_POINTER(penalty);
}
/*
* The GiST PickSplit method
*
* There are two ways to split. First one is to split by address families,
* if there are multiple families appearing in the input.
*
* The second and more common way is to split by addresses. To achieve this,
* determine the number of leading bits shared by all the keys, then split on
* the next bit. (We don't currently consider the netmask widths while doing
* this; should we?) If we fail to get a nontrivial split that way, split
* 50-50.
*/
Datum
inet_gist_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *splitvec = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
GISTENTRY *ent = entryvec->vector;
int minfamily,
maxfamily,
minbits,
commonbits;
unsigned char *addr;
GistInetKey *tmp,
*left_union,
*right_union;
int maxoff,
nbytes;
OffsetNumber i,
*left,
*right;
maxoff = entryvec->n - 1;
nbytes = (maxoff + 1) * sizeof(OffsetNumber);
left = (OffsetNumber *) palloc(nbytes);
right = (OffsetNumber *) palloc(nbytes);
splitvec->spl_left = left;
splitvec->spl_right = right;
splitvec->spl_nleft = 0;
splitvec->spl_nright = 0;
/* Determine parameters of the union of all the inputs. */
calc_inet_union_params(ent, FirstOffsetNumber, maxoff,
&minfamily, &maxfamily,
&minbits, &commonbits);
if (minfamily != maxfamily)
{
/* Multiple families, so split by family. */
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
{
/*
* If there's more than 2 families, all but maxfamily go into the
* left union. This could only happen if the inputs include some
* IPv4, some IPv6, and some already-multiple-family unions.
*/
tmp = DatumGetInetKeyP(ent[i].key);
if (gk_ip_family(tmp) != maxfamily)
left[splitvec->spl_nleft++] = i;
else
right[splitvec->spl_nright++] = i;
}
}
else
{
/*
* Split on the next bit after the common bits. If that yields a
* trivial split, try the next bit position to the right. Repeat till
* success; or if we run out of bits, do an arbitrary 50-50 split.
*/
int maxbits = ip_family_maxbits(minfamily);
while (commonbits < maxbits)
{
/* Split using the commonbits'th bit position. */
int bitbyte = commonbits / 8;
int bitmask = 0x80 >> (commonbits % 8);
splitvec->spl_nleft = splitvec->spl_nright = 0;
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
{
tmp = DatumGetInetKeyP(ent[i].key);
addr = gk_ip_addr(tmp);
if ((addr[bitbyte] & bitmask) == 0)
left[splitvec->spl_nleft++] = i;
else
right[splitvec->spl_nright++] = i;
}
if (splitvec->spl_nleft > 0 && splitvec->spl_nright > 0)
break; /* success */
commonbits++;
}
if (commonbits >= maxbits)
{
/* Failed ... do a 50-50 split. */
splitvec->spl_nleft = splitvec->spl_nright = 0;
for (i = FirstOffsetNumber; i <= maxoff / 2; i = OffsetNumberNext(i))
{
left[splitvec->spl_nleft++] = i;
}
for (; i <= maxoff; i = OffsetNumberNext(i))
{
right[splitvec->spl_nright++] = i;
}
}
}
/*
* Compute the union value for each side from scratch. In most cases we
* could approximate the union values with what we already know, but this
* ensures that each side has minbits and commonbits set as high as
* possible.
*/
calc_inet_union_params_indexed(ent, left, splitvec->spl_nleft,
&minfamily, &maxfamily,
&minbits, &commonbits);
if (minfamily != maxfamily)
minfamily = 0;
tmp = DatumGetInetKeyP(ent[left[0]].key);
addr = gk_ip_addr(tmp);
left_union = build_inet_union_key(minfamily, minbits, commonbits, addr);
splitvec->spl_ldatum = PointerGetDatum(left_union);
calc_inet_union_params_indexed(ent, right, splitvec->spl_nright,
&minfamily, &maxfamily,
&minbits, &commonbits);
if (minfamily != maxfamily)
minfamily = 0;
tmp = DatumGetInetKeyP(ent[right[0]].key);
addr = gk_ip_addr(tmp);
right_union = build_inet_union_key(minfamily, minbits, commonbits, addr);
splitvec->spl_rdatum = PointerGetDatum(right_union);
PG_RETURN_POINTER(splitvec);
}
/*
* The GiST equality function
*/
Datum
inet_gist_same(PG_FUNCTION_ARGS)
{
GistInetKey *left = DatumGetInetKeyP(PG_GETARG_DATUM(0));
GistInetKey *right = DatumGetInetKeyP(PG_GETARG_DATUM(1));
bool *result = (bool *) PG_GETARG_POINTER(2);
*result = (gk_ip_family(left) == gk_ip_family(right) &&
gk_ip_minbits(left) == gk_ip_minbits(right) &&
gk_ip_commonbits(left) == gk_ip_commonbits(right) &&
memcmp(gk_ip_addr(left), gk_ip_addr(right),
gk_ip_addrsize(left)) == 0);
PG_RETURN_POINTER(result);
}