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>&&</>
+ <literal><<</>
+ <literal>>></>
+ <literal><<|</>
+ <literal>|>></>
+ <literal><@</>
+ <literal>@></>
+ </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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers