Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitce773f2

Browse files
committed
Fix float4/float8 hash functions to produce uniform results for NaNs.
The IEEE 754 standard allows a wide variety of bit patterns for NaNs,of which at least two ("NaN" and "-NaN") are pretty easy to producefrom SQL on most machines. This is problematic because our btreecomparison functions deem all NaNs to be equal, but our float hashfunctions know nothing about NaNs and will happily produce varyinghash codes for them. That causes unexpected results from queriesthat hash a column containing different NaN values. It could alsoproduce unexpected lookup failures when using a hash index on afloat column, i.e. "WHERE x = 'NaN'" will not find all the rowsit should.To fix, special-case NaN in the float hash functions, not too muchunlike the existing special case that forces zero and minus zeroto hash the same. I arranged for the most vanilla sort of NaN(that coming from the C99 NAN constant) to still have the samehash code as before, to reduce the risk to existing hash indexes.I dithered about whether to back-patch this into stable branches,but ultimately decided to do so. It's a clear improvement forqueries that hash internally. If there is anybody who has -NaNin a hash index, they'd be well advised to re-index after applyingthis patch ... but the misbehavior if they don't will not be muchworse than the misbehavior they had before.Per bug #17172 from Ma Liangzhu.Discussion:https://postgr.es/m/17172-7505bea9e04e230f@postgresql.org
1 parentba1b763 commitce773f2

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

‎src/backend/access/hash/hashfunc.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include"catalog/pg_collation.h"
3131
#include"common/hashfn.h"
3232
#include"utils/builtins.h"
33+
#include"utils/float.h"
3334
#include"utils/pg_locale.h"
3435

3536
/*
@@ -150,6 +151,14 @@ hashfloat4(PG_FUNCTION_ARGS)
150151
if (key== (float4)0)
151152
PG_RETURN_UINT32(0);
152153

154+
/*
155+
* Similarly, NaNs can have different bit patterns but they should all
156+
* compare as equal. For backwards-compatibility reasons we force them to
157+
* have the hash value of a standard NaN.
158+
*/
159+
if (isnan(key))
160+
key=get_float4_nan();
161+
153162
/*
154163
* To support cross-type hashing of float8 and float4, we want to return
155164
* the same hash value hashfloat8 would produce for an equal float8 value.
@@ -172,6 +181,8 @@ hashfloat4extended(PG_FUNCTION_ARGS)
172181
/* Same approach as hashfloat4 */
173182
if (key== (float4)0)
174183
PG_RETURN_UINT64(seed);
184+
if (isnan(key))
185+
key=get_float4_nan();
175186
key8=key;
176187

177188
returnhash_any_extended((unsignedchar*)&key8,sizeof(key8),seed);
@@ -190,6 +201,14 @@ hashfloat8(PG_FUNCTION_ARGS)
190201
if (key== (float8)0)
191202
PG_RETURN_UINT32(0);
192203

204+
/*
205+
* Similarly, NaNs can have different bit patterns but they should all
206+
* compare as equal. For backwards-compatibility reasons we force them to
207+
* have the hash value of a standard NaN.
208+
*/
209+
if (isnan(key))
210+
key=get_float8_nan();
211+
193212
returnhash_any((unsignedchar*)&key,sizeof(key));
194213
}
195214

@@ -202,6 +221,8 @@ hashfloat8extended(PG_FUNCTION_ARGS)
202221
/* Same approach as hashfloat8 */
203222
if (key== (float8)0)
204223
PG_RETURN_UINT64(seed);
224+
if (isnan(key))
225+
key=get_float8_nan();
205226

206227
returnhash_any_extended((unsignedchar*)&key,sizeof(key),seed);
207228
}

‎src/test/regress/expected/hash_func.out

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,36 @@ SELECT v as value, hash_record_extended(v, 0)::bit(32) as extended0
339339
FROM (VALUES (row(1, 'aaa')::hash_test_t2)) x(v);
340340
ERROR: could not identify an extended hash function for type money
341341
DROP TYPE hash_test_t2;
342+
--
343+
-- Check special cases for specific data types
344+
--
345+
SELECT hashfloat4('0'::float4) = hashfloat4('-0'::float4) AS t;
346+
t
347+
---
348+
t
349+
(1 row)
350+
351+
SELECT hashfloat4('NaN'::float4) = hashfloat4('-NaN'::float4) AS t;
352+
t
353+
---
354+
t
355+
(1 row)
356+
357+
SELECT hashfloat8('0'::float8) = hashfloat8('-0'::float8) AS t;
358+
t
359+
---
360+
t
361+
(1 row)
362+
363+
SELECT hashfloat8('NaN'::float8) = hashfloat8('-NaN'::float8) AS t;
364+
t
365+
---
366+
t
367+
(1 row)
368+
369+
SELECT hashfloat4('NaN'::float4) = hashfloat8('NaN'::float8) AS t;
370+
t
371+
---
372+
t
373+
(1 row)
374+

‎src/test/regress/sql/hash_func.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,12 @@ FROM (VALUES (row(1, 'aaa')::hash_test_t2)) x(v);
253253
SELECT vas value, hash_record_extended(v,0)::bit(32)as extended0
254254
FROM (VALUES (row(1,'aaa')::hash_test_t2)) x(v);
255255
DROPTYPE hash_test_t2;
256+
257+
--
258+
-- Check special cases for specific data types
259+
--
260+
SELECT hashfloat4('0'::float4)= hashfloat4('-0'::float4)AS t;
261+
SELECT hashfloat4('NaN'::float4)= hashfloat4('-NaN'::float4)AS t;
262+
SELECT hashfloat8('0'::float8)= hashfloat8('-0'::float8)AS t;
263+
SELECT hashfloat8('NaN'::float8)= hashfloat8('-NaN'::float8)AS t;
264+
SELECT hashfloat4('NaN'::float4)= hashfloat8('NaN'::float8)AS t;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp