Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1weAjc-004cDB-0W for pgsql-bugs@arkaria.postgresql.org; Mon, 29 Jun 2026 12:05:08 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1weAjb-0035cA-0O for pgsql-bugs@arkaria.postgresql.org; Mon, 29 Jun 2026 12:05:07 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1weAja-0035bx-2i for pgsql-bugs@lists.postgresql.org; Mon, 29 Jun 2026 12:05:06 +0000 Received: from mail-ed1-x536.google.com ([2a00:1450:4864:20::536]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1weAjY-00000000mb0-2Ora for pgsql-bugs@lists.postgresql.org; Mon, 29 Jun 2026 12:05:06 +0000 Received: by mail-ed1-x536.google.com with SMTP id 4fb4d7f45d1cf-6983f8b0aebso2130145a12.1 for ; Mon, 29 Jun 2026 05:05:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1782734703; cv=none; d=google.com; s=arc-20260327; b=QH0RAMt7109P5vAW9FoRp8e2mpZz3n/mS148yaLz7B2KJ864DlMPL0xNRjSQsHkk/+ OEWNA4QQbjlEMuAJqRVVN+bTQ+tXAxGeyX3d6g0iPnGWfFN+KGUkFC6cBlT9K57yzdbM fAFQ8uEnAsrgOvBvoahGJzBDN+hWU02p9dBYnKVUieZGMzEchKeigmLGYh3stWlenCZ4 C09QBiKhxUh7KkTU57kla+IpgZeXw+IcU6edLNKq93rKpYGSDn2+fK4J6kkelkzNMNJg SSgH02Jx9B/B1MQRYSxduyMtCcsqH31jUebB5kSolKSnfUGLXUiAEo69MuEJbY8yfoAV BjnA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=Vl4J02uA8Zhs9RfPwmMaSR88dHsHORx7r6BICyfuw6g=; fh=aMSUl7sca0qa6pQgpHf7FRLcZDXkNksZJI0ve3Qf8kA=; b=VyK/CrioU2lb6QX++5TvoULwZT5C9IWKq+I/BlNkJpxtLxXWQKyXo59iL9uYWItOzH uVSqm0q9uaDJJ68K8UG4aVfF30QNER+qJYcgUUDRMPlBTTgP7NCLkTgflqBCR6nylgq1 3+l49Y2HJb0+5mqDYbsEA8fbp+REchtVwR5G1zOM5MBbE/AN0f3614jEOU9e85Tu9eIJ HF/Yg2VHkyHGRtT1R844XNOHPQxn3QLVR/q1sXBcvInRKYokFO8x/SbEnolmk/TRrlE0 R1EXdqrHReuFn6QCangAoWB/lqintFd0nhtzzdeqGBvU47/nYkZ/SijARw0414FThG7X mNsw==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1782734703; x=1783339503; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=Vl4J02uA8Zhs9RfPwmMaSR88dHsHORx7r6BICyfuw6g=; b=ZeEhotUV9bCVv+aH0S5gH3tAh0Wmg07/WZUBMfPnCx4wmKixT631TTz5TGWBOVdVrO Ro0uUyFTj9/3yiwA1LGs4O4MDt8JGnjx56r8uu+Gsjc+U8/uqCO/nBkxwXUZCSqRFdFU nseLifsKTa+256jGd6KwtnkFs7Sp/DdtvaOKT/NEsTz2A7/AzLpc5H5tW2/IZ1LbZz9t 8FR0wUBqTyY1ZDftM0KbSwCDTHmGK9kBdYpnqbui04bH81Vz6WUHLV8MewN0JWOZzXOn IrdlbodJHE60vx9KOGpLx4K/mlOEOgmCNaUi2Uyf9jV8NODZ42NMichcUMp4VTuVKvP5 jndg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782734703; x=1783339503; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=Vl4J02uA8Zhs9RfPwmMaSR88dHsHORx7r6BICyfuw6g=; b=bwQwranz8N8PFVWWBNxbdMCIGxuXgOM4AZP5wocWACmUBv0SrMGzC0EYbnoWvCFLWw vOQFEH2T9hkr09xYnwREHHdiV2LSNmByEmBOhHHZgp2DxF1IXDQMggYjxBCGqasVBlEq nEsJQFSeXbzG/zqIqMGr/gUCYwkIYWvEfEvWq6bmVt7Q5tMcEhCHb/RQKJakeq1/aXxb fy7N3mDGo8MUtu8quapUipHZJWdfc9wzJYR9L1bwnoZgjQAyLqBv/gym00hmyGgMfxQb 27iVxUnHkE8q4IPRGyjch4u14IvH5aLnFXCbTphPM9CrEQ7iitlUrJIA2K7HZfzDxWjO 5FUg== X-Gm-Message-State: AOJu0YxYRLVrflEgcoMl5kVr9bygKJPyKVrpiBNh3rB99lbca/BrZVkH BmB3uw+iSeO4cmS9gepEUBEdCmkiNz4q4c5XaePtgOv2oreaeKItAq8EQEhAToDVwzyIWqEmxle pawvoGt388qFEznEExYq5ir7EGhLeEupz2OMq X-Gm-Gg: AfdE7clsgVQhd66orfqCOd5EC4I/GpaxeroUbFjG36ifly8IAaHculKgaVuNtdxTCrN Z2B4qIxZ5BM145YN2pMS2nAnUuarkD5gDSCov1qpMbB284Zcrsdu0RqNCnUyjdgh5kr7Eq36txc 7pqedjWCZt/1aw4uqG9jL4bHaMmt9JJ0Omqd1ZJt+jIA+EDamv3lAWb4LnQvgdLv3bBlEN0Pg4/ If4krjjAnb9uo+KYalKy/zS2Geso5IQr0qmhfYDSFIEM4hlybsgLeevIMBaJiZEPXj6wiu8eTfR ak0tFc4OVXN7yNO4e1p+hN8tVBMAeA== X-Received: by 2002:a05:6402:5658:b0:695:4916:b97c with SMTP id 4fb4d7f45d1cf-697d42db4bamr8135284a12.8.1782734703377; Mon, 29 Jun 2026 05:05:03 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Bill Kim Date: Mon, 29 Jun 2026 21:04:52 +0900 X-Gm-Features: AVVi8CdG77BGOSUAkR6PBt2OOHWmIH7GhkZV8_hC5VxI6vIpwK2s-bArs4J3gFE Message-ID: Subject: Re: BUG #19524: NaN handling in btree_gist's float4/float8 opclasses To: Daniel Gustafsson Cc: pgsql-bugs@lists.postgresql.org Content-Type: multipart/alternative; boundary="000000000000b940db065563441f" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000b940db065563441f Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi, contrib/btree_gist's float4/float8 GiST opclasses compared keys with raw C operators (=3D=3D, <, >). Under IEEE 754 every comparison involving NaN returns false, so GiST disagreed with the regular btree opclass, which uses float[4|8]_cmp_internal() (all NaNs equal, NaN sorts after every non-NaN value). The disagreement was reachable from SQL: * an index scan on a GiST index over a float column returned no rows for `WHERE a =3D 'NaN'`, while a sequential scan returned the NaN row; * `EXCLUDE USING gist (a WITH =3D)` accepted two rows whose key was NaN, because gbt_float8eq(NaN, NaN) was false; * an RLS predicate `USING (a !=3D 'NaN')` leaked NaN rows on index scan. The fix swaps the five gbt_float{4,8}{gt,ge,eq,le,lt} comparators and the picksplit gbt_float{4,8}key_cmp for the NaN-aware float{4,8}_{gt,ge,eq,le,lt} / float{4,8}_cmp_internal() helpers in utils/float.h =E2=80=94 same total order the btree opclass already uses. = No on-disk format change. Added regression coverage in contrib/btree_gist/sql/float{4,8}.sql for the three scenarios above. `make check` is green: core 245/245 + contrib btree_gist 32/32 + all 48 contrib modules. Originally reported as BUG #19501 and BUG #19524: https://www.postgresql.org/message-id/19501-3bff3bbc97f1e7c9%40postgresql.o= rg https://www.postgresql.org/message-id/19524-9559d302c8455664%40postgresql.o= rg I used an LLM (Claude Code) to help with the analysis and to draft the patch. I reviewed and tested the diff myself before sending. Regards, Bill Kim 2026=EB=85=84 6=EC=9B=94 29=EC=9D=BC (=EC=9B=94) =EC=98=A4=EC=A0=84 3:54, D= aniel Gustafsson =EB=8B=98=EC=9D=B4 =EC=9E=91=EC=84=B1: > > On 28 Jun 2026, at 04:14, Bill Kim wrote: > > > > Hi all, > > > > I have a patch for this and `make check` passes (core + all contrib, > plus > > new btree_gist NaN cases covering the three scenarios in the report). > > Posting here first in case someone is already further along =E2=80=94= if not, > > I'll send it to pgsql-hackers and register it in the open CF in a > couple > > of days. > > Even if someone else is working on it, always feel free to send your patc= h. > Multiple implementations of a fix is not a problem but an opportunity for > everyone to learn from each other. > > -- > Daniel Gustafsson > > --000000000000b940db065563441f Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
=C2=A0 Hi,

=C2=A0 contrib/btree_gist's float4/f= loat8 GiST opclasses compared keys with
=C2=A0 raw C operators (=3D=3D, = <, >). Under IEEE 754 every comparison involving
=C2=A0 NaN return= s false, so GiST disagreed with the regular btree opclass,
=C2=A0 which = uses float[4|8]_cmp_internal() (all NaNs equal, NaN sorts after
=C2=A0 e= very non-NaN value).

=C2=A0 The disagreement was reachable from SQL:=

=C2=A0 * an index scan on a GiST index over a float column returned= no rows
=C2=A0 =C2=A0 for `WHERE a =3D 'NaN'`, while a sequenti= al scan returned the NaN row;
=C2=A0 * `EXCLUDE USING gist (a WITH =3D)`= accepted two rows whose key was NaN,
=C2=A0 =C2=A0 because gbt_float8eq= (NaN, NaN) was false;
=C2=A0 * an RLS predicate `USING (a !=3D 'NaN&= #39;)` leaked NaN rows on index scan.

=C2=A0 The fix swaps the five = gbt_float{4,8}{gt,ge,eq,le,lt} comparators and
=C2=A0 the picksplit gbt_= float{4,8}key_cmp for the NaN-aware
=C2=A0 float{4,8}_{gt,ge,eq,le,lt} /= float{4,8}_cmp_internal() helpers in
=C2=A0 utils/float.h =E2=80=94 sam= e total order the btree opclass already uses. No
=C2=A0 on-disk format c= hange.

=C2=A0 Added regression coverage in contrib/btree_gist/sql/fl= oat{4,8}.sql for
=C2=A0 the three scenarios above. `make check` is green= : core 245/245 +
=C2=A0 contrib btree_gist 32/32 + all 48 contrib module= s.
=C2=A0 Originally reported as BUG #19501 and BUG #19524:
=C2=A0 https://www.postgresql.org/message-id/19501-3bff3bbc97f1e7c9%4= 0postgresql.org
=C2=A0 https://www.postgresql.org/mes= sage-id/19524-9559d302c8455664%40postgresql.org

=C2=A0 I used an= LLM (Claude Code) to help with the analysis and to draft the
=C2=A0 pat= ch. I reviewed and tested the diff myself before sending.

=C2=A0 Reg= ards,
=C2=A0 Bill Kim

2026=EB=85=84 6=EC=9B=94 29= =EC=9D=BC (=EC=9B=94) =EC=98=A4=EC=A0=84 3:54, Daniel Gustafsson <daniel@yesql.se>=EB=8B=98=EC=9D=B4 =EC= =9E=91=EC=84=B1:
> On 28 Jun 2026, at 04:14, Bill Kim <billkimjh@gmail.com> wrote:
>
>=C2=A0 Hi all,
>
>=C2=A0 =C2=A0I have a patch for this and `make check` passes (core + al= l contrib, plus
>=C2=A0 =C2=A0new btree_gist NaN cases covering the three scenarios in t= he report).
>=C2=A0 =C2=A0Posting here first in case someone is already further alon= g =E2=80=94 if not,
>=C2=A0 =C2=A0I'll send it to pgsql-hackers and register it in the o= pen CF in a couple
>=C2=A0 =C2=A0of days.

Even if someone else is working on it, always feel free to send your patch.=
Multiple implementations of a fix is not a problem but an opportunity for everyone to learn from each other.

--
Daniel Gustafsson

--000000000000b940db065563441f--