Contribution · PostgreSQL · RDBMS Core (contrib)
PostgreSQL — btree_gist NaN ordering (BUG #19501 / #19524)
contrib/btree_gist의 float4/float8 opclass가 raw C ==/</> 연산자를 그대로 써서 IEEE 754 NaN에 대해 false를 돌려주는 바람에, GiST 인덱스가 regular btree opclass와 같은 total order를 따르지 않는 버그. EXCLUDE 제약 우회, RLS 누수, index scan ↔ seq scan 결과 불일치 세 시나리오가 동시에 발생. NaN-aware 헬퍼(float4_* / float8_* from utils/float.h)로 교체하는 패치 준비 완료.
문제 — BUG #19501 / #19524
contrib/btree_gist/btree_float4.c·btree_float8.c의 5개 GiST 비교 콜백(gbt_float{4,8}{gt,ge,eq,le,lt})이 raw C 연산자를 사용. IEEE 754에서 NaN과의 모든 비교는 false를 돌려주므로, GiST가 "NaN ↔ NaN", "NaN > 1.0" 같은 케이스를 잘못 판정. 같은 float에 대해 regular btree opclass는 float4_cmp_internal()(NaN 모두 동등, NaN이 모든 non-NaN 위에 정렬)을 쓰니까 두 인덱스 종류가 같은 데이터에 대해 다른 결과를 돌려주는 상태.
SQL로 재현 가능한 세 시나리오:
- Index/seq scan 불일치 — float GiST 인덱스에
WHERE v = 'NaN'::float8이 index scan에선 0 row, seq scan에선 NaN row를 정상 반환. - EXCLUDE 우회 —
EXCLUDE USING gist (a WITH =)가 NaN 중복을 허용.gbt_float8eq(NaN, NaN)이 false로 평가되어 충돌 감지가 안 됨. - RLS 누수 —
USING (a != 'NaN'::float8)정책이 index scan 경로에서만 NaN row를 노출. predicate의!=가 raw 연산자 의미로 인덱스 키에 적용되기 때문.
수정
5개의 boolean 콜백을 utils/float.h의 NaN-aware float{4,8}_{gt,ge,eq,le,lt} inline 헬퍼로 교체, picksplit 비교(gbt_float{4,8}key_cmp)도 float{4,8}_cmp_internal()로 교체. 이 헬퍼들은 regular btree opclass가 이미 쓰는 total order(NaN 모두 동등, NaN이 모든 non-NaN 위에 정렬)와 같으므로, 두 인덱스 종류의 동작이 일치하게 됨. on-disk 포맷 변경 없음 — 메모리 비교 경로만 수정.
회귀 테스트는 contrib/btree_gist/sql/float4.sql·float8.sql에 NaN 섹션을 추가: index scan ↔ seq scan 일치 확인, NaN이 finite 값 뒤로 정렬되는지 확인, EXCLUDE 제약이 NaN 중복을 정확히 거부하는지 확인. 패치 적용 후 make check 결과: core 245/245 + contrib btree_gist 32/32 + 전체 contrib 48 modules 통과.
제출 현황
- BUG #19524 thread "I have a patch" reply sent to pgsql-bugs held for moderation
- CF /59/ CommitFest registration — pending after pgsql-hackers submission pending
관련 자료
- Sourcepostgres/postgres (read-only mirror)
- Upstreamgit.postgresql.org/postgresql.git
- BUG #1BUG #19501 (Man Zeng, 2026-05-29)
- BUG #2BUG #19524 (Yuelin Wang, 2026-06-18)
- CommitFestcommitfest.postgresql.org/59/
- Patch guidewiki.postgresql.org/Submitting_a_Patch
- Authorgithub.com/BK202503
- Blogdevbilllab.tistory.com