Hello, Hacker.

* [PATCH] add a box index to sp-gist

  We have extended sp-gist with an index that keeps track of boxes

  We have used ideas underlying sp-gist range implementation to
  represent 2D boxes as points in 4D space. We use quad tree
  analogue, but in 4-dimensional space. We call this tree q4d. Each
  node of this tree is a box (a point in 4D space) which splits space
  in 16 hyperrectangles.

  Rationale: r-tree assumes that boxes we're trying to index don't
  overlap much. When this assumption fails, r-tree performs badly,
  while our proposal to represent a rectangle as a point in 4D space
  solves this problem.

  NB: the index uses traversalValue introduced in a separate patch.

* [PATCH] add traversalValue in sp-gist

  During implementation of box index for sp-gist we saw that we only
  keep rectangles, but to determine traversal direction we may need
  to know boundaries of a hyperrectangle. So we calculate them
  on the fly and store them in traversalValue, available
  when traversing child nodes, because otherwise we would't be able to
  calculate them from inside the inner_consistent function call.

  This patch was written by Teodor Sigaev.

* [PATCH] change reconstructValue -> traversalValue in range_spgist.c

  During traversal, local context is
  insufficient to pick traversal direction. As of now, this is worked
  around with the help of reconstructValue. reconstructValue only
  stores data of the same type as a tree node, that is, a range.

  We have written a patch that calculates auxillary values and makes
  them accessible during traversal.

  We then use traversalValue in a new box index and change
  range_spgist.c to use it in place of reconstructValue.

  NB: apply this patch after traversalValue patch.

diff --git a/doc/src/sgml/spgist.sgml b/doc/src/sgml/spgist.sgml
index 56827e5..d558b8e 100644
--- a/doc/src/sgml/spgist.sgml
+++ b/doc/src/sgml/spgist.sgml
@@ -542,6 +542,8 @@ typedef struct spgInnerConsistentIn
     int         nkeys;          /* length of array */
 
     Datum       reconstructedValue;     /* value reconstructed at parent */
+    void       *traversalValue; /* opclass-specific traverse value */
+    MemoryContext traversalMemoryContext;
     int         level;          /* current level (counting from zero) */
     bool        returnData;     /* original data must be returned? */
 
@@ -559,6 +561,8 @@ typedef struct spgInnerConsistentOut
     int        *nodeNumbers;    /* their indexes in the node array */
     int        *levelAdds;      /* increment level by this much for each */
     Datum      *reconstructedValues;    /* associated reconstructed values */
+    void      **traversalValues;        /* opclass-specific traverse values */
+
 } spgInnerConsistentOut;
 </programlisting>
 
@@ -593,6 +597,9 @@ typedef struct spgInnerConsistentOut
        inner tuple, and
        <structfield>nodeLabels</> is an array of their label values, or
        NULL if the nodes do not have labels.
+       <structfield>traversalValue</> is a pointer to data that
+       <function>inner_consistent</> gets when called on child nodes from an
+       outer call of <function>inner_consistent</> on parent nodes.
       </para>
 
       <para>
@@ -612,6 +619,15 @@ typedef struct spgInnerConsistentOut
        responsible for palloc'ing the
        <structfield>nodeNumbers</>, <structfield>levelAdds</> and
        <structfield>reconstructedValues</> arrays.
+       Sometimes accumulating some information is needed, while 
+       descending from parent to child node was happened. In this case
+
+       <structfield>traversalValues</> array keeps pointers to
+       specific data you need to accumulate for every child node.
+       Memory for <structfield>traversalValues</> should be allocated in
+       the default context, but make sure to switch to
+       <structfield>traversalMemoryContext</> before allocating memory
+       for pointers themselves.
       </para>
      </listitem>
     </varlistentry>
@@ -638,6 +654,7 @@ typedef struct spgLeafConsistentIn
     ScanKey     scankeys;       /* array of operators and comparison values */
     int         nkeys;          /* length of array */
 
+    void       *traversalValue; /* opclass-specific traverse value */
     Datum       reconstructedValue;     /* value reconstructed at parent */
     int         level;          /* current level (counting from zero) */
     bool        returnData;     /* original data must be returned? */
