diff --git a/src/common/backend/utils/adt/float.cpp b/src/common/backend/utils/adt/float.cpp index cdd55b83cf6f821547b6496dcad64c19efbcbc26..c0bea9df67f3d1f046928a597c89c0a40e3b8e64 100644 --- a/src/common/backend/utils/adt/float.cpp +++ b/src/common/backend/utils/adt/float.cpp @@ -61,7 +61,6 @@ static const uint32 nan[2] = {0xffffffff, 0x7fffffff}; /* ========== USER I/O ROUTINES ========== */ static int float4_cmp_internal(float4 a, float4 b); -int float8_cmp_internal(float8 a, float8 b); double float8in_internal(char* str, char** s, bool* hasError); #ifndef HAVE_CBRT diff --git a/src/gausskernel/storage/access/gist/gistproc.cpp b/src/gausskernel/storage/access/gist/gistproc.cpp index 23059fd283092967a89a9c1b43b2f029e833a874..6dd7214af5cc8dc9a04c907f96df89c99e94189b 100644 --- a/src/gausskernel/storage/access/gist/gistproc.cpp +++ b/src/gausskernel/storage/access/gist/gistproc.cpp @@ -18,30 +18,80 @@ */ #include "postgres.h" #include "knl/knl_variable.h" +#include #include "access/gist.h" #include "access/skey.h" #include "utils/geo_decls.h" +#include "utils/builtins.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 **************************************************/ /* * 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) +static void 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_box) { - 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_box); + return size_box(&unionbox) - size_box(original); } /* @@ -77,18 +127,18 @@ Datum gist_box_consistent(PG_FUNCTION_ARGS) } } -static void adjustBox(BOX *b, BOX *addon) +static void 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; } } @@ -154,10 +204,8 @@ Datum 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); } @@ -255,13 +303,7 @@ static int interval_cmp_lower(const void *i1, const void *i2) double lower1 = ((const SplitInterval *)i1)->lower; double 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); } /* @@ -272,17 +314,11 @@ static int interval_cmp_upper(const void *i1, const void *i2) double upper1 = ((const SplitInterval *)i1)->upper; double 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) { @@ -390,19 +426,7 @@ static inline void g_box_consider_split(ConsiderSplitContext *context, int dimNu } /* - * Return increase of original BOX area by new BOX area insertion. - */ -static double box_penalty(BOX *original, BOX *newm) -{ - double union_width, union_height; - - union_width = Max(original->high.x, newm->high.x) - Min(original->low.x, newm->low.x); - union_height = Max(original->high.y, newm->high.y) - Min(original->low.y, newm->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. + * 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) { @@ -552,8 +576,10 @@ Datum gist_box_picksplit(PG_FUNCTION_ARGS) /* * Find next lower bound of right group. */ - while (i1 < nentries && rightLower == intervalsLower[i1].lower) { - leftUpper = Max(leftUpper, intervalsLower[i1].upper); + while (i1 < nentries && FLOAT8_EQ(rightLower, intervalsLower[i1].lower)) { + if (FLOAT8_LT(leftUpper, intervalsLower[i1].upper)) { + leftUpper = intervalsLower[i1].upper; + } i1++; } if (i1 >= nentries) { @@ -565,7 +591,7 @@ Datum 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++; } @@ -585,8 +611,10 @@ Datum gist_box_picksplit(PG_FUNCTION_ARGS) /* * Find next upper bound of left group. */ - while (i2 >= 0 && leftUpper == intervalsUpper[i2].upper) { - rightLower = Min(rightLower, intervalsUpper[i2].lower); + while (i2 >= 0 && FLOAT8_EQ(leftUpper, intervalsUpper[i2].upper)) { + if (FLOAT8_GT(rightLower, intervalsUpper[i2].lower)) { + rightLower = intervalsUpper[i2].lower; + } i2--; } if (i2 < 0) { @@ -595,7 +623,7 @@ Datum gist_box_picksplit(PG_FUNCTION_ARGS) leftUpper = intervalsUpper[i2].upper; // 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--; } @@ -673,9 +701,9 @@ Datum 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; } else { @@ -688,7 +716,7 @@ Datum 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); @@ -767,8 +795,8 @@ Datum gist_box_same(PG_FUNCTION_ARGS) bool *result = (bool *)PG_GETARG_POINTER(2); if (b1 != NULL && b2 != NULL) { - *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); } @@ -828,14 +856,6 @@ static bool gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strate 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) *****************************************/ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 0a9efc02bfa311b2b2707d0df84ea0ddca5647ec..5f6d9ff1bbcc653f0f48366350eedc1667c9e350 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -1842,4 +1842,7 @@ extern Datum pg_read_binary_file_blocks(PG_FUNCTION_ARGS); extern char *pg_ultostr(char *str, uint32 value); extern char *pg_ultostr_zeropad(char *str, uint32 value, int32 minwidth); +/* float.cpp */ +extern int float8_cmp_internal(float8 a, float8 b); + #endif /* BUILTINS_H */ diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h index 5c249725984ff03fc3938c411c627d27242d31e6..1cd1824f7f3b66e570bd69e13054bd28c09bf527 100644 --- a/src/include/utils/geo_decls.h +++ b/src/include/utils/geo_decls.h @@ -33,20 +33,20 @@ #ifdef EPSILON #define FPzero(A) (fabs(A) <= EPSILON) -#define FPeq(A, B) (fabs((A) - (B)) <= EPSILON) -#define FPne(A, B) (fabs((A) - (B)) > EPSILON) -#define FPlt(A, B) ((B) - (A) > EPSILON) -#define FPle(A, B) ((A) - (B) <= EPSILON) -#define FPgt(A, B) ((A) - (B) > EPSILON) -#define FPge(A, B) ((B) - (A) <= EPSILON) +#define FPeq(A, B) (fabs((A) - (B)) <= EPSILON || (isnan(A) && isnan(B))) +#define FPne(A, B) !FPeq(A, B) +#define FPlt(A, B) ((B) - (A) > EPSILON || (isnan(B) && !isnan(A))) +#define FPle(A, B) ((A) - (B) <= EPSILON || isnan(B)) +#define FPgt(A, B) ((A) - (B) > EPSILON || (isnan(A) && !isnan(B))) +#define FPge(A, B) ((B) - (A) <= EPSILON || isnan(A)) #else #define FPzero(A) ((A) == 0) -#define FPeq(A, B) ((A) == (B)) -#define FPne(A, B) ((A) != (B)) -#define FPlt(A, B) ((A) < (B)) -#define FPle(A, B) ((A) <= (B)) -#define FPgt(A, B) ((A) > (B)) -#define FPge(A, B) ((A) >= (B)) +#define FPeq(A, B) ((A) == (B) || (isnan(A) && isnan(B))) +#define FPne(A, B) !FPeq(A, B) +#define FPlt(A, B) ((A) < (B) || (isnan(B) && !isnan(A))) +#define FPle(A, B) ((A) <= (B) || isnan(B)) +#define FPgt(A, B) ((A) > (B) || (isnan(A) && !isnan(B))) +#define FPge(A, B) ((A) >= (B) || isnan(A)) #endif #define HYPOT(A, B) pg_hypot(A, B) diff --git a/src/test/regress/expected/single_node_point.out b/src/test/regress/expected/single_node_point.out index e44af88e71877c626229762691cef82b169547f6..2e515c83e598402ed16a54eb7b394d7c8a319a05 100644 --- a/src/test/regress/expected/single_node_point.out +++ b/src/test/regress/expected/single_node_point.out @@ -298,3 +298,20 @@ SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; +CREATE TABLE point_tbl_test(f1 point); +CREATE INDEX gpointind_test ON point_tbl_test USING gist(f1); +INSERT INTO point_tbl_test SELECT ('0,0') FROM generate_series(1, 185); +INSERT INTO point_tbl_test VALUES ('0,NaN'); +select count(*) from point_tbl_test where f1~='0,0'; + count +------- + 185 +(1 row) + +select count(*) from point_tbl_test where f1~='0,NaN'; + count +------- + 1 +(1 row) + +drop table point_tbl_test; diff --git a/src/test/regress/sql/single_node_point.sql b/src/test/regress/sql/single_node_point.sql index 63a803a809dda4497b452618b1314be87063f9da..e937161947de2cd6b5c3b67710ada314c0bbe199 100644 --- a/src/test/regress/sql/single_node_point.sql +++ b/src/test/regress/sql/single_node_point.sql @@ -101,3 +101,11 @@ SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; + +CREATE TABLE point_tbl_test(f1 point); +CREATE INDEX gpointind_test ON point_tbl_test USING gist(f1); +INSERT INTO point_tbl_test SELECT ('0,0') FROM generate_series(1, 185); +INSERT INTO point_tbl_test VALUES ('0,NaN'); +select count(*) from point_tbl_test where f1~='0,0'; +select count(*) from point_tbl_test where f1~='0,NaN'; +drop table point_tbl_test;