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