[PATCH] Use ssup_datum_*_cmp for int2, oid, and oid8 sort support
Hi,
While auditing nbtcompare.c, I noticed that int2, oid, and oid8
sortsupport functions use custom local fastcmp helpers that are
functionally equivalent to the existing ssup_datum_int32_cmp (for
int2) and ssup_datum_unsigned_cmp (for oid, oid8).
This prevents these types from hitting the radix sort fast path
added in commit ef3c3cf6d02 [1]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=ef3c3cf6d02, which dispatches based on the
comparator function pointer.
The original 2021-2022 thread that introduced the ssup_datum_*_cmp
helpers (commit 6974924347c, Apr 2022) [2]/messages/by-id/CA+hUKGJ2-eaDqAum5bxhpMNhvuJmRDZxB_Tow0n-gse+HG0Yig@mail.gmail.com covered int4, int8,
timestamp, date, and the abbreviated-key types (text, uuid, macaddr,
inet, bytea). int2 and oid weren't called out in that discussion,
and oid8 didn't exist at the time, it was added in Jan 2026 [3]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=b139bd3b6
and inherited the custom fastcmp pattern from oid, about a month
before radix sort landed. This patch fills those gaps.
Switching to the existing helpers makes these types eligible for
radix sort. Benchmark (10M-row single-key sort):
Type Before After Speedup
---- ------ ----- -------
int2 2440 ms 1778 ms ~27%
oid 2875 ms 2073 ms ~28%
oid8 2837 ms 2042 ms ~28%
int4 -- 1765 ms (baseline)
int8 -- 2031 ms (baseline)
The patch just replaces the comparator assignment and removes the
now-unused local fastcmp functions. No behavioral change and the
helpers produce identical results.
int2 uses ssup_datum_int32_cmp because there is no int16-specific
helper, every int16 fits losslessly in int32, and int32_cmp is more
efficient than signed_cmp (4-byte radix passes instead of 8).
Other custom fastcmp users in core (float4/float8, varlena types)
cannot be trivially switched due to NaN handling or locale-dependent
comparison, so they are left as-is.
Tested with make check (245/245 pass) and make isolation/check
(128/128 pass).
[1]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=ef3c3cf6d02
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=ef3c3cf6d02
[2]: /messages/by-id/CA+hUKGJ2-eaDqAum5bxhpMNhvuJmRDZxB_Tow0n-gse+HG0Yig@mail.gmail.com
/messages/by-id/CA+hUKGJ2-eaDqAum5bxhpMNhvuJmRDZxB_Tow0n-gse+HG0Yig@mail.gmail.com
[3]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=b139bd3b6
Thanks,
Baji Shaik
Attachments:
0001-Use-ssup_datum_-_cmp-for-int2-oid-and-oid8-sort-supp.patchapplication/octet-stream; name=0001-Use-ssup_datum_-_cmp-for-int2-oid-and-oid8-sort-supp.patchDownload+3-41
On Wed, Jun 03, 2026 at 06:37:09PM -0500, Baji Shaik wrote:
int2 uses ssup_datum_int32_cmp because there is no int16-specific
helper, every int16 fits losslessly in int32, and int32_cmp is more
efficient than signed_cmp (4-byte radix passes instead of 8).
That's nice for such a simple change. That seems correct to me.
Could you add that to the next commit fest please at [1]https://commitfest.postgresql.org/59/ -- Michael?
Other custom fastcmp users in core (float4/float8, varlena types)
cannot be trivially switched due to NaN handling or locale-dependent
comparison, so they are left as-is.
Nope, we cannot do that.
[1]: https://commitfest.postgresql.org/59/ -- Michael
--
Michael
On Thu, Jun 4, 2026 at 10:45 PM Michael Paquier <michael@paquier.xyz> wrote:
That's nice for such a simple change. That seems correct to me.
Could you add that to the next commit fest please at [1]?
Thanks for the review.
Added to the commitfest: https://commitfest.postgresql.org/patch/6851/
On Thu, Jun 4, 2026 at 6:37 AM Baji Shaik <baji.pgdev@gmail.com> wrote:
The patch just replaces the comparator assignment and removes the
now-unused local fastcmp functions. No behavioral change and the
helpers produce identical results.
Pushed, thanks for the patch!
--
John Naylor
Amazon Web Services
Hello
I think this commit caused a regression with OIDs >= 2**31:
CREATE TABLE t (o oid);
INSERT INTO t VALUES ('2147483648');
SELECT o FROM t UNION ALL SELECT '3000000000'::oid ORDER BY 1;
On Thu, Jul 23, 2026 at 12:26 PM Zsolt Parragi
<zsolt.parragi@percona.com> wrote:
I think this commit caused a regression with OIDs >= 2**31:
CREATE TABLE t (o oid);
INSERT INTO t VALUES ('2147483648');
SELECT o FROM t UNION ALL SELECT '3000000000'::oid ORDER BY 1;
Thanks for reporting! Here's the problem: An oid from a heap tuple
goes through fetch_att(), which uses Int32GetDatum for all 4-byte
byval types, which sign-extends. An oid produced by oidin() goes
through ObjectIdGetDatum, which zero-extends. That didn't matter for
btoidfastcmp(), since it compared via DatumGetObjectId(x).
I think the easiest fix is to revert the oid part of commit 51cd5d6f0,
leaving behind the int2 and oid8 parts. The asymmetry between the 2
oid types would look odd, though, so that would require an explanatory
comment.
--
John Naylor
Amazon Web Services
I think the easiest fix is to revert the oid part of commit 51cd5d6f0,
leaving behind the int2 and oid8 parts. The asymmetry between the 2
oid types would look odd, though, so that would require an explanatory
comment.
Yes, that was my conclusion too, but I wanted to leave the decision up to you and Baji Shaik.
On Thu, Jul 23, 2026 at 3:15 PM Zsolt Parragi <zsolt.parragi@percona.com> wrote:
I think the easiest fix is to revert the oid part of commit 51cd5d6f0,
leaving behind the int2 and oid8 parts. The asymmetry between the 2
oid types would look odd, though, so that would require an explanatory
comment.Yes, that was my conclusion too, but I wanted to leave the decision up
to you and Baji Shaik.
The attached is what I had in mind (including a regression test to
keep from tempting anyone else), with maybe some minor adjustments in
the comments.
--
John Naylor
Amazon Web Services
Attachments:
v1-0001-Revert-using-ssup_datum_unsigned_cmp-for-comparin.patchtext/x-patch; charset=US-ASCII; name=v1-0001-Revert-using-ssup_datum_unsigned_cmp-for-comparin.patchDownload+35-2
LGTM.
+ * We cannot use ssup_datum_unsigned_cmp here, since we cannot count on
+ * Datums being zero-extended.
One nitpick that this explains the why, but it doesn't mention the difference with oid8.
On Fri, Jul 24, 2026 at 7:04 PM Zsolt Parragi <zsolt.parragi@percona.com> wrote:
LGTM.
+ * We cannot use ssup_datum_unsigned_cmp here, since we cannot count on + * Datums being zero-extended.One nitpick that this explains the why, but it doesn't mention the
difference with oid8.
Do you mean prefacing the above with "Unlike the oid8 case, ..." ?
--
John Naylor
Amazon Web Services
On Fri, 24 Jul 2026 at 06:34, John Naylor <johncnaylorls@gmail.com> wrote:
On Thu, Jul 23, 2026 at 12:26 PM Zsolt Parragi
<zsolt.parragi@percona.com> wrote:I think this commit caused a regression with OIDs >= 2**31:
CREATE TABLE t (o oid);
INSERT INTO t VALUES ('2147483648');
SELECT o FROM t UNION ALL SELECT '3000000000'::oid ORDER BY 1;Thanks for reporting! Here's the problem: An oid from a heap tuple
goes through fetch_att(), which uses Int32GetDatum for all 4-byte
byval types, which sign-extends. An oid produced by oidin() goes
through ObjectIdGetDatum, which zero-extends. That didn't matter for
btoidfastcmp(), since it compared via DatumGetObjectId(x).I think the easiest fix is to revert the oid part of commit 51cd5d6f0,
leaving behind the int2 and oid8 parts. The asymmetry between the 2
oid types would look odd, though, so that would require an explanatory
comment.
I didn't see it mentioned, but just for the archives' sake, did you
rule out adding a dedicated uint32 comparator function?
Or is there some other reason this can't be done due to the radix sort code?
David
On Tue, Aug 4, 2026 at 7:09 PM David Rowley <dgrowleyml@gmail.com> wrote:
On Fri, 24 Jul 2026 at 06:34, John Naylor <johncnaylorls@gmail.com> wrote:
I think the easiest fix is to revert the oid part of commit 51cd5d6f0,
leaving behind the int2 and oid8 parts. The asymmetry between the 2
oid types would look odd, though, so that would require an explanatory
comment.I didn't see it mentioned, but just for the archives' sake, did you
rule out adding a dedicated uint32 comparator function?Or is there some other reason this can't be done due to the radix sort code?
To be honest, I hadn't put much thought into it, but it seems like a
good invariant to keep that all integer types with normal comparison
semantics are eligible for radix sort. v2 goes in this direction, and
I've run the same tests used when developing radix sort.
On Fri, Jul 24, 2026 at 7:04 PM Zsolt Parragi <zsolt.parragi@percona.com> wrote:
+ * We cannot use ssup_datum_unsigned_cmp here, since we cannot count on + * Datums being zero-extended.One nitpick that this explains the why, but it doesn't mention the
difference with oid8.
How about:
+ /*
+ * We cannot use ssup_datum_unsigned_cmp here, since the upper half of a
+ * Datum containing a 32-bit type is not reliably zero-extended.
+ */
...by mentioning 32-bit the difference from oid8 should be obvious, I hope.
--
John Naylor
Amazon Web Services
Attachments:
v2-0001-Add-ssup_datum_uint32_cmp-for-comparing-oids.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Add-ssup_datum_uint32_cmp-for-comparing-oids.patchDownload+50-13
On Thu, 6 Aug 2026 at 19:55, John Naylor <johncnaylorls@gmail.com> wrote:
On Tue, Aug 4, 2026 at 7:09 PM David Rowley <dgrowleyml@gmail.com> wrote:
On Fri, 24 Jul 2026 at 06:34, John Naylor <johncnaylorls@gmail.com> wrote:
I think the easiest fix is to revert the oid part of commit 51cd5d6f0,
leaving behind the int2 and oid8 parts. The asymmetry between the 2
oid types would look odd, though, so that would require an explanatory
comment.I didn't see it mentioned, but just for the archives' sake, did you
rule out adding a dedicated uint32 comparator function?Or is there some other reason this can't be done due to the radix sort code?
To be honest, I hadn't put much thought into it, but it seems like a
good invariant to keep that all integer types with normal comparison
semantics are eligible for radix sort. v2 goes in this direction, and
I've run the same tests used when developing radix sort.
The patch looks how I thought it would.
On Fri, Jul 24, 2026 at 7:04 PM Zsolt Parragi <zsolt.parragi@percona.com> wrote:
+ * We cannot use ssup_datum_unsigned_cmp here, since we cannot count on + * Datums being zero-extended.One nitpick that this explains the why, but it doesn't mention the
difference with oid8.How about:
+ /* + * We cannot use ssup_datum_unsigned_cmp here, since the upper half of a + * Datum containing a 32-bit type is not reliably zero-extended. + */...by mentioning 32-bit the difference from oid8 should be obvious, I hope.
Is it ever necessary to have that as a comment? Maybe it'd be better
to rename ssup_datum_unsigned_cmp to ssup_datum_uint64_cmp. It just
doesn't seem questionable why you'd use the 64-bit version for a
32-bit type with those names.
David
On Thu, Aug 6, 2026 at 3:29 PM David Rowley <dgrowleyml@gmail.com> wrote:
On Fri, Jul 24, 2026 at 7:04 PM Zsolt Parragi <zsolt.parragi@percona.com> wrote:
+ /* + * We cannot use ssup_datum_unsigned_cmp here, since the upper half of a + * Datum containing a 32-bit type is not reliably zero-extended. + */...by mentioning 32-bit the difference from oid8 should be obvious, I hope.
Is it ever necessary to have that as a comment? Maybe it'd be better
to rename ssup_datum_unsigned_cmp to ssup_datum_uint64_cmp. It just
doesn't seem questionable why you'd use the 64-bit version for a
32-bit type with those names.
Yeah, self-documenting code is best. I'll plan on pushing both the
rename and v2 early next week. Thanks for looking!
--
John Naylor
Amazon Web Services
On Thu, Aug 6, 2026 at 5:46 PM John Naylor <johncnaylorls@gmail.com> wrote:
Yeah, self-documenting code is best. I'll plan on pushing both the
rename and v2 early next week. Thanks for looking!
This is done.
--
John Naylor
Amazon Web Services