/* * Inet histogram partial match divider calculation * * First the families and the lengths of the network parts are compared using * the subnet inclusion operator. If those are acceptable for the operator, * the divider will be calculated using the masklens and the common bits of * the addresses. -1 will be returned if it cannot be calculated. * * See commentary for inet_hist_value_sel() for some rationale for this. */ static int inet_hist_match_divider(inet *boundary, inet *query, int opr_codenum) { if (ip_family(boundary) == ip_family(query) && inet_masklen_inclusion_cmp(boundary, query, opr_codenum) == 0) { int min_bits, decisive_bits; min_bits = Min(ip_bits(boundary), ip_bits(query)); /* * Set decisive_bits to the masklen of the one that should contain the * other according to the operator. */ if (opr_codenum < 0) decisive_bits = ip_bits(boundary); else if (opr_codenum > 0) decisive_bits = ip_bits(query); else decisive_bits = min_bits; /* * Now return the number of non-common decisive bits. (This will be * zero if the boundary and query in fact match, else positive.) */ if (min_bits > 0) return decisive_bits - bitncommon(ip_addr(boundary), ip_addr(query), min_bits); return decisive_bits; } return -1; }
/* * The SP-GiST choose function */ Datum inet_spg_choose(PG_FUNCTION_ARGS) { spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0); spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1); inet *val = DatumGetInetPP(in->datum), *prefix; int commonbits; /* * If we're looking at a tuple that splits by address family, choose the * appropriate subnode. */ if (!in->hasPrefix) { /* allTheSame isn't possible for such a tuple */ Assert(!in->allTheSame); Assert(in->nNodes == 2); out->resultType = spgMatchNode; out->result.matchNode.nodeN = (ip_family(val) == PGSQL_AF_INET) ? 0 : 1; out->result.matchNode.restDatum = InetPGetDatum(val); PG_RETURN_VOID(); } /* Else it must split by prefix */ Assert(in->nNodes == 4 || in->allTheSame); prefix = DatumGetInetPP(in->prefixDatum); commonbits = ip_bits(prefix); /* * We cannot put addresses from different families under the same inner * node, so we have to split if the new value's family is different. */ if (ip_family(val) != ip_family(prefix)) { /* Set up 2-node tuple */ out->resultType = spgSplitTuple; out->result.splitTuple.prefixHasPrefix = false; out->result.splitTuple.prefixNNodes = 2; out->result.splitTuple.prefixNodeLabels = NULL; /* Identify which node the existing data goes into */ out->result.splitTuple.childNodeN = (ip_family(prefix) == PGSQL_AF_INET) ? 0 : 1; out->result.splitTuple.postfixHasPrefix = true; out->result.splitTuple.postfixPrefixDatum = InetPGetDatum(prefix); PG_RETURN_VOID(); } /* * If the new value does not match the existing prefix, we have to split. */ if (ip_bits(val) < commonbits || bitncmp(ip_addr(prefix), ip_addr(val), commonbits) != 0) { /* Determine new prefix length for the split tuple */ commonbits = bitncommon(ip_addr(prefix), ip_addr(val), Min(ip_bits(val), commonbits)); /* Set up 4-node tuple */ out->resultType = spgSplitTuple; out->result.splitTuple.prefixHasPrefix = true; out->result.splitTuple.prefixPrefixDatum = InetPGetDatum(cidr_set_masklen_internal(val, commonbits)); out->result.splitTuple.prefixNNodes = 4; out->result.splitTuple.prefixNodeLabels = NULL; /* Identify which node the existing data goes into */ out->result.splitTuple.childNodeN = inet_spg_node_number(prefix, commonbits); out->result.splitTuple.postfixHasPrefix = true; out->result.splitTuple.postfixPrefixDatum = InetPGetDatum(prefix); PG_RETURN_VOID(); } /* * All OK, choose the node to descend into. (If this tuple is marked * allTheSame, the core code will ignore our choice of nodeN; but we need * not account for that case explicitly here.) */ out->resultType = spgMatchNode; out->result.matchNode.nodeN = inet_spg_node_number(val, commonbits); out->result.matchNode.restDatum = InetPGetDatum(val); PG_RETURN_VOID(); }
/* * The GiST PickSplit method */ Datum inet_spg_picksplit(PG_FUNCTION_ARGS) { spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0); spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1); inet *prefix, *tmp; int i, commonbits; bool differentFamilies = false; /* Initialize the prefix with the first item */ prefix = DatumGetInetPP(in->datums[0]); commonbits = ip_bits(prefix); /* Examine remaining items to discover minimum common prefix length */ for (i = 1; i < in->nTuples; i++) { tmp = DatumGetInetPP(in->datums[i]); if (ip_family(tmp) != ip_family(prefix)) { differentFamilies = true; break; } if (ip_bits(tmp) < commonbits) commonbits = ip_bits(tmp); commonbits = bitncommon(ip_addr(prefix), ip_addr(tmp), commonbits); if (commonbits == 0) break; } /* Don't need labels; allocate output arrays */ out->nodeLabels = NULL; out->mapTuplesToNodes = (int *) palloc(sizeof(int) * in->nTuples); out->leafTupleDatums = (Datum *) palloc(sizeof(Datum) * in->nTuples); if (differentFamilies) { /* Set up 2-node tuple */ out->hasPrefix = false; out->nNodes = 2; for (i = 0; i < in->nTuples; i++) { tmp = DatumGetInetPP(in->datums[i]); out->mapTuplesToNodes[i] = (ip_family(tmp) == PGSQL_AF_INET) ? 0 : 1; out->leafTupleDatums[i] = InetPGetDatum(tmp); } } else { /* Set up 4-node tuple */ out->hasPrefix = true; out->prefixDatum = InetPGetDatum(cidr_set_masklen_internal(prefix, commonbits)); out->nNodes = 4; for (i = 0; i < in->nTuples; i++) { tmp = DatumGetInetPP(in->datums[i]); out->mapTuplesToNodes[i] = inet_spg_node_number(tmp, commonbits); out->leafTupleDatums[i] = InetPGetDatum(tmp); } } PG_RETURN_VOID(); }