summaryrefslogtreecommitdiff
path: root/src/backend/access/gist/gistproc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/gist/gistproc.c')
-rw-r--r--src/backend/access/gist/gistproc.c151
1 files changed, 88 insertions, 63 deletions
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index e8213e2baf..d47211afc0 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -17,20 +17,31 @@
*/
#include "postgres.h"
+#include <math.h>
+
#include "access/gist.h"
#include "access/stratnum.h"
+#include "utils/builtins.h"
#include "utils/geo_decls.h"
static bool gist_box_leaf_consistent(BOX *key, BOX *query,
StrategyNumber strategy);
-static double size_box(BOX *box);
static bool rtree_internal_consistent(BOX *key, BOX *query,
StrategyNumber strategy);
/* Minimum accepted ratio of split */
#define LIMIT_RATIO 0.3
+/* Convenience macros for NaN-aware comparisons */
+#define FLOAT8_EQ(a,b) (float8_cmp_internal(a, b) == 0)
+#define FLOAT8_LT(a,b) (float8_cmp_internal(a, b) < 0)
+#define FLOAT8_LE(a,b) (float8_cmp_internal(a, b) <= 0)
+#define FLOAT8_GT(a,b) (float8_cmp_internal(a, b) > 0)
+#define FLOAT8_GE(a,b) (float8_cmp_internal(a, b) >= 0)
+#define FLOAT8_MAX(a,b) (FLOAT8_GT(a, b) ? (a) : (b))
+#define FLOAT8_MIN(a,b) (FLOAT8_LT(a, b) ? (a) : (b))
+
/**************************************************
* Box ops
@@ -40,12 +51,53 @@ static bool rtree_internal_consistent(BOX *key, BOX *query,
* Calculates union of two boxes, a and b. The result is stored in *n.
*/
static void
-rt_box_union(BOX *n, BOX *a, BOX *b)
+rt_box_union(BOX *n, const BOX *a, const BOX *b)
+{
+ n->high.x = FLOAT8_MAX(a->high.x, b->high.x);
+ n->high.y = FLOAT8_MAX(a->high.y, b->high.y);
+ n->low.x = FLOAT8_MIN(a->low.x, b->low.x);
+ n->low.y = FLOAT8_MIN(a->low.y, b->low.y);
+}
+
+/*
+ * Size of a BOX for penalty-calculation purposes.
+ * The result can be +Infinity, but not NaN.
+ */
+static double
+size_box(const BOX *box)
+{
+ /*
+ * Check for zero-width cases. Note that we define the size of a zero-
+ * by-infinity box as zero. It's important to special-case this somehow,
+ * as naively multiplying infinity by zero will produce NaN.
+ *
+ * The less-than cases should not happen, but if they do, say "zero".
+ */
+ if (FLOAT8_LE(box->high.x, box->low.x) ||
+ FLOAT8_LE(box->high.y, box->low.y))
+ return 0.0;
+
+ /*
+ * We treat NaN as larger than +Infinity, so any distance involving a NaN
+ * and a non-NaN is infinite. Note the previous check eliminated the
+ * possibility that the low fields are NaNs.
+ */
+ if (isnan(box->high.x) || isnan(box->high.y))
+ return get_float8_infinity();
+ return (box->high.x - box->low.x) * (box->high.y - box->low.y);
+}
+
+/*
+ * Return amount by which the union of the two boxes is larger than
+ * the original BOX's area. The result can be +Infinity, but not NaN.
+ */
+static double
+box_penalty(const BOX *original, const BOX *new)
{
- n->high.x = Max(a->high.x, b->high.x);
- n->high.y = Max(a->high.y, b->high.y);
- n->low.x = Min(a->low.x, b->low.x);
- n->low.y = Min(a->low.y, b->low.y);
+ BOX unionbox;
+
+ rt_box_union(&unionbox, original, new);
+ return size_box(&unionbox) - size_box(original);
}
/*
@@ -85,16 +137,19 @@ gist_box_consistent(PG_FUNCTION_ARGS)
strategy));
}
+/*
+ * Increase BOX b to include addon.
+ */
static void
-adjustBox(BOX *b, BOX *addon)
+adjustBox(BOX *b, const BOX *addon)
{
- if (b->high.x < addon->high.x)
+ if (FLOAT8_LT(b->high.x, addon->high.x))
b->high.x = addon->high.x;
- if (b->low.x > addon->low.x)
+ if (FLOAT8_GT(b->low.x, addon->low.x))
b->low.x = addon->low.x;
- if (b->high.y < addon->high.y)
+ if (FLOAT8_LT(b->high.y, addon->high.y))
b->high.y = addon->high.y;
- if (b->low.y > addon->low.y)
+ if (FLOAT8_GT(b->low.y, addon->low.y))
b->low.y = addon->low.y;
}
@@ -174,10 +229,8 @@ gist_box_penalty(PG_FUNCTION_ARGS)
float *result = (float *) PG_GETARG_POINTER(2);
BOX *origbox = DatumGetBoxP(origentry->key);
BOX *newbox = DatumGetBoxP(newentry->key);
- BOX unionbox;
- rt_box_union(&unionbox, origbox, newbox);
- *result = (float) (size_box(&unionbox) - size_box(origbox));
+ *result = (float) box_penalty(origbox, newbox);
PG_RETURN_POINTER(result);
}
@@ -290,12 +343,7 @@ interval_cmp_lower(const void *i1, const void *i2)
double lower1 = ((const SplitInterval *) i1)->lower,
lower2 = ((const SplitInterval *) i2)->lower;
- if (lower1 < lower2)
- return -1;
- else if (lower1 > lower2)
- return 1;
- else
- return 0;
+ return float8_cmp_internal(lower1, lower2);
}
/*
@@ -307,16 +355,11 @@ interval_cmp_upper(const void *i1, const void *i2)
double upper1 = ((const SplitInterval *) i1)->upper,
upper2 = ((const SplitInterval *) i2)->upper;
- if (upper1 < upper2)
- return -1;
- else if (upper1 > upper2)
- return 1;
- else
- return 0;
+ return float8_cmp_internal(upper1, upper2);
}
/*
- * Replace negative value with zero.
+ * Replace negative (or NaN) value with zero.
*/
static inline float
non_negative(float val)
@@ -436,24 +479,8 @@ g_box_consider_split(ConsiderSplitContext *context, int dimNum,
}
/*
- * Return increase of original BOX area by new BOX area insertion.
- */
-static double
-box_penalty(BOX *original, BOX *new)
-{
- double union_width,
- union_height;
-
- union_width = Max(original->high.x, new->high.x) -
- Min(original->low.x, new->low.x);
- union_height = Max(original->high.y, new->high.y) -
- Min(original->low.y, new->low.y);
- return union_width * union_height - (original->high.x - original->low.x) *
- (original->high.y - original->low.y);
-}
-
-/*
* Compare common entries by their deltas.
+ * (We assume the deltas can't be NaN.)
*/
static int
common_entry_cmp(const void *i1, const void *i2)
@@ -615,9 +642,11 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
/*
* Find next lower bound of right group.
*/
- while (i1 < nentries && rightLower == intervalsLower[i1].lower)
+ while (i1 < nentries &&
+ FLOAT8_EQ(rightLower, intervalsLower[i1].lower))
{
- leftUpper = Max(leftUpper, intervalsLower[i1].upper);
+ if (FLOAT8_LT(leftUpper, intervalsLower[i1].upper))
+ leftUpper = intervalsLower[i1].upper;
i1++;
}
if (i1 >= nentries)
@@ -628,7 +657,8 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
* Find count of intervals which anyway should be placed to the
* left group.
*/
- while (i2 < nentries && intervalsUpper[i2].upper <= leftUpper)
+ while (i2 < nentries &&
+ FLOAT8_LE(intervalsUpper[i2].upper, leftUpper))
i2++;
/*
@@ -650,9 +680,10 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
/*
* Find next upper bound of left group.
*/
- while (i2 >= 0 && leftUpper == intervalsUpper[i2].upper)
+ while (i2 >= 0 && FLOAT8_EQ(leftUpper, intervalsUpper[i2].upper))
{
- rightLower = Min(rightLower, intervalsUpper[i2].lower);
+ if (FLOAT8_GT(rightLower, intervalsUpper[i2].lower))
+ rightLower = intervalsUpper[i2].lower;
i2--;
}
if (i2 < 0)
@@ -663,7 +694,7 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
* Find count of intervals which anyway should be placed to the
* right group.
*/
- while (i1 >= 0 && intervalsLower[i1].lower >= rightLower)
+ while (i1 >= 0 && FLOAT8_GE(intervalsLower[i1].lower, rightLower))
i1--;
/*
@@ -751,10 +782,10 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
upper = box->high.y;
}
- if (upper <= context.leftUpper)
+ if (FLOAT8_LE(upper, context.leftUpper))
{
/* Fits to the left group */
- if (lower >= context.rightLower)
+ if (FLOAT8_GE(lower, context.rightLower))
{
/* Fits also to the right group, so "common entry" */
commonEntries[commonEntriesCount++].index = i;
@@ -772,7 +803,7 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
* entry didn't fit on the left group, it better fit in the right
* group.
*/
- Assert(lower >= context.rightLower);
+ Assert(FLOAT8_GE(lower, context.rightLower));
/* Doesn't fit to the left group, so join to the right group */
PLACE_RIGHT(box, i);
@@ -856,8 +887,10 @@ gist_box_same(PG_FUNCTION_ARGS)
bool *result = (bool *) PG_GETARG_POINTER(2);
if (b1 && b2)
- *result = (b1->low.x == b2->low.x && b1->low.y == b2->low.y &&
- b1->high.x == b2->high.x && b1->high.y == b2->high.y);
+ *result = (FLOAT8_EQ(b1->low.x, b2->low.x) &&
+ FLOAT8_EQ(b1->low.y, b2->low.y) &&
+ FLOAT8_EQ(b1->high.x, b2->high.x) &&
+ FLOAT8_EQ(b1->high.y, b2->high.y));
else
*result = (b1 == NULL && b2 == NULL);
PG_RETURN_POINTER(result);
@@ -943,14 +976,6 @@ gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
return retval;
}
-static double
-size_box(BOX *box)
-{
- if (box->high.x <= box->low.x || box->high.y <= box->low.y)
- return 0.0;
- return (box->high.x - box->low.x) * (box->high.y - box->low.y);
-}
-
/*****************************************
* Common rtree functions (for boxes, polygons, and circles)
*****************************************/