diff --git a/src/backend/access/spgist/spgscan.c 
b/src/backend/access/spgist/spgscan.c
index 8a0d909..bec6daf 100644
--- a/src/backend/access/spgist/spgscan.c
+++ b/src/backend/access/spgist/spgscan.c
@@ -30,6 +30,7 @@ typedef void (*storeRes_func) (SpGistScanOpaque so, 
ItemPointer heapPtr,
 typedef struct ScanStackEntry
 {
        Datum           reconstructedValue;             /* value reconstructed 
from parent */
+       void       *traversalValue; /* opclass-specific traverse value */
        int                     level;                  /* level of items on 
this page */
        ItemPointerData ptr;            /* block and offset to scan from */
 } ScanStackEntry;
@@ -42,6 +43,9 @@ freeScanStackEntry(SpGistScanOpaque so, ScanStackEntry 
*stackEntry)
        if (!so->state.attType.attbyval &&
                DatumGetPointer(stackEntry->reconstructedValue) != NULL)
                pfree(DatumGetPointer(stackEntry->reconstructedValue));
+       if (stackEntry->traversalValue)
+               pfree(stackEntry->traversalValue);
+
        pfree(stackEntry);
 }
 
@@ -263,6 +267,7 @@ static bool
 spgLeafTest(Relation index, SpGistScanOpaque so,
                        SpGistLeafTuple leafTuple, bool isnull,
                        int level, Datum reconstructedValue,
+                       void *traversalValue,
                        Datum *leafValue, bool *recheck)
 {
        bool            result;
@@ -289,6 +294,7 @@ spgLeafTest(Relation index, SpGistScanOpaque so,
        in.scankeys = so->keyData;
        in.nkeys = so->numberOfKeys;
        in.reconstructedValue = reconstructedValue;
+       in.traversalValue = traversalValue;
        in.level = level;
        in.returnData = so->want_itup;
        in.leafDatum = leafDatum;
@@ -389,6 +395,7 @@ redirect:
                                                                        
leafTuple, isnull,
                                                                        
stackEntry->level,
                                                                        
stackEntry->reconstructedValue,
+                                                                       
stackEntry->traversalValue,
                                                                        
&leafValue,
                                                                        
&recheck))
                                        {
@@ -435,6 +442,7 @@ redirect:
                                                                        
leafTuple, isnull,
                                                                        
stackEntry->level,
                                                                        
stackEntry->reconstructedValue,
+                                                                       
stackEntry->traversalValue,
                                                                        
&leafValue,
                                                                        
&recheck))
                                        {
@@ -480,6 +488,8 @@ redirect:
                        in.scankeys = so->keyData;
                        in.nkeys = so->numberOfKeys;
                        in.reconstructedValue = stackEntry->reconstructedValue;
+                       in.traversalMemoryContext = oldCtx;
+                       in.traversalValue = stackEntry->traversalValue;
                        in.level = stackEntry->level;
                        in.returnData = so->want_itup;
                        in.allTheSame = innerTuple->allTheSame;
@@ -547,6 +557,9 @@ redirect:
                                        else
                                                newEntry->reconstructedValue = 
(Datum) 0;
 
+                                       newEntry->traversalValue = 
(out.traversalValues) ?
+                                               out.traversalValues[i] : NULL;
+
                                        so->scanStack = lcons(newEntry, 
so->scanStack);
                                }
                        }
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 0cb8fd9..b3bbdf4 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -133,6 +133,8 @@ typedef struct spgInnerConsistentIn
        int                     nkeys;                  /* length of array */
 
        Datum           reconstructedValue;             /* value reconstructed 
at parent */
+       void       *traversalValue; /* opclass-specific traverse value */
+       MemoryContext traversalMemoryContext;
        int                     level;                  /* current level 
(counting from zero) */
        bool            returnData;             /* original data must be 
returned? */
 
