]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Allow width_bucket()'s "operand" input to be NaN.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 2 Jul 2025 15:34:40 +0000 (11:34 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 2 Jul 2025 15:34:40 +0000 (11:34 -0400)
The array-based variant of width_bucket() has always accepted NaN
inputs, treating them as equal but larger than any non-NaN,
as we do in ordinary comparisons.  But up to now, the four-argument
variants threw errors for a NaN operand.  This is inconsistent
and unnecessary, since we can perfectly well regard NaN as falling
after the last bucket.

We do still throw error for NaN or infinity histogram-bound inputs,
since there's no way to compute sensible bucket boundaries.

Arguably this is a bug fix, but given the lack of field complaints
I'm content to fix it in master.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Discussion: https://postgr.es/m/2822872.1750540911@sss.pgh.pa.us

src/backend/utils/adt/float.c
src/backend/utils/adt/numeric.c
src/test/regress/expected/numeric.out
src/test/regress/sql/numeric.sql

index ba66a9c4ce63affca0a7cc026aec397f60eeac7b..7b97d2be6caedb6bcb5775dd8e6bf2d2499f54ad 100644 (file)
@@ -4067,8 +4067,9 @@ float84ge(PG_FUNCTION_ARGS)
  * with the specified characteristics. An operand smaller than the
  * lower bound is assigned to bucket 0. An operand greater than or equal
  * to the upper bound is assigned to an additional bucket (with number
- * count+1). We don't allow "NaN" for any of the float8 inputs, and we
- * don't allow either of the histogram bounds to be +/- infinity.
+ * count+1). We don't allow the histogram bounds to be NaN or +/- infinity,
+ * but we do allow those values for the operand (taking NaN to be larger
+ * than any other value, as we do in comparisons).
  */
 Datum
 width_bucket_float8(PG_FUNCTION_ARGS)
@@ -4084,12 +4085,11 @@ width_bucket_float8(PG_FUNCTION_ARGS)
                                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
                                 errmsg("count must be greater than zero")));
 
-       if (isnan(operand) || isnan(bound1) || isnan(bound2))
+       if (isnan(bound1) || isnan(bound2))
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
-                                errmsg("operand, lower bound, and upper bound cannot be NaN")));
+                                errmsg("lower and upper bounds cannot be NaN")));
 
-       /* Note that we allow "operand" to be infinite */
        if (isinf(bound1) || isinf(bound2))
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
@@ -4097,15 +4097,15 @@ width_bucket_float8(PG_FUNCTION_ARGS)
 
        if (bound1 < bound2)
        {
-               if (operand < bound1)
-                       result = 0;
-               else if (operand >= bound2)
+               if (isnan(operand) || operand >= bound2)
                {
                        if (pg_add_s32_overflow(count, 1, &result))
                                ereport(ERROR,
                                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                                 errmsg("integer out of range")));
                }
+               else if (operand < bound1)
+                       result = 0;
                else
                {
                        if (!isinf(bound2 - bound1))
@@ -4135,7 +4135,7 @@ width_bucket_float8(PG_FUNCTION_ARGS)
        }
        else if (bound1 > bound2)
        {
-               if (operand > bound1)
+               if (isnan(operand) || operand > bound1)
                        result = 0;
                else if (operand <= bound2)
                {
index 58ad1a65ef7b109b73860c4945661921632a596e..c9233565d57a7bfda52d3ceb8da001dfa65b6279 100644 (file)
@@ -1960,8 +1960,9 @@ generate_series_numeric_support(PG_FUNCTION_ARGS)
  * with the specified characteristics. An operand smaller than the
  * lower bound is assigned to bucket 0. An operand greater than or equal
  * to the upper bound is assigned to an additional bucket (with number
- * count+1). We don't allow "NaN" for any of the numeric inputs, and we
- * don't allow either of the histogram bounds to be +/- infinity.
+ * count+1). We don't allow the histogram bounds to be NaN or +/- infinity,
+ * but we do allow those values for the operand (taking NaN to be larger
+ * than any other value, as we do in comparisons).
  */
 Datum
 width_bucket_numeric(PG_FUNCTION_ARGS)
@@ -1979,17 +1980,13 @@ width_bucket_numeric(PG_FUNCTION_ARGS)
                                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
                                 errmsg("count must be greater than zero")));
 
-       if (NUMERIC_IS_SPECIAL(operand) ||
-               NUMERIC_IS_SPECIAL(bound1) ||
-               NUMERIC_IS_SPECIAL(bound2))
+       if (NUMERIC_IS_SPECIAL(bound1) || NUMERIC_IS_SPECIAL(bound2))
        {
-               if (NUMERIC_IS_NAN(operand) ||
-                       NUMERIC_IS_NAN(bound1) ||
-                       NUMERIC_IS_NAN(bound2))
+               if (NUMERIC_IS_NAN(bound1) || NUMERIC_IS_NAN(bound2))
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
-                                        errmsg("operand, lower bound, and upper bound cannot be NaN")));
-               /* We allow "operand" to be infinite; cmp_numerics will cope */
+                                        errmsg("lower and upper bounds cannot be NaN")));
+
                if (NUMERIC_IS_INF(bound1) || NUMERIC_IS_INF(bound2))
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
index 072d76ce13173095743b81172b5dc1ab3a6f4088..93e93be56689d730c9dd8f45b5567a33ffb660d6 100644 (file)
@@ -1464,9 +1464,21 @@ ERROR:  count must be greater than zero
 SELECT width_bucket(3.5::float8, 3.0::float8, 3.0::float8, 888);
 ERROR:  lower bound cannot equal upper bound
 SELECT width_bucket('NaN', 3.0, 4.0, 888);
-ERROR:  operand, lower bound, and upper bound cannot be NaN
+ width_bucket 
+--------------
+          889
+(1 row)
+
+SELECT width_bucket('NaN'::float8, 3.0::float8, 4.0::float8, 888);
+ width_bucket 
+--------------
+          889
+(1 row)
+
+SELECT width_bucket(0, 'NaN', 4.0, 888);
+ERROR:  lower and upper bounds cannot be NaN
 SELECT width_bucket(0::float8, 'NaN', 4.0::float8, 888);
-ERROR:  operand, lower bound, and upper bound cannot be NaN
+ERROR:  lower and upper bounds cannot be NaN
 SELECT width_bucket(2.0, 3.0, '-inf', 888);
 ERROR:  lower and upper bounds must be finite
 SELECT width_bucket(0::float8, '-inf', 4.0::float8, 888);
index b98ae27df5691dbe80dd6af90bcf0adc4fee5a4e..640c6d92f4ce7745e18e11f6545faa6a4768d8d2 100644 (file)
@@ -869,6 +869,8 @@ SELECT width_bucket(5.0::float8, 3.0::float8, 4.0::float8, 0);
 SELECT width_bucket(5.0::float8, 3.0::float8, 4.0::float8, -5);
 SELECT width_bucket(3.5::float8, 3.0::float8, 3.0::float8, 888);
 SELECT width_bucket('NaN', 3.0, 4.0, 888);
+SELECT width_bucket('NaN'::float8, 3.0::float8, 4.0::float8, 888);
+SELECT width_bucket(0, 'NaN', 4.0, 888);
 SELECT width_bucket(0::float8, 'NaN', 4.0::float8, 888);
 SELECT width_bucket(2.0, 3.0, '-inf', 888);
 SELECT width_bucket(0::float8, '-inf', 4.0::float8, 888);