*** a/src/backend/utils/adt/Makefile --- b/src/backend/utils/adt/Makefile *************** *** 30,36 **** OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \ tsginidx.o tsgistidx.o tsquery.o tsquery_cleanup.o tsquery_gist.o \ tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \ tsvector.o tsvector_op.o tsvector_parser.o \ ! txid.o uuid.o windowfuncs.o xml.o like.o: like.c like_match.c --- 30,36 ---- tsginidx.o tsgistidx.o tsquery.o tsquery_cleanup.o tsquery_gist.o \ tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \ tsvector.o tsvector_op.o tsvector_parser.o \ ! txid.o uuid.o windowfuncs.o xml.o rangetypes_spgistkd.o like.o: like.c like_match.c *** a/src/backend/utils/adt/rangetypes_gist.c --- b/src/backend/utils/adt/rangetypes_gist.c *************** *** 20,39 **** #include "utils/datum.h" #include "utils/rangetypes.h" - - /* Operator strategy numbers used in the GiST range opclass */ - /* Numbers are chosen to match up operator names with existing usages */ - #define RANGESTRAT_BEFORE 1 - #define RANGESTRAT_OVERLEFT 2 - #define RANGESTRAT_OVERLAPS 3 - #define RANGESTRAT_OVERRIGHT 4 - #define RANGESTRAT_AFTER 5 - #define RANGESTRAT_ADJACENT 6 - #define RANGESTRAT_CONTAINS 7 - #define RANGESTRAT_CONTAINED_BY 8 - #define RANGESTRAT_CONTAINS_ELEM 16 - #define RANGESTRAT_EQ 18 - /* * Range class properties used to segregate different classes of ranges in * GiST. Each unique combination of properties is a class. CLS_EMPTY cannot --- 20,25 ---- *************** *** 792,798 **** range_super_union(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2) * part of the relcache entry for the index, typically) this essentially * eliminates lookup overhead during operations on a GiST range index. */ ! static Datum TrickFunctionCall2(PGFunction proc, FmgrInfo *flinfo, Datum arg1, Datum arg2) { FunctionCallInfoData fcinfo; --- 778,784 ---- * part of the relcache entry for the index, typically) this essentially * eliminates lookup overhead during operations on a GiST range index. */ ! Datum TrickFunctionCall2(PGFunction proc, FmgrInfo *flinfo, Datum arg1, Datum arg2) { FunctionCallInfoData fcinfo; *** /dev/null --- b/src/backend/utils/adt/rangetypes_spgistkd.c *************** *** 0 **** --- 1,860 ---- + /*------------------------------------------------------------------------- + * + * rangetypes_spgistkd.c + * implementation of k-d tree over ranges mapped to 2d-points for SP-GiST + * + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/adt/rangetypes_spgistkd.c + * + *------------------------------------------------------------------------- + */ + + #include "postgres.h" + + #include "access/spgist.h" + #include "access/skey.h" + #include "catalog/pg_type.h" + #include "utils/builtins.h" + #include "utils/datum.h" + #include "utils/rangetypes.h" + + static int2 getNodeNumber(TypeCacheEntry *typcache, RangeType *centroid, RangeType *tst, int level); + static int bound_cmp(const void *a, const void *b, void *arg); + static bool bounds_connected(TypeCacheEntry *typcache, RangeBound lower, RangeBound upper); + + /* + * Config SP-GiST interface function. + */ + Datum + spg_range_kd_config(PG_FUNCTION_ARGS) + { + /* spgConfigIn *cfgin = (spgConfigIn *) PG_GETARG_POINTER(0); */ + spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1); + + cfg->prefixType = ANYRANGEOID; + cfg->labelType = VOIDOID; /* we don't need node labels */ + cfg->canReturnData = true; + cfg->longValuesOK = false; + PG_RETURN_VOID(); + } + + /* + * Returns node number for k-d tree by given coordinate of split. + */ + static int2 + getNodeNumber(TypeCacheEntry *typcache, RangeType *coord, + RangeType *tst, int level) + { + RangeBound centroidLower, centroidUpper, lower, upper; + bool centroidEmpty, empty; + + range_deserialize(typcache, tst, &lower, &upper, &empty); + + /* Empty ranges are going to node 3 */ + if (empty) + return 3; + + range_deserialize(typcache, coord, ¢roidLower, ¢roidUpper, + ¢roidEmpty); + + if (level % 2 == 0) + { + /* Even level number, split by lower bound of range */ + if (range_cmp_bounds(typcache, &lower, ¢roidLower) < 0) + return 1; + else + return 2; + } + else + { + /* Odd level number, split by lower bound of range */ + if (range_cmp_bounds(typcache, &upper, ¢roidUpper) < 0) + return 1; + else + return 2; + } + + elog(ERROR, "getQuadrant: impossible case"); + return 0; + } + + + /* + * Choose SP-GiST function: choose path for addition of new range. + */ + Datum + spg_range_kd_choose(PG_FUNCTION_ARGS) + { + spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0); + spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1); + RangeType *inRange = DatumGetRangeType(in->datum), *centroid; + int2 nodeNumber; + TypeCacheEntry *typcache; + + if (in->allTheSame) + { + out->resultType = spgMatchNode; + /* nodeN will be set by core */ + out->result.matchNode.levelAdd = 0; + out->result.matchNode.restDatum = RangeTypeGetDatum(inRange); + PG_RETURN_VOID(); + } + + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(inRange)); + + Assert(in->hasPrefix); + centroid = DatumGetRangeType(in->prefixDatum); + + /* + * Empty prefix datum divides ranges by empty sign. All empty ranges are + * taken into node 0, all non-empty ranges are taken into node 1. + */ + if (RangeIsEmpty(centroid)) + { + out->resultType = spgMatchNode; + if (RangeIsEmpty(inRange)) + out->result.matchNode.nodeN = 0; + else + out->result.matchNode.nodeN = 1; + out->result.matchNode.levelAdd = 0; + out->result.matchNode.restDatum = RangeTypeGetDatum(inRange); + PG_RETURN_VOID(); + } + + nodeNumber = getNodeNumber(typcache, centroid, inRange, in->level); + + /* Node for empty range is possibly not exist, create it if so */ + if (nodeNumber == 3 && in->nNodes == 2) + { + out->resultType = spgAddNode; + out->result.addNode.nodeN = nodeNumber - 1; + } + + Assert(nodeNumber <= in->nNodes); + + /* Select node */ + out->resultType = spgMatchNode; + out->result.matchNode.nodeN = nodeNumber - 1; + out->result.matchNode.levelAdd = 1; + out->result.matchNode.restDatum = RangeTypeGetDatum(inRange); + + PG_RETURN_VOID(); + } + + /* + * Bound comparison for sorting. + */ + static int + bound_cmp(const void *a, const void *b, void *arg) + { + RangeBound *ba = (RangeBound *) a; + RangeBound *bb = (RangeBound *) b; + TypeCacheEntry *typcache = (TypeCacheEntry *)arg; + + return range_cmp_bounds(typcache, ba, bb); + } + + /* + * Picksplit SP-GiST function: split ranges into nodes. Select "median" + * of bound corresponding to level number. + */ + Datum + spg_range_kd_picksplit(PG_FUNCTION_ARGS) + { + spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0); + spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1); + int i, j, nonEmptyCount; + RangeType *coord; + bool empty; + TypeCacheEntry *typcache; + + /* Use the median values of lower or upper bounds for split */ + RangeBound *bounds, otherBound; + + typcache = range_get_typcache(fcinfo, + RangeTypeGetOid(DatumGetRangeType(in->datums[0]))); + + /* Allocate memory for bounds */ + bounds = palloc(sizeof(RangeBound) * in->nTuples); + j = 0; + + /* Deserialize bounds of ranges, count non-empty ranges */ + for (i = 0; i < in->nTuples; i++) + { + if (in->level % 2 == 0) + range_deserialize(typcache, DatumGetRangeType(in->datums[i]), + &bounds[j], &otherBound, &empty); + else + range_deserialize(typcache, DatumGetRangeType(in->datums[i]), + &otherBound, &bounds[j], &empty); + + if (!empty) + j++; + } + nonEmptyCount = j; + + /* + * All the ranges are empty. We've nothing better than put all the ranges + * into node 0. Non-empty range will be routed to node 1. + */ + if (nonEmptyCount == 0) + { + out->nNodes = 2; + out->hasPrefix = true; + /* Prefix is empty */ + out->prefixDatum = RangeTypeGetDatum( + range_serialize(typcache, NULL, NULL, true)); + out->nodeLabels = NULL; + + out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples); + out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples); + + /* Place all ranges into node 0 */ + for (i = 0; i < in->nTuples; i++) + { + RangeType *range = DatumGetRangeType(in->datums[i]); + + out->leafTupleDatums[i] = RangeTypeGetDatum(range); + out->mapTuplesToNodes[i] = 0; + } + PG_RETURN_VOID(); + } + + /* Sort range bounds in order to find median */ + qsort_arg(bounds, nonEmptyCount, sizeof(RangeBound), bound_cmp, typcache); + + + otherBound.inclusive = false; + otherBound.infinite = true; + otherBound.val = PointerGetDatum(NULL); + + /* + * Construct range representing coordinate of split, another bound of this + * range is infinity for space saving. + */ + if (in->level % 2 == 0) + { + otherBound.lower = false; + coord = range_serialize(typcache, &bounds[nonEmptyCount / 2], + &otherBound, false); + } + else + { + otherBound.lower = true; + coord = range_serialize(typcache, &otherBound, + &bounds[nonEmptyCount / 2], false); + } + + + out->hasPrefix = true; + out->prefixDatum = RangeTypeGetDatum(coord); + + /* Create node for empty ranges only if it is actually needed */ + out->nNodes = (nonEmptyCount == in->nTuples) ? 2 : 3; + out->nodeLabels = NULL; /* we don't need node labels */ + + out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples); + out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples); + + /* + * Add ranges to corresponding nodes according to their position relative + * to split coordinate + */ + for (i = 0; i < in->nTuples; i++) + { + RangeType *range = DatumGetRangeType(in->datums[i]); + int2 nodeNumber = getNodeNumber(typcache, coord, range, in->level); + + out->leafTupleDatums[i] = RangeTypeGetDatum(range); + out->mapTuplesToNodes[i] = nodeNumber - 1; + } + + PG_RETURN_VOID(); + } + + /* + * Check if two bounds are "connected", i.e. there are no values which satisfy + * both bounds and there are no values between the bounds. + */ + static bool + bounds_connected(TypeCacheEntry *typcache, RangeBound lower, RangeBound upper) + { + int cmp = range_cmp_bound_values(typcache, &upper, &lower); + if (cmp < 0) + { + RangeType *r; + /* in a continuous subtype, there are assumed to be points between */ + if (!OidIsValid(typcache->rng_canonical_finfo.fn_oid)) + return false; + /* flip the inclusion flags */ + upper.inclusive = !upper.inclusive; + lower.inclusive = !lower.inclusive; + /* change upper/lower labels to avoid Assert failures */ + upper.lower = true; + lower.lower = false; + r = make_range(typcache, &upper, &lower, false); + PG_RETURN_BOOL(RangeIsEmpty(r)); + } + else if (cmp == 0) + { + PG_RETURN_BOOL(upper.inclusive != lower.inclusive); + } + else + { + PG_RETURN_BOOL(false); + } + } + + /* + * Inner consisted SP-GiST function: check which nodes are consistent with + * given set of queries. + */ + Datum + spg_range_kd_inner_consistent(PG_FUNCTION_ARGS) + { + spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); + spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); + RangeBound prevLower, prevUpper, coordLower, coordUpper; + RangeType *coord, *reconstructed = NULL; + bool centroidEmpty; + TypeCacheEntry *typcache; + int which, i; + bool needPrevious = false; + + Assert(in->hasPrefix); + coord = DatumGetRangeType(in->prefixDatum); + + if (in->allTheSame) + { + /* Report that all nodes should be visited */ + out->nNodes = in->nNodes; + out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); + for (i = 0; i < in->nNodes; i++) + out->nodeNumbers[i] = i; + PG_RETURN_VOID(); + } + + if (RangeIsEmpty(coord)) + { + /* + * Empty "coordinate". We can use only information about emptiness of + * ranges in nodes. + */ + Assert(in->nNodes == 2); + + /* + * Nth bit of which variable means that (N - 1)th node should be + * visited. Initially all bits are set. Bits of nodes which should be + * skipped will be unset. + */ + which = (1 << 1) | (1 << 2); + for (i = 0; i < in->nkeys; i++) + { + StrategyNumber strategy; + bool empty; + + strategy = in->scankeys[i].sk_strategy; + + /* + * The only strategy when second argument of operator is not + * range is RANGESTRAT_CONTAINS_ELEM. + */ + if (strategy != RANGESTRAT_CONTAINS_ELEM) + empty = RangeIsEmpty( + DatumGetRangeType(in->scankeys[i].sk_argument)); + + switch (strategy) + { + /* These strategies return false if any argument is empty */ + case RANGESTRAT_BEFORE: + case RANGESTRAT_OVERLEFT: + case RANGESTRAT_OVERLAPS: + case RANGESTRAT_OVERRIGHT: + case RANGESTRAT_AFTER: + case RANGESTRAT_ADJACENT: + if (empty) + which = 0; + else + which &= (1 << 2); + break; + /* + * "Empty" range is contained in any range. Non-empty ranges + * can be contained in only non-empty ranges. + */ + case RANGESTRAT_CONTAINS: + if (!empty) + which &= (1 << 2); + break; + case RANGESTRAT_CONTAINED_BY: + if (empty) + which &= (1 << 1); + break; + /* Empty range can't contain any element */ + case RANGESTRAT_CONTAINS_ELEM: + which &= (1 << 2); + break; + case RANGESTRAT_EQ: + if (empty) + which &= (1 << 1); + else + which &= (1 << 2); + break; + default: + elog(ERROR, "unrecognized range strategy: %d", strategy); + break; + } + if (which == 0) + break; /* no need to consider remaining conditions */ + } + } + else + { + + /* Coordinate is not empty, get information about it. */ + typcache = range_get_typcache(fcinfo, + RangeTypeGetOid(DatumGetRangeType(coord))); + range_deserialize(typcache, coord, &coordLower, &coordUpper, + ¢roidEmpty); + + Assert(in->nNodes == 2 || in->nNodes == 3); + + /* + * Nth bit of which variable means that (N - 1)th node (Nth quadrant) + * should be visited. Initially all bits are set. Bits of nodes which + * should be skipped will be unset. + */ + which = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5); + + for (i = 0; i < in->nkeys; i++) + { + StrategyNumber strategy; + RangeBound lower, upper; + bool empty; + RangeType *range = NULL; + + strategy = in->scankeys[i].sk_strategy; + + /* + * Deserialize range if argument is range. The only strategy when + * second argument of operator is not range is + * RANGESTRAT_CONTAINS_ELEM. + */ + if (strategy != RANGESTRAT_CONTAINS_ELEM) + { + range = DatumGetRangeType(in->scankeys[i].sk_argument); + range_deserialize(typcache, range, &lower, &upper, &empty); + } + + switch (strategy) + { + bool prevEmpty, prevPresent; + RangeType *prevCentroid; + int cmp1, cmp2, cmp3; + + /* + * Range A is before range B if upper bound of A is lower than + * lower bound of B. If upper bound "coordinate" is greater + * or equal to lower bound of argument then no ranges before + * argument can be contained in node 2. + */ + case RANGESTRAT_BEFORE: + if (empty) + which = 0; + else if (range_cmp_bounds(typcache, &coordUpper, + &lower) >= 0 && in->level % 2 == 1) + which &= (1 << 1); + else + which &= (1 << 1) | (1 << 2); + break; + /* + * Range A is overleft to range B if upper bound of A is lower + * or equal to lower bound of B. If upper bound "coordinate" is + * greater to upper bound of argument then no ranges overleft + * argument can be contained in node 2. + */ + case RANGESTRAT_OVERLEFT: + if (empty) + which = 0; + else if (range_cmp_bounds(typcache, &coordUpper, + &upper) > 0 && in->level % 2 == 1) + which = (1 << 1); + else + which &= (1 << 1) | (1 << 2); + break; + /* + * Non-empty ranges overlaps if lower bound of each range is + * lower or equal to upper bound of another ranges. + */ + case RANGESTRAT_OVERLAPS: + if (empty) + which = 0; + else + { + which &= (1 << 1) | (1 << 2); + + if (in->level % 2 == 0) + { + /* + * If lower bound "coordinate" is greater than upper + * bound of argument then no overlapping ranges can + * be in node 2. + */ + if (range_cmp_bounds(typcache, &coordLower, + &upper) > 0) + which &= (1 << 1); + } + else + { + /* + * If upper bound "coordinate" is lower or equal than + * lower bound of argument then no overlapping + * ranges can be in node 1. + */ + if (range_cmp_bounds(typcache, &coordUpper, + &lower) <= 0) + which &= (1 << 2); + } + } + break; + /* + * Range A is overright to range B if lower bound of A is upper + * or equal to upper bound of B. If lower bound "coordinate" is + * lower or equal to lower bound of argument then no ranges + * overright argument can be contained in node 1. + */ + case RANGESTRAT_OVERRIGHT: + if (empty) + which = 0; + else if (range_cmp_bounds(typcache, &coordLower, + &lower) <= 0 && in->level % 2 == 0) + which = (1 << 2); + else + which &= (1 << 1) | (1 << 2); + break; + /* + * Range A is after range B if lower bound of A is greater than + * upper bound of B. If lower bound "coordinate" is lower + * or equal to upper bound of argument then no ranges after + * argument can be contained in node 1. + */ + case RANGESTRAT_AFTER: + if (empty) + which = 0; + else if (range_cmp_bounds(typcache, &coordLower, + &upper) <= 0 && in->level % 2 == 0) + which &= (1 << 2); + else + which &= (1 << 1) | (1 << 2); + break; + /* + * Ranges are adjacent if lower bound of one range is connected + * to upper bound of another range. + */ + case RANGESTRAT_ADJACENT: + /* Deserialize previous "coordinate" if present. */ + prevPresent = (in->reconstructedValue != (Datum) 0); + if (prevPresent) + { + prevCentroid = DatumGetRangeType(in->reconstructedValue); + range_deserialize(typcache, prevCentroid, &prevLower, + &prevUpper, &prevEmpty); + } + + if (in->level %2 == 0) + { + cmp2 = range_cmp_bounds(typcache, &upper, &coordLower); + if (prevPresent) + { + /* + * Do comparison with previous "coordinate" of + * lower bound. + */ + cmp1 = range_cmp_bounds(typcache, &upper, &prevLower); + cmp3 = range_cmp_bounds(typcache, &coordLower, + &prevLower); + + /* + * Check if lower bound of argument is not in + * a quadrant we visit in previous step. + */ + if ((cmp3 < 0 && cmp1 > 0) || (cmp3 > 0 && cmp1 < 0)) + which = 0; + } + + if (cmp2 >= 0) + which &= (1 >> 2); + else if (!bounds_connected(typcache, coordLower, upper)) + which &= (1 >> 1); + } + else + { + cmp2 = range_cmp_bounds(typcache, &lower, &coordUpper); + if (prevPresent) + { + /* + * Do comparison with previous "coordinate" of + * upper bound. + */ + cmp1 = range_cmp_bounds(typcache, &lower, &prevUpper); + cmp3 = range_cmp_bounds(typcache, &coordUpper, &prevUpper); + /* + * Check if upper bound of argument is not in + * a quadrant we visit in previous step. + */ + if ((cmp3 < 0 && cmp1 > 0) || (cmp3 > 0 && cmp1 < 0)) + which = 0; + } + + if (cmp2 > 0) + which &= (1 >> 2); + else if (cmp2 < 0) + which &= (1 >> 1); + } + + needPrevious = true; + break; + /* + * Non-empty range A contains non-empty range B if lower bound + * of A is lower or equal to lower bound of range B and upper + * bound of range A is greater or equal to upper bound of range + * A. + */ + case RANGESTRAT_CONTAINS: + if (empty) + which = 0; + else + { + which &= (1 << 1) | (1 << 2); + if (in->level % 2 == 0) + { + /* + * If lower bound "coordinate" is greater than lower + * bound of argument then no ranges which contain + * argument can be in node 2. + */ + if (range_cmp_bounds(typcache, &coordLower, + &lower) > 0) + which &= (1 << 1); + } + else + { + /* + * If upper bound "coordinate" is lower or equal to + * upper bound of argument then no ranges which + * contain argument can be in node 1. + */ + if (range_cmp_bounds(typcache, &coordUpper, + &upper) <= 0) + which &= (1 << 2); + } + } + break; + case RANGESTRAT_CONTAINED_BY: + if (empty) + which = 0; + else + { + which &= (1 << 1) | (1 << 2); + if (in->level % 2 == 0) + { + /* + * If lower bound "coordinate" is lower or equal to + * lower bound of argument then no ranges which are + * contained in argument can be in node 1. + */ + if (range_cmp_bounds(typcache, &coordLower, + &lower) <= 0) + which &= (1 << 2); + } + else + { + /* + * If upper bound "coordinate" is greater than upper + * bound of argument then no ranges which are + * contained in argument can be in node 2. + */ + if (range_cmp_bounds(typcache, &coordUpper, + &upper) > 0) + which &= (1 << 1); + } + } + break; + case RANGESTRAT_CONTAINS_ELEM: + which &= (1 << 1) | (1 << 2); + + if (in->level % 2 == 0) + { + /* + * Construct bound to pass then to bound comparison + * functions + */ + lower.inclusive = true; + lower.infinite = false; + lower.lower = true; + lower.val = in->scankeys[i].sk_argument; + + /* + * If lower bound "coordinate" is greater than element + * then ranges containing element can't be in node 2. + */ + if (range_cmp_bound_values(typcache, &coordLower, + &lower) > 0) + which &= (1 << 1); + } + else + { + /* + * Construct bound to pass then to bound comparison + * functions + */ + upper.inclusive = true; + upper.infinite = false; + upper.lower = false; + upper.val = in->scankeys[i].sk_argument; + + /* + * If upper bound "coordinate" is lower or equal than + * element then ranges containing element can't be in + * node 1. + */ + if (range_cmp_bound_values(typcache, &coordUpper, + &upper) <= 0) + which &= (1 << 2); + } + + break; + /* + * Equal range can be only in the same node where argument + * would be placed to. + */ + case RANGESTRAT_EQ: + which &= (1 << getNodeNumber(typcache, coord, range, + in->level)); + break; + default: + elog(ERROR, "unrecognized range strategy: %d", strategy); + break; + } + + if (which == 0) + break; /* no need to consider remaining conditions */ + } + } + + /* We must descend into the node(s) identified by which */ + out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); + out->levelAdds = (int *) palloc(sizeof(int) * in->nNodes); + + /* Need to save this coordinate in reconstructed value for next checks? */ + if (needPrevious) + { + out->reconstructedValues = (Datum *) palloc(sizeof(Datum) * in->nNodes); + if (in->level == 0) + { + reconstructed = coord; + } + else + { + /* Replace corresponding bound of current reconstructed value*/ + if (in->level % 2 == 0) + reconstructed = range_serialize(typcache, &coordLower, + &prevUpper, false); + else + reconstructed = range_serialize(typcache, &coordUpper, + &prevLower, false); + } + } + out->nNodes = 0; + for (i = 1; i <= in->nNodes; i++) + { + if (which & (1 << i)) + { + /* Save this "coordinate" if needed */ + if (needPrevious) + out->reconstructedValues[out->nNodes] = + RangeTypeGetDatum(reconstructed); + out->nodeNumbers[out->nNodes++] = i - 1; + out->levelAdds[out->nNodes] = RangeIsEmpty(coord) ? 0 : 1; + } + } + + PG_RETURN_VOID(); + } + + /* + * Leaf consistent SP-GiST function: check leaf value against query using + * corresponding function. + */ + Datum + spg_range_kd_leaf_consistent(PG_FUNCTION_ARGS) + { + spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0); + spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1); + bool res; + int i; + + /* all tests are exact */ + out->recheck = false; + + /* leafDatum is what it is... */ + out->leafValue = in->leafDatum; + + /* Perform the required comparison(s) */ + res = true; + for (i = 0; i < in->nkeys; i++) + { + PGFunction proc; + + /* Find the function which is corresponding to the scan strategy */ + switch (in->scankeys[i].sk_strategy) + { + case RANGESTRAT_BEFORE: + proc = range_before; + break; + case RANGESTRAT_OVERLEFT: + proc = range_overleft; + break; + case RANGESTRAT_OVERLAPS: + proc = range_overlaps; + break; + case RANGESTRAT_OVERRIGHT: + proc = range_overright; + break; + case RANGESTRAT_AFTER: + proc = range_after; + break; + case RANGESTRAT_ADJACENT: + proc = range_adjacent; + break; + case RANGESTRAT_CONTAINS: + proc = range_contains; + break; + case RANGESTRAT_CONTAINED_BY: + proc = range_contained_by; + break; + case RANGESTRAT_CONTAINS_ELEM: + proc = range_contains_elem; + break; + case RANGESTRAT_EQ: + proc = range_eq; + break; + default: + elog(ERROR, "unrecognized range strategy: %d", + in->scankeys[i].sk_strategy); + proc = InvalidOid; + break; + } + res = DatumGetBool(TrickFunctionCall2(proc, fcinfo->flinfo, + in->leafDatum, in->scankeys[i].sk_argument)); + + /* If leaf datum don't match to one query, we can don't check another */ + if (!res) + break; + } + + PG_RETURN_BOOL(res); + } *** a/src/include/catalog/pg_amop.h --- b/src/include/catalog/pg_amop.h *************** *** 767,770 **** DATA(insert ( 4017 25 25 12 s 665 4000 0 )); --- 767,784 ---- DATA(insert ( 4017 25 25 14 s 667 4000 0 )); DATA(insert ( 4017 25 25 15 s 666 4000 0 )); + /* + * SP-GiST range_ops + */ + DATA(insert ( 3475 3831 3831 1 s 3893 4000 0 )); + DATA(insert ( 3475 3831 3831 2 s 3895 4000 0 )); + DATA(insert ( 3475 3831 3831 3 s 3888 4000 0 )); + DATA(insert ( 3475 3831 3831 4 s 3896 4000 0 )); + DATA(insert ( 3475 3831 3831 5 s 3894 4000 0 )); + DATA(insert ( 3475 3831 3831 6 s 3897 4000 0 )); + DATA(insert ( 3475 3831 3831 7 s 3890 4000 0 )); + DATA(insert ( 3475 3831 3831 8 s 3892 4000 0 )); + DATA(insert ( 3475 3831 2283 16 s 3889 4000 0 )); + DATA(insert ( 3475 3831 3831 18 s 3882 4000 0 )); + #endif /* PG_AMOP_H */ *** a/src/include/catalog/pg_amproc.h --- b/src/include/catalog/pg_amproc.h *************** *** 373,377 **** DATA(insert ( 4017 25 25 2 4028 )); --- 373,382 ---- DATA(insert ( 4017 25 25 3 4029 )); DATA(insert ( 4017 25 25 4 4030 )); DATA(insert ( 4017 25 25 5 4031 )); + DATA(insert ( 3475 3831 3831 1 3476 )); + DATA(insert ( 3475 3831 3831 2 3477 )); + DATA(insert ( 3475 3831 3831 3 3478 )); + DATA(insert ( 3475 3831 3831 4 3479 )); + DATA(insert ( 3475 3831 3831 5 3480 )); #endif /* PG_AMPROC_H */ *** a/src/include/catalog/pg_opclass.h --- b/src/include/catalog/pg_opclass.h *************** *** 223,228 **** DATA(insert ( 783 tsquery_ops PGNSP PGUID 3702 3615 t 20 )); --- 223,229 ---- DATA(insert ( 403 range_ops PGNSP PGUID 3901 3831 t 0 )); DATA(insert ( 405 range_ops PGNSP PGUID 3903 3831 t 0 )); DATA(insert ( 783 range_ops PGNSP PGUID 3919 3831 t 0 )); + DATA(insert ( 4000 range_ops PGNSP PGUID 3475 3831 t 0 )); DATA(insert ( 4000 quad_point_ops PGNSP PGUID 4015 600 t 0 )); DATA(insert ( 4000 kd_point_ops PGNSP PGUID 4016 600 f 0 )); DATA(insert ( 4000 text_ops PGNSP PGUID 4017 25 t 0 )); *** a/src/include/catalog/pg_opfamily.h --- b/src/include/catalog/pg_opfamily.h *************** *** 142,147 **** DATA(insert OID = 3702 ( 783 tsquery_ops PGNSP PGUID )); --- 142,148 ---- DATA(insert OID = 3901 ( 403 range_ops PGNSP PGUID )); DATA(insert OID = 3903 ( 405 range_ops PGNSP PGUID )); DATA(insert OID = 3919 ( 783 range_ops PGNSP PGUID )); + DATA(insert OID = 3475 ( 4000 range_ops PGNSP PGUID )); DATA(insert OID = 4015 ( 4000 quad_point_ops PGNSP PGUID )); DATA(insert OID = 4016 ( 4000 kd_point_ops PGNSP PGUID )); DATA(insert OID = 4017 ( 4000 text_ops PGNSP PGUID )); *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 4645,4650 **** DESCR("SP-GiST support for suffix tree over text"); --- 4645,4661 ---- DATA(insert OID = 4031 ( spg_text_leaf_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ spg_text_leaf_consistent _null_ _null_ _null_ )); DESCR("SP-GiST support for suffix tree over text"); + DATA(insert OID = 3476 ( spg_range_kd_config PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ spg_range_kd_config _null_ _null_ _null_ )); + DESCR("SP-GiST support for k-d tree over range"); + DATA(insert OID = 3477 ( spg_range_kd_choose PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ spg_range_kd_choose _null_ _null_ _null_ )); + DESCR("SP-GiST support for k-d tree over range"); + DATA(insert OID = 3478 ( spg_range_kd_picksplit PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ spg_range_kd_picksplit _null_ _null_ _null_ )); + DESCR("SP-GiST support for k-d tree over range"); + DATA(insert OID = 3479 ( spg_range_kd_inner_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ spg_range_kd_inner_consistent _null_ _null_ _null_ )); + DESCR("SP-GiST support for k-d tree over range"); + DATA(insert OID = 3480 ( spg_range_kd_leaf_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ spg_range_kd_leaf_consistent _null_ _null_ _null_ )); + DESCR("SP-GiST support for k-d tree over range"); + /* * Symbolic values for provolatile column: these indicate whether the result *** a/src/include/utils/rangetypes.h --- b/src/include/utils/rangetypes.h *************** *** 75,80 **** typedef struct --- 75,93 ---- #define PG_GETARG_RANGE_COPY(n) DatumGetRangeTypeCopy(PG_GETARG_DATUM(n)) #define PG_RETURN_RANGE(x) return RangeTypeGetDatum(x) + /* Operator strategy numbers used in the GiST range opclass */ + /* Numbers are chosen to match up operator names with existing usages */ + #define RANGESTRAT_BEFORE 1 + #define RANGESTRAT_OVERLEFT 2 + #define RANGESTRAT_OVERLAPS 3 + #define RANGESTRAT_OVERRIGHT 4 + #define RANGESTRAT_AFTER 5 + #define RANGESTRAT_ADJACENT 6 + #define RANGESTRAT_CONTAINS 7 + #define RANGESTRAT_CONTAINED_BY 8 + #define RANGESTRAT_CONTAINS_ELEM 16 + #define RANGESTRAT_EQ 18 + /* * prototypes for functions defined in rangetypes.c */ *************** *** 166,171 **** extern int range_cmp_bound_values(TypeCacheEntry *typcache, RangeBound *b1, --- 179,186 ---- extern RangeType *make_empty_range(TypeCacheEntry *typcache); /* GiST support (in rangetypes_gist.c) */ + extern Datum TrickFunctionCall2(PGFunction proc, FmgrInfo *flinfo, Datum arg1, + Datum arg2); extern Datum range_gist_consistent(PG_FUNCTION_ARGS); extern Datum range_gist_compress(PG_FUNCTION_ARGS); extern Datum range_gist_decompress(PG_FUNCTION_ARGS); *************** *** 174,177 **** extern Datum range_gist_penalty(PG_FUNCTION_ARGS); --- 189,199 ---- extern Datum range_gist_picksplit(PG_FUNCTION_ARGS); extern Datum range_gist_same(PG_FUNCTION_ARGS); + /* SP-GiST k-d tree support (in rangetypes_spgistkd.c */ + Datum spg_range_kd_config(PG_FUNCTION_ARGS); + Datum spg_range_kd_choose(PG_FUNCTION_ARGS); + Datum spg_range_kd_picksplit(PG_FUNCTION_ARGS); + Datum spg_range_kd_inner_consistent(PG_FUNCTION_ARGS); + Datum spg_range_kd_leaf_consistent(PG_FUNCTION_ARGS); + #endif /* RANGETYPES_H */