diff --git a/doc/src/sgml/spgist.sgml b/doc/src/sgml/spgist.sgml index d558b8e..8628205 100644 --- a/doc/src/sgml/spgist.sgml +++ b/doc/src/sgml/spgist.sgml @@ -113,6 +113,19 @@ + box_ops + box + + && + << + >> + <<| + |>> + <@ + @> + + + text_ops text diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c index bec6daf..5ac5bef 100644 --- a/src/backend/access/spgist/spgscan.c +++ b/src/backend/access/spgist/spgscan.c @@ -23,7 +23,6 @@ #include "utils/memutils.h" #include "utils/rel.h" - typedef void (*storeRes_func) (SpGistScanOpaque so, ItemPointer heapPtr, Datum leafValue, bool isnull, bool recheck); diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 3ed0b44..2236737 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -18,7 +18,7 @@ endif # keep this list arranged alphabetically or it gets to be a mess OBJS = acl.o arrayfuncs.o array_expanded.o array_selfuncs.o \ array_typanalyze.o array_userfuncs.o arrayutils.o ascii.o \ - bool.o cash.o char.o date.o datetime.o datum.o dbsize.o domains.o \ + bool.o boxtype_spgist.o cash.o char.o date.o datetime.o datum.o dbsize.o domains.o \ encode.o enum.o expandeddatum.o \ float.o format_type.o formatting.o genfile.o \ geo_ops.o geo_selfuncs.o inet_cidr_ntop.o inet_net_pton.o int.o \ diff --git a/src/backend/utils/adt/boxtype_spgist.c b/src/backend/utils/adt/boxtype_spgist.c new file mode 100644 index 0000000..a633ce9 --- /dev/null +++ b/src/backend/utils/adt/boxtype_spgist.c @@ -0,0 +1,733 @@ +/*------------------------------------------------------------------------- + * + * boxtype_spgist.c + * implementation of quad-4d tree over boxes for SP-GiST. + * + * Quad-4d is a 4-dimensional analog of quadtree. Quad-4d tree splits + * 4-dimensional space into 16 quadrants. Each inner node of a quad-4d tree contains + * a box. We call these boxes centroids. Main purpose of the boxtype index is + * to tell, for a given box, which other boxes intersect it, + * contain or are contained by it, etc. + * + * For example consider the case of intersection. + * When recursion descends deeper and deeper down the tree, all quadrants in the + * current node will eventually be passed to the intersect4D function call. This function + * answers the question: can any box from this quadrant intersect with given box (query box)? + * If yes, then this quadrant will be walked. If no, then this quadrant will be rejected. + * + * A quadrant has bounds, but sp-gist keeps only 4-d point (box) in inner nodes. + * We use traversalValue to calculate quadrant bounds from parent's quadrant + * bounds. + * + * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/adt/boxtype_spgist.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/spgist.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "utils/builtins.h"; +#include "utils/datum.h" +#include "utils/geo_decls.h" + +#define NegInf -1 +#define PosInf 1 +#define NotInf 0 + +#define LT -1 +#define GT 1 +#define EQ 0 + + + +/* InfR type implements doubles and +- infinity */ +typedef struct +{ + int infFlag; + double val; +} InfR; + +static InfR negInf = {.infFlag = NegInf,.val = 0}; +static InfR posInf = {.infFlag = PosInf,.val = 0}; + +/* wrap double to InfR */ +static InfR +toInfR(double v, InfR * r) +{ + r->infFlag = NotInf; + r->val = v; +} + +/* compare InfR with double */ +static int +cmp_InfR_r(const InfR * infVal, const double val) +{ + if (infVal->infFlag == PosInf) + return GT; + + else if (infVal->infFlag == NegInf) + return LT; + + else + { + double val0 = infVal->val; + + if (val0 < val) + return LT; + if (val0 > val) + return GT; + } + + return EQ; +} + +static int +compareDoubles(const void *a, const void *b) +{ + double x = *(double *) a; + double y = *(double *) b; + + if (x < y) + return LT; + if (x > y) + return GT; + return EQ; +} + +/*------------------------------------------------------------------------- + * We have two families of types: + * IRange, IRangeBox and IRectBox are parameterized with InfR, + * while Range and Rectangle are parameterized with double + *------------------------------------------------------------------------- + */ +typedef struct +{ + InfR low; + InfR high; +} IRange; + +typedef struct +{ + IRange left; + IRange right; +} IRangeBox; + +typedef struct +{ + IRangeBox range_box_x; + IRangeBox range_box_y; +} IRectBox; + +typedef struct +{ + double low; + double high; +} Range; + +typedef struct +{ + Range range_x; + Range range_y; +} Rectangle; + + +/* Fill Rectangle using BOX */ +inline static void +boxPointerToRectangle(BOX *box, Rectangle * rectangle) +{ + rectangle->range_x.low = box->low.x; + rectangle->range_x.high = box->high.x; + + rectangle->range_y.low = box->low.y; + rectangle->range_y.high = box->high.y; +} + +/*----------------------------------------------------------------- + * quadrant is 8bits unsigned integer with bits: + * [0,0,0,0,a,b,c,d] where + * a is 1 if inBox->low.x > centroid->low.x + * b is 1 if inBox->high.x > centroid->high.x + * c is 1 if inBox->low.y > centroid->low.y + * d is 1 if inBox->high.y > centroid->high.y + *----------------------------------------------------------------- + */ +static uint8 +getQuadrant(const BOX *centroid, const BOX *inBox) +{ + uint8 quadrant = 0; + + if (inBox->low.x > centroid->low.x) + quadrant |= 0x8; + + if (inBox->high.x > centroid->high.x) + quadrant |= 0x4; + + if (inBox->low.y > centroid->low.y) + quadrant |= 0x2; + + if (inBox->high.y > centroid->high.y) + quadrant |= 0x1; + + return quadrant; +} + + +/* + * All centroids in q4d tree are bounded by IRectBox, but SP-Gist only keeps boxes. + * When we walk into depth, we must calculate IRectBox, + * using centroid and quadrant. The following function calculates IRangeBox. + */ +static void +evalIRangeBox(const IRangeBox * range_box, const Range * range, const int half1, const int half2, IRangeBox * new_range_box) +{ + if (half1 == 0) + { + toInfR(range->low, &(new_range_box->left.high)); + new_range_box->left.low = range_box->left.low; + } + else + { + toInfR(range->low, &(new_range_box->left.low)); + new_range_box->left.high = range_box->left.high; + } + + if (half2 == 0) + { + toInfR(range->high, &(new_range_box->right.high)); + new_range_box->right.low = range_box->right.low; + } + else + { + toInfR(range->high, &(new_range_box->right.low)); + new_range_box->right.high = range_box->right.high; + } +} + + + +/* + * All centroids in q4d tree are bounded by IRectBox, but SP-Gist only keeps boxes. + * When we walk into depth, we must calculate IRectBox, + * using centroid and quadrant. + */ +static void +evalIRectBox(const IRectBox * rect_box, const Rectangle * centroid, const uint8 quadrant, IRectBox * new_rect_box) +{ + const int half1 = quadrant & 0x8; + const int half2 = quadrant & 0x4; + const int half3 = quadrant & 0x2; + const int half4 = quadrant & 0x1; + + evalIRangeBox(&(rect_box->range_box_x), &(centroid->range_x), half1, half2, &(new_rect_box->range_box_x)); + evalIRangeBox(&(rect_box->range_box_y), &(centroid->range_y), half3, half4, &(new_rect_box->range_box_y)); +} + + +/* initialize IRangeBox covering all space */ +inline static void +initializeUnboundedBox(IRectBox * rect_box) +{ + rect_box->range_box_x.left.low = negInf; + rect_box->range_box_x.left.high = posInf; + + rect_box->range_box_x.right.low = negInf; + rect_box->range_box_x.right.high = posInf; + + rect_box->range_box_y.left.low = negInf; + rect_box->range_box_y.left.high = posInf; + + rect_box->range_box_y.right.low = negInf; + rect_box->range_box_y.right.high = posInf; +} + + +/* answer the question: Can this range and any range from range_box intersect? */ +static int +intersect2D(const Range * range, const IRangeBox * range_box) +{ + const InfR *x0 = &(range_box->left.low); + const InfR *y1 = &(range_box->right.high); + + const double a = range->low; + const double b = range->high; + + const int p1 = cmp_InfR_r(y1, a); + const int p2 = cmp_InfR_r(x0, b); + + return ((p1 != LT) && (p2 != GT)); +} + +/* answer the question: Can this rectangle and any rectangle from rect_box intersect? */ +static int +intersect4D(const Rectangle * rectangle, const IRectBox * rect_box) +{ + const int px = intersect2D(&(rectangle->range_x), &(rect_box->range_box_x)); + const int py = intersect2D(&(rectangle->range_y), &(rect_box->range_box_y)); + + return (px && py); +} + + +/* answer the question: Can any range from range_box contain this range? */ +static int +contain2D(const Range * range, const IRangeBox * range_box) +{ + const InfR *x0 = &(range_box->left.low); + const InfR *y1 = &(range_box->right.high); + + const double a = range->low; + const double b = range->high; + + const int p1 = cmp_InfR_r(y1, b); + const int p2 = cmp_InfR_r(x0, a); + + return ((p1 != LT) && (p2 != GT)); +} + + +/* answer the question: Can any rectangle from rect_box contain this rectangle? */ +static int +contain4D(const Rectangle * rectangle, const IRectBox * rect_box) +{ + const int px = contain2D(&(rectangle->range_x), &(rect_box->range_box_x)); + const int py = contain2D(&(rectangle->range_y), &(rect_box->range_box_y)); + + return (px && py); +} + + +/* answer the question: Can this range contain any range from range_box? */ +static int +contained2D(const Range * range, const IRangeBox * range_box) +{ + const InfR *x0 = &(range_box->left.low); + const InfR *x1 = &(range_box->left.high); + + const InfR *y0 = &(range_box->right.low); + const InfR *y1 = &(range_box->right.high); + + const double a = range->low; + const double b = range->high; + + const int p1 = cmp_InfR_r(x0, b); + const int p2 = cmp_InfR_r(x1, a); + const int p3 = cmp_InfR_r(y0, b); + const int p4 = cmp_InfR_r(y1, a); + + return ((p1 != GT) && (p2 != LT) && (p3 != GT) && (p4 != LT)); +} + +/* answer the question: Can this rectangle contain any rectangle from rect_box? */ +static int +contained4D(const Rectangle * rectangle, const IRectBox * rect_box) +{ + const int px = contained2D(&(rectangle->range_x), &(rect_box->range_box_x)); + const int py = contained2D(&(rectangle->range_y), &(rect_box->range_box_y)); + + return (px && py); +} + + +/* answer the question: Can any range from range_box to be lower than this range? */ +static int +isLower(const Range * range, const IRangeBox * range_box) +{ + const InfR *x0 = &(range_box->left.low); + const InfR *y0 = &(range_box->right.low); + + const double a = range->low; + + const int p1 = cmp_InfR_r(x0, a); + const int p2 = cmp_InfR_r(y0, a); + + return (p1 == LT && p2 == LT); +} + +/* answer the question: Can any range from range_box to be higher than this range? */ +static int +isHigher(const Range * range, const IRangeBox * range_box) +{ + const InfR *x1 = &(range_box->left.high); + const InfR *y1 = &(range_box->right.high); + + const double b = range->high; + + const int p1 = cmp_InfR_r(x1, b); + const int p2 = cmp_InfR_r(y1, b); + + return (p1 == GT && p2 == GT); +} + +static int +left4D(const Rectangle * rectangle, const IRectBox * rect_box) +{ + return isLower(&(rectangle->range_x), &(rect_box->range_box_x)); +} + +static int +right4D(const Rectangle * rectangle, const IRectBox * rect_box) +{ + return isHigher(&(rectangle->range_x), &(rect_box->range_box_x)); +} + +static int +below4D(const Rectangle * rectangle, const IRectBox * rect_box) +{ + return isLower(&(rectangle->range_y), &(rect_box->range_box_y)); +} + +static int +above4D(const Rectangle * rectangle, const IRectBox * rect_box) +{ + return isHigher(&(rectangle->range_y), &(rect_box->range_box_y)); +} + + +/* SP-GiST API functions */ +Datum spg_box_quad_config(PG_FUNCTION_ARGS); +Datum spg_box_quad_choose(PG_FUNCTION_ARGS); +Datum spg_box_quad_picksplit(PG_FUNCTION_ARGS); +Datum spg_box_quad_inner_consistent(PG_FUNCTION_ARGS); +Datum spg_box_quad_leaf_consistent(PG_FUNCTION_ARGS); + +/* + * SP-GiST 'config' interface function. + */ +Datum +spg_box_quad_config(PG_FUNCTION_ARGS) +{ + spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1); + + cfg->prefixType = BOXOID; + cfg->labelType = VOIDOID; /* we don't need node labels */ + cfg->canReturnData = true; + cfg->longValuesOK = false; + PG_RETURN_VOID(); +} + + +Datum +spg_box_quad_choose(PG_FUNCTION_ARGS) +{ + const spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0); + spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1); + + const BOX *inBox = DatumGetBoxP(in->datum); + const BOX *centroid = DatumGetBoxP(in->prefixDatum); + + uint8 quadrant; + + if (in->allTheSame) + { + out->resultType = spgMatchNode; + /* nodeN will be set by core */ + out->result.matchNode.levelAdd = 0; + out->result.matchNode.restDatum = BoxPGetDatum(inBox); + PG_RETURN_VOID(); + } + + quadrant = getQuadrant(centroid, inBox); + + out->resultType = spgMatchNode; + out->result.matchNode.nodeN = quadrant; + out->result.matchNode.levelAdd = 1; + out->result.matchNode.restDatum = BoxPGetDatum(inBox); + PG_RETURN_VOID(); +} + + +Datum +spg_box_quad_picksplit(PG_FUNCTION_ARGS) +{ + const spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0); + spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1); + + BOX *centroid; + int median, + i; + + + /* + * Begin. This block evaluates the median of coordinates of boxes + */ + + double *lowXs = palloc(sizeof(double) * in->nTuples); + double *highXs = palloc(sizeof(double) * in->nTuples); + double *lowYs = palloc(sizeof(double) * in->nTuples); + double *highYs = palloc(sizeof(double) * in->nTuples); + + for (i = 0; i < in->nTuples; i++) + { + const BOX *box = DatumGetBoxP(in->datums[i]); + + lowXs[i] = box->low.x; + highXs[i] = box->high.x; + lowYs[i] = box->low.y; + highYs[i] = box->high.y; + } + + qsort(lowXs, in->nTuples, sizeof(double), compareDoubles); + qsort(highXs, in->nTuples, sizeof(double), compareDoubles); + qsort(lowYs, in->nTuples, sizeof(double), compareDoubles); + qsort(highYs, in->nTuples, sizeof(double), compareDoubles); + + median = in->nTuples / 2; + + centroid = palloc(sizeof(BOX)); + + centroid->low.x = lowXs[median]; + centroid->high.x = highXs[median]; + centroid->low.y = lowYs[median]; + centroid->high.y = highYs[median]; + + /* + * This block evaluates the median of coordinates of boxes. End. + */ + + out->hasPrefix = true; + out->prefixDatum = BoxPGetDatum(centroid); + + out->nNodes = 16; + out->nodeLabels = NULL; /* we don't need node labels */ + + out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples); + out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples); + + /* + * Assign ranges to corresponding nodes according to quadrants relative to + * "centroid" range. + */ + + for (i = 0; i < in->nTuples; i++) + { + const BOX *box = DatumGetBoxP(in->datums[i]); + const uint8 quadrant = getQuadrant(centroid, box); + + out->leafTupleDatums[i] = BoxPGetDatum(box); + out->mapTuplesToNodes[i] = quadrant; + } + + PG_RETURN_VOID(); +} + +Datum +spg_box_quad_inner_consistent(PG_FUNCTION_ARGS) +{ + spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); + spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); + int i; + + MemoryContext oldCtx; + IRectBox *rect_box; + + uint8 quadrant; + + Rectangle *rectangle_centroid = (Rectangle *) palloc(sizeof(Rectangle)); + Rectangle *p_query_rect = (Rectangle *) palloc(sizeof(Rectangle)); + + boxPointerToRectangle(DatumGetBoxP(in->prefixDatum), rectangle_centroid); + + if (in->traversalValue) + { + /* Here we get 4 dimension bound box (IRectBox) from traversalValue */ + rect_box = in->traversalValue; + } + else + { + /* + * Here we initialize rect_box, because we have just begun to walk + * through the tree + */ + + rect_box = (IRectBox *) palloc(sizeof(IRectBox)); + initializeUnboundedBox(rect_box); + } + + out->traversalValues = (void **) palloc(sizeof(void *) * in->nNodes); + + if (in->allTheSame) + { + /* Report that all nodes should be visited */ + int nnode; + + out->nNodes = in->nNodes; + out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); + + /* + * We switch memory context, because we want allocate memory for new + * traversal values for IRectBox and transmit these pieces of memory + * to further calls of spg_box_quad_inner_consistent. + */ + oldCtx = MemoryContextSwitchTo(in->traversalMemoryContext); + + for (nnode = 0; nnode < in->nNodes; nnode++) + { + IRectBox *new_rect_box; + + new_rect_box = (IRectBox *) palloc(sizeof(IRectBox)); + memcpy(new_rect_box, rect_box, sizeof(IRectBox)); + + out->traversalValues[nnode] = new_rect_box; + out->nodeNumbers[nnode] = nnode; + } + /* Switch back */ + MemoryContextSwitchTo(oldCtx); + PG_RETURN_VOID(); + } + + out->nNodes = 0; + out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); + + /* + * We switch memory context, because we want to allocate memory for new + * traversal values (new_rect_box) and pass these pieces of memory to + * further call of spg_box_quad_inner_consistent. + */ + oldCtx = MemoryContextSwitchTo(in->traversalMemoryContext); + + for (quadrant = 0; quadrant < in->nNodes; quadrant++) + { + IRectBox *new_rect_box; + int flag; + + new_rect_box = (IRectBox *) palloc(sizeof(IRectBox)); + + /* Calculates 4-dim IRectBox */ + evalIRectBox(rect_box, rectangle_centroid, quadrant, new_rect_box); + + for (i = 0, flag = 1; i < in->nkeys && flag; i++) + { + const StrategyNumber strategy = in->scankeys[i].sk_strategy; + + switch (strategy) + { + case RTOverlapStrategyNumber: + boxPointerToRectangle(DatumGetBoxP(in->scankeys[i].sk_argument), p_query_rect); + flag = flag && intersect4D(p_query_rect, new_rect_box); + break; + + case RTContainsStrategyNumber: + boxPointerToRectangle(DatumGetBoxP(in->scankeys[i].sk_argument), p_query_rect); + flag = flag && contain4D(p_query_rect, new_rect_box); + break; + + case RTContainedByStrategyNumber: + boxPointerToRectangle(DatumGetBoxP(in->scankeys[i].sk_argument), p_query_rect); + flag = flag && contained4D(p_query_rect, new_rect_box); + break; + + case RTLeftStrategyNumber: + boxPointerToRectangle(DatumGetBoxP(in->scankeys[i].sk_argument), p_query_rect); + flag = flag && left4D(p_query_rect, new_rect_box); + break; + + case RTRightStrategyNumber: + boxPointerToRectangle(DatumGetBoxP(in->scankeys[i].sk_argument), p_query_rect); + flag = flag && right4D(p_query_rect, new_rect_box); + break; + + case RTAboveStrategyNumber: + boxPointerToRectangle(DatumGetBoxP(in->scankeys[i].sk_argument), p_query_rect); + flag = flag && above4D(p_query_rect, new_rect_box); + break; + + case RTBelowStrategyNumber: + boxPointerToRectangle(DatumGetBoxP(in->scankeys[i].sk_argument), p_query_rect); + flag = flag && below4D(p_query_rect, new_rect_box); + break; + + default: + elog(ERROR, "This operation doesn't support by SP-Gist"); + } + } + if (flag) + { + out->traversalValues[out->nNodes] = new_rect_box; + out->nodeNumbers[out->nNodes] = quadrant; + out->nNodes++; + } + } + + MemoryContextSwitchTo(oldCtx); + PG_RETURN_VOID(); +} + +Datum +spg_box_quad_leaf_consistent(PG_FUNCTION_ARGS) +{ + spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0); + spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1); + BOX *leafBox = DatumGetBoxP(in->leafDatum); + int flag, + i; + + /* all tests are exact */ + out->recheck = false; + + /* leafDatum is what it is... */ + out->leafValue = in->leafDatum; + + /* Perform the required comparison(s) */ + for (i = 0, flag = 1; i < in->nkeys && flag; i++) + { + const StrategyNumber strategy = in->scankeys[i].sk_strategy; + const Datum keyDatum = in->scankeys[i].sk_argument; + + switch (strategy) + { + case RTOverlapStrategyNumber: + flag = flag && DatumGetBool(DirectFunctionCall2(box_overlap, + PointerGetDatum(leafBox), + keyDatum)); + break; + + case RTContainsStrategyNumber: + flag = flag && DatumGetBool(DirectFunctionCall2(box_contain, + PointerGetDatum(leafBox), + keyDatum)); + break; + + case RTContainedByStrategyNumber: + flag = flag && DatumGetBool(DirectFunctionCall2(box_contained, + PointerGetDatum(leafBox), + keyDatum)); + break; + + case RTLeftStrategyNumber: + flag = flag && DatumGetBool(DirectFunctionCall2(box_left, + PointerGetDatum(leafBox), + keyDatum)); + break; + + case RTRightStrategyNumber: + flag = flag && DatumGetBool(DirectFunctionCall2(box_right, + PointerGetDatum(leafBox), + keyDatum)); + break; + + case RTAboveStrategyNumber: + flag = flag && DatumGetBool(DirectFunctionCall2(box_above, + PointerGetDatum(leafBox), + keyDatum)); + break; + + case RTBelowStrategyNumber: + flag = flag && DatumGetBool(DirectFunctionCall2(box_below, + PointerGetDatum(leafBox), + keyDatum)); + break; + + default: + elog(ERROR, "This type operation doesn't support by sp-gist"); + } + } + + PG_RETURN_BOOL(flag); +} diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index da5fe9d..c5083f9 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -833,6 +833,17 @@ DATA(insert ( 3474 3831 2283 16 s 3889 4000 0 )); DATA(insert ( 3474 3831 3831 18 s 3882 4000 0 )); /* + * SP-GiST box_ops + */ +DATA(insert ( 5000 603 603 1 s 493 4000 0 )); +DATA(insert ( 5000 603 603 3 s 500 4000 0 )); +DATA(insert ( 5000 603 603 5 s 496 4000 0 )); +DATA(insert ( 5000 603 603 7 s 498 4000 0 )); +DATA(insert ( 5000 603 603 8 s 497 4000 0 )); +DATA(insert ( 5000 603 603 10 s 2570 4000 0 )); +DATA(insert ( 5000 603 603 11 s 2573 4000 0 )); + +/* * GiST inet_ops */ DATA(insert ( 3550 869 869 3 s 3552 783 0 )); diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index b57d6e6..86e3351 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -438,6 +438,11 @@ DATA(insert ( 4017 25 25 2 4028 )); DATA(insert ( 4017 25 25 3 4029 )); DATA(insert ( 4017 25 25 4 4030 )); DATA(insert ( 4017 25 25 5 4031 )); +DATA(insert ( 5000 603 603 1 5012 )); +DATA(insert ( 5000 603 603 2 5013 )); +DATA(insert ( 5000 603 603 3 5014 )); +DATA(insert ( 5000 603 603 4 5015 )); +DATA(insert ( 5000 603 603 5 5016 )); /* BRIN opclasses */ /* minmax bytea */ diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index e7b3148..5a97c65 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -228,6 +228,7 @@ 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 3474 3831 t 0 )); +DATA(insert ( 4000 box_ops PGNSP PGUID 5000 603 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 )); diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h index acbc100..08b0c91 100644 --- a/src/include/catalog/pg_opfamily.h +++ b/src/include/catalog/pg_opfamily.h @@ -182,5 +182,6 @@ DATA(insert OID = 4081 ( 3580 uuid_minmax_ops PGNSP PGUID )); DATA(insert OID = 4103 ( 3580 range_inclusion_ops PGNSP PGUID )); DATA(insert OID = 4082 ( 3580 pg_lsn_minmax_ops PGNSP PGUID )); DATA(insert OID = 4104 ( 3580 box_inclusion_ops PGNSP PGUID )); +DATA(insert OID = 5000 ( 4000 box_ops PGNSP PGUID )); #endif /* PG_OPFAMILY_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index f688454..0a3a6ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -5194,6 +5194,17 @@ DESCR("SP-GiST support for quad tree over range"); DATA(insert OID = 3473 ( spg_range_quad_leaf_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ spg_range_quad_leaf_consistent _null_ _null_ _null_ )); DESCR("SP-GiST support for quad tree over range"); +DATA(insert OID = 5012 ( spg_box_quad_config PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ _null_ spg_box_quad_config _null_ _null_ _null_ )); +DESCR("SP-GiST support for quad tree over box"); +DATA(insert OID = 5013 ( spg_box_quad_choose PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ _null_ spg_box_quad_choose _null_ _null_ _null_ )); +DESCR("SP-GiST support for quad tree over box"); +DATA(insert OID = 5014 ( spg_box_quad_picksplit PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ _null_ spg_box_quad_picksplit _null_ _null_ _null_ )); +DESCR("SP-GiST support for quad tree over box"); +DATA(insert OID = 5015 ( spg_box_quad_inner_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ _null_ spg_box_quad_inner_consistent _null_ _null_ _null_ )); +DESCR("SP-GiST support for quad tree over box"); +DATA(insert OID = 5016 ( spg_box_quad_leaf_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ spg_box_quad_leaf_consistent _null_ _null_ _null_ )); +DESCR("SP-GiST support for quad tree over box"); + /* replication slots */ DATA(insert OID = 3779 ( pg_create_physical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v u 2 0 2249 "19 16" "{19,16,19,3220}" "{i,i,o,o}" "{slot_name,immediately_reserve,slot_name,xlog_position}" _null_ _null_ pg_create_physical_replication_slot _null_ _null_ _null_ )); DESCR("create a physical replication slot"); diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index b72e65d..b10a8c9 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -70,6 +70,9 @@ CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1); -- -- SP-GiST -- +CREATE TEMP TABLE q4d_boxes_tbl AS + SELECT home_base AS home_base FROM fast_emp4000; +CREATE INDEX q4d_index ON q4d_boxes_tbl USING spgist (home_base); CREATE TABLE quad_point_tbl AS SELECT point(unique1,unique2) AS p FROM tenk1; INSERT INTO quad_point_tbl @@ -113,6 +116,27 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; 278 (1 row) +SELECT * FROM q4d_boxes_tbl + WHERE home_base @ '(200,200),(2000,1000)'::box + ORDER BY (home_base[0])[0]; + home_base +----------------------- + (337,455),(240,359) + (1444,403),(1346,344) +(2 rows) + +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base && '(1000,1000,0,0)'::box; + count +------- + 2 +(1 row) + +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base IS NULL; + count +------- + 278 +(1 row) + SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; f1 @@ -458,6 +482,57 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; (1 row) EXPLAIN (COSTS OFF) +SELECT * FROM q4d_boxes_tbl + WHERE home_base @ '(200,200),(2000,1000)'::box + ORDER BY (home_base[0])[0]; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: ((home_base[0])[0]) + -> Index Only Scan using q4d_index on q4d_boxes_tbl + Filter: (home_base @ '(2000,1000),(200,200)'::box) +(4 rows) + +SELECT * FROM q4d_boxes_tbl + WHERE home_base @ '(200,200),(2000,1000)'::box + ORDER BY (home_base[0])[0]; + home_base +----------------------- + (337,455),(240,359) + (1444,403),(1346,344) +(2 rows) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base && '(1000,1000,0,0)'::box; + QUERY PLAN +------------------------------------------------------------- + Aggregate + -> Index Only Scan using q4d_index on q4d_boxes_tbl + Index Cond: (home_base && '(1000,1000),(0,0)'::box) +(3 rows) + +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base && '(1000,1000,0,0)'::box; + count +------- + 2 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base IS NULL; + QUERY PLAN +-------------------------------------------------------- + Aggregate + -> Index Only Scan using q4d_index on q4d_boxes_tbl + Index Cond: (home_base IS NULL) +(3 rows) + +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base IS NULL; + count +------- + 278 +(1 row) + +EXPLAIN (COSTS OFF) SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; QUERY PLAN diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index df29fe5..1ec0d57 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1704,15 +1704,17 @@ ORDER BY 1, 2, 3; 4000 | 6 | ~= 4000 | 7 | @> 4000 | 8 | <@ + 4000 | 10 | <<| 4000 | 10 | <^ 4000 | 11 | < 4000 | 11 | >^ + 4000 | 11 | |>> 4000 | 12 | <= 4000 | 14 | >= 4000 | 15 | > 4000 | 16 | @> 4000 | 18 | = -(108 rows) +(110 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index ff86953..655fcf0 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -100,6 +100,11 @@ CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1); -- SP-GiST -- +CREATE TEMP TABLE q4d_boxes_tbl AS + SELECT home_base AS home_base FROM fast_emp4000; + +CREATE INDEX q4d_index ON q4d_boxes_tbl USING spgist (home_base); + CREATE TABLE quad_point_tbl AS SELECT point(unique1,unique2) AS p FROM tenk1; @@ -142,6 +147,14 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; +SELECT * FROM q4d_boxes_tbl + WHERE home_base @ '(200,200),(2000,1000)'::box + ORDER BY (home_base[0])[0]; + +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base && '(1000,1000,0,0)'::box; + +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base IS NULL; + SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; @@ -250,6 +263,22 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; EXPLAIN (COSTS OFF) +SELECT * FROM q4d_boxes_tbl + WHERE home_base @ '(200,200),(2000,1000)'::box + ORDER BY (home_base[0])[0]; +SELECT * FROM q4d_boxes_tbl + WHERE home_base @ '(200,200),(2000,1000)'::box + ORDER BY (home_base[0])[0]; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base && '(1000,1000,0,0)'::box; +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base && '(1000,1000,0,0)'::box; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base IS NULL; +SELECT count(*) FROM q4d_boxes_tbl WHERE home_base IS NULL; + +EXPLAIN (COSTS OFF) SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon