1
0
mirror of https://github.com/postgres/postgres.git synced 2025-10-25 13:17:41 +03:00

Allow width_bucket()'s "operand" input to be NaN.

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
This commit is contained in:
Tom Lane
2025-07-02 11:34:40 -04:00
parent c989affb52
commit 7374b3a536
4 changed files with 32 additions and 21 deletions

View File

@@ -4067,8 +4067,9 @@ float84ge(PG_FUNCTION_ARGS)
* with the specified characteristics. An operand smaller than the * with the specified characteristics. An operand smaller than the
* lower bound is assigned to bucket 0. An operand greater than or equal * 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 * 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 * count+1). We don't allow the histogram bounds to be NaN or +/- infinity,
* don't allow either of the histogram bounds to be +/- 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 Datum
width_bucket_float8(PG_FUNCTION_ARGS) width_bucket_float8(PG_FUNCTION_ARGS)
@@ -4084,12 +4085,11 @@ width_bucket_float8(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
errmsg("count must be greater than zero"))); errmsg("count must be greater than zero")));
if (isnan(operand) || isnan(bound1) || isnan(bound2)) if (isnan(bound1) || isnan(bound2))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), (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)) if (isinf(bound1) || isinf(bound2))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
@@ -4097,15 +4097,15 @@ width_bucket_float8(PG_FUNCTION_ARGS)
if (bound1 < bound2) if (bound1 < bound2)
{ {
if (operand < bound1) if (isnan(operand) || operand >= bound2)
result = 0;
else if (operand >= bound2)
{ {
if (pg_add_s32_overflow(count, 1, &result)) if (pg_add_s32_overflow(count, 1, &result))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
} }
else if (operand < bound1)
result = 0;
else else
{ {
if (!isinf(bound2 - bound1)) if (!isinf(bound2 - bound1))
@@ -4135,7 +4135,7 @@ width_bucket_float8(PG_FUNCTION_ARGS)
} }
else if (bound1 > bound2) else if (bound1 > bound2)
{ {
if (operand > bound1) if (isnan(operand) || operand > bound1)
result = 0; result = 0;
else if (operand <= bound2) else if (operand <= bound2)
{ {

View File

@@ -1960,8 +1960,9 @@ generate_series_numeric_support(PG_FUNCTION_ARGS)
* with the specified characteristics. An operand smaller than the * with the specified characteristics. An operand smaller than the
* lower bound is assigned to bucket 0. An operand greater than or equal * 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 * 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 * count+1). We don't allow the histogram bounds to be NaN or +/- infinity,
* don't allow either of the histogram bounds to be +/- 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 Datum
width_bucket_numeric(PG_FUNCTION_ARGS) width_bucket_numeric(PG_FUNCTION_ARGS)
@@ -1979,17 +1980,13 @@ width_bucket_numeric(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
errmsg("count must be greater than zero"))); errmsg("count must be greater than zero")));
if (NUMERIC_IS_SPECIAL(operand) || if (NUMERIC_IS_SPECIAL(bound1) || NUMERIC_IS_SPECIAL(bound2))
NUMERIC_IS_SPECIAL(bound1) ||
NUMERIC_IS_SPECIAL(bound2))
{ {
if (NUMERIC_IS_NAN(operand) || if (NUMERIC_IS_NAN(bound1) || NUMERIC_IS_NAN(bound2))
NUMERIC_IS_NAN(bound1) ||
NUMERIC_IS_NAN(bound2))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), (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")));
/* We allow "operand" to be infinite; cmp_numerics will cope */
if (NUMERIC_IS_INF(bound1) || NUMERIC_IS_INF(bound2)) if (NUMERIC_IS_INF(bound1) || NUMERIC_IS_INF(bound2))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),

View 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); SELECT width_bucket(3.5::float8, 3.0::float8, 3.0::float8, 888);
ERROR: lower bound cannot equal upper bound ERROR: lower bound cannot equal upper bound
SELECT width_bucket('NaN', 3.0, 4.0, 888); 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); 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); SELECT width_bucket(2.0, 3.0, '-inf', 888);
ERROR: lower and upper bounds must be finite ERROR: lower and upper bounds must be finite
SELECT width_bucket(0::float8, '-inf', 4.0::float8, 888); SELECT width_bucket(0::float8, '-inf', 4.0::float8, 888);

View 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(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(3.5::float8, 3.0::float8, 3.0::float8, 888);
SELECT width_bucket('NaN', 3.0, 4.0, 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(0::float8, 'NaN', 4.0::float8, 888);
SELECT width_bucket(2.0, 3.0, '-inf', 888); SELECT width_bucket(2.0, 3.0, '-inf', 888);
SELECT width_bucket(0::float8, '-inf', 4.0::float8, 888); SELECT width_bucket(0::float8, '-inf', 4.0::float8, 888);