diff options
Diffstat (limited to 'src/backend/access/gist/gistproc.c')
-rw-r--r-- | src/backend/access/gist/gistproc.c | 151 |
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) *****************************************/ |