@@ -150,6 +152,7 @@ typedef struct spgInnerConsistentOut
        int                *nodeNumbers;        /* their indexes in the node 
array */
        int                *levelAdds;          /* increment level by this much 
for each */
        Datum      *reconstructedValues;        /* associated reconstructed 
values */
+       void      **traversalValues;    /* opclass-specific traverse values */
 } spgInnerConsistentOut;
 
 /*
@@ -160,6 +163,7 @@ typedef struct spgLeafConsistentIn
        ScanKey         scankeys;               /* array of operators and 
comparison values */
        int                     nkeys;                  /* length of array */
 
+       void       *traversalValue; /* opclass-specific traverse value */
        Datum           reconstructedValue;             /* value reconstructed 
at parent */
        int                     level;                  /* current level 
(counting from zero) */
        bool            returnData;             /* original data must be 
returned? */
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 @@
       </entry>
      </row>
      <row>
+      <entry><literal>box_ops</></entry>
+      <entry>box</entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;&lt;</>
+       <literal>&gt;&gt;</>
+       <literal>&lt;&lt;|</>
+       <literal>|&gt;&gt;</>
+       <literal>&lt;@</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
       <entry><literal>text_ops</></entry>
       <entry><type>text</></entry>
       <entry>
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
diff --git a/src/backend/utils/adt/rangetypes_spgist.c 
b/src/backend/utils/adt/rangetypes_spgist.c
index 3b5529e..23b1b9d 100644
--- a/src/backend/utils/adt/rangetypes_spgist.c
+++ b/src/backend/utils/adt/rangetypes_spgist.c
@@ -310,14 +310,12 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
        spgInnerConsistentOut *out = (spgInnerConsistentOut *) 
PG_GETARG_POINTER(1);
        int                     which;
        int                     i;
+       MemoryContext oldCtx;
 
        /*
         * For adjacent search we need also previous centroid (if any) to 
improve
         * the precision of the consistent check. In this case needPrevious flag
-        * is set and centroid is passed into reconstructedValues. This is not 
the
-        * intended purpose of reconstructedValues (because we already have the
-        * full value available at the leaf), but it's a convenient place to 
store
-        * state while traversing the tree.
+        * is set and centroid is passed into traversalValue.
         */
        bool            needPrevious = false;
 
@@ -565,9 +563,9 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
                                         * for lower or upper bounds to be 
adjacent. Deserialize
                                         * previous centroid range if present 
for checking this.
                                         */
-                                       if (in->reconstructedValue != (Datum) 0)
+                                       if (in->traversalValue != (Datum) 0)
                                        {
-                                               prevCentroid = 
DatumGetRangeType(in->reconstructedValue);
+                                               prevCentroid = 
DatumGetRangeType(in->traversalValue);
                                                range_deserialize(typcache, 
prevCentroid,
                                                                                
  &prevLower, &prevUpper, &prevEmpty);
                                        }
@@ -746,19 +744,33 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
        /* We must descend into the quadrant(s) identified by 'which' */
        out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
        if (needPrevious)
-               out->reconstructedValues = (Datum *) palloc(sizeof(Datum) * 
in->nNodes);
+               out->traversalValues = (void **) palloc(sizeof(void *) * 
in->nNodes);
        out->nNodes = 0;
+
+       oldCtx = MemoryContextSwitchTo(in->traversalMemoryContext);
+
        for (i = 1; i <= in->nNodes; i++)
        {
                if (which & (1 << i))
                {
                        /* Save previous prefix if needed */
                        if (needPrevious)
-                               out->reconstructedValues[out->nNodes] = 
in->prefixDatum;
-                       out->nodeNumbers[out->nNodes++] = i - 1;
+                       {
+                               Datum previousCentroid;
+
+                               /* We know, that in->prefixDatum in this place 
is varlena,
+                                * because it's range
+                                */
+                               previousCentroid = datumCopy(in->prefixDatum, 
false, -1);
+                               out->traversalValues[out->nNodes] = (void 
*)previousCentroid;
+                       }
+                       out->nodeNumbers[out->nNodes] = i - 1;
+                       out->nNodes++;
                }
        }
 
+       MemoryContextSwitchTo(oldCtx);
+
        PG_RETURN_VOID();
 }
 
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to