Re: BUG #19584: tid input acceptance is platform-dependent: '(,5)'::tid yields (0,5) on glibc, errors on macOS - Mailing list pgsql-bugs

From Tristan Partin
Subject Re: BUG #19584: tid input acceptance is platform-dependent: '(,5)'::tid yields (0,5) on glibc, errors on macOS
Date
Msg-id DKBS5Q4H0LL4.1BVC92UHVQT1A@partin.io
Whole thread
List pgsql-bugs
On Wed Jul 29, 2026 at 7:24 AM CDT, PG Bug reporting form wrote:
> The following bug has been logged on the website:
>
> Bug reference:      19584
> Logged by:          Michael Malis
> Email address:      malis@pgrust.com
> PostgreSQL version: 19beta2
> Operating system:   Debian 13/aarch64 (glibc) and macOS 15/aarch64
> Description:
>
> The tid type accepts or rejects the same input string depending on
> the platform's C library, and on glibc platforms it converts empty
> coordinate fields to 0 rather than rejecting them.
>
> On Debian (official postgres:18 Docker image):
>
> SELECT version();
>                                                          version
>
--------------------------------------------------------------------------------------------------------------------------
>  PostgreSQL 18.4 (Debian 18.4-1.pgdg13+1) on aarch64-unknown-linux-gnu,
> compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
> (1 row)
>
> SELECT '(,5)'::tid;
>   tid
> -------
>  (0,5)
> (1 row)
>
> (likewise '(5,)' → (5,0) and '(,)' → (0,0))
>
> On macOS (Homebrew build):
>
> SELECT version();
>                                                            version
>
-----------------------------------------------------------------------------------------------------------------------------
>  PostgreSQL 18.4 (Homebrew) on aarch64-apple-darwin24.6.0, compiled by Apple
> clang version 17.0.0 (clang-1700.6.4.2), 64-bit
> (1 row)
>
> SELECT '(,5)'::tid;
> ERROR:  invalid input syntax for type tid: "(,5)"
> LINE 1: SELECT '(,5)'::tid;
>                ^
>
> Expected behavior: the same literal is either valid or invalid on
> every platform. I would expect rejection everywhere, since an empty
> block or offset field matches no documented form of the type, but
> either consistent behavior would resolve this report.
>
> Cause, from the source: tidin (src/backend/utils/adt/tid.c) parses
> each coordinate with
>
> errno = 0;
> cvt = strtoul(coord[0], &badp, 10);
> if (errno || *badp != DELIM)
>     ereturn(...);
>
> When the field is empty, strtoul performs no conversion: it returns 0
> and sets *endptr to the start of the field, so badp points at the
> delimiter and the second test passes. Rejection then depends entirely
> on errno — and setting EINVAL for "no conversion could be performed"
> is optional per POSIX ("may fail"). glibc does not set it; macOS
> libc does. tidin never tests whether any digits were consumed
> (badp == coord[0]). Notably, uint32in_subr() in numutils.c — which
> tidin's own comment cites as "similar code" — does carry exactly that
> check ("|| endptr == s"), so the deterministic form already exists
> in the tree.
>
> This same platform difference was reported and diagnosed in April
> 2004 ("invalid input syntax for type tid: '(,)'", e.g.
> 200404061432.i36EWab06319@candle.pha.pa.us), where Tom Lane noted
> "the reason for the platform dependency is probably that strtoul()
> is setting errno on some machines and not others" and that "the TID
> input parser ... should never allow this". The trigger in that
> thread (an ODBC driver emitting '(,)') was fixed on the driver side;
> the parser was not changed.
>
> Happy to provide additional cases or a patch.

Thanks for the awesome report, again! I sent a patch[0] to fix this
issue to -hackers. I also spotted some other issues in the codebase, and
sent them in the series as well.

[0]: https://www.postgresql.org/message-id/DKBS2Z9CGARC.2T07O6TJYSE8B%40partin.io

--
Tristan Partin
PostgreSQL Contributors Team
AWS (https://aws.amazon.com)



pgsql-bugs by date:

Previous
From: "Tristan Partin"
Date:
Subject: Re: BUG #19587: hashchar (internal "char" type) depends on platform char signedness
Next
From: ld_zju
Date:
Subject: Re:Re: DO NOT pull up a sublink when it has no join condition with the upper relation