Thread
Commits
-
Fix float4/float8 hash functions to produce uniform results for NaNs.
- dea212e245f6 9.6.24 landed
- be2beadaff1a 13.5 landed
- ad66373ead6d 11.14 landed
- a3bf136732b1 12.9 landed
- 2bb20e34c11f 10.19 landed
- 23c6bc581dc9 14.0 landed
- ce773f230d9b 15.0 landed
-
BUG #17172: NaN compare error in hash agg
The Post Office <noreply@postgresql.org> — 2021-09-01T02:25:31Z
The following bug has been logged on the website: Bug reference: 17172 Logged by: ma liangzhu Email address: ma100@hotmail.com PostgreSQL version: 14beta1 Operating system: centos7 Description: postgres=# select '-NaN'::float union select ('Infinity'::float + '-Infinity') union select 'NaN'; float8 -------- NaN NaN (2 rows) postgres=# set enable_hashagg =0; SET postgres=# select '-NaN'::float union select ('Infinity'::float + '-Infinity') union select 'NaN'; float8 -------- NaN (1 row) -
Re: BUG #17172: NaN compare error in hash agg
Tom Lane <tgl@sss.pgh.pa.us> — 2021-09-01T15:40:36Z
PG Bug reporting form <noreply@postgresql.org> writes: > postgres=# select '-NaN'::float union select ('Infinity'::float + > '-Infinity') union select 'NaN'; > float8 > -------- > NaN > NaN > (2 rows) Hm. The reason for that is that NaN and -NaN are different bitpatterns (0x7ff8000000000000 vs 0xfff8000000000000) so they produce different hash values. (On my machine, Inf plus -Inf produces -NaN, which is not what I'd have expected, but that's what I see.) Since float8_eq considers all NaNs equal, this is clearly a bug in the float hash functions. It's simple enough to fix, along the same lines that we special-case minus zero: check isnan() and return some fixed value if so. I'm inclined to do that like this: Datum hashfloat8(PG_FUNCTION_ARGS) { float8 key = PG_GETARG_FLOAT8(0); /* * On IEEE-float machines, minus zero and zero have different bit patterns * but should compare as equal. We must ensure that they have the same * hash value, which is most reliably done this way: */ if (key == (float8) 0) PG_RETURN_UINT32(0); + /* + * Similarly, NaNs can have different bit patterns but should compare + * as equal. For backwards-compatibility reasons we force them all to + * have the hash value of a standard NaN. + */ + if (isnan(key)) + key = get_float8_nan(); + return hash_any((unsigned char *) &key, sizeof(key)); } This has been broken for a very long time. Do we dare back-patch the fix? Given the lack of complaints, maybe fixing it in HEAD/v14 is enough. OTOH, it's not likely that many people have hash indexes containing minus NaNs, so maybe it's okay to back-patch. regards, tom lane -
Re: BUG #17172: NaN compare error in hash agg
Tom Lane <tgl@sss.pgh.pa.us> — 2021-09-02T21:28:04Z
I wrote: > This has been broken for a very long time. Do we dare back-patch > the fix? Given the lack of complaints, maybe fixing it in HEAD/v14 > is enough. OTOH, it's not likely that many people have hash indexes > containing minus NaNs, so maybe it's okay to back-patch. After further thought I concluded that there's little reason not to back-patch. If someone has -NaN in a hash index, they'd need to re-index if they ever want to find that entry again ... but it was already true that many queries would not find that entry. Hence, pushed to all branches. regards, tom lane