[PATCH] seg: preserve the upper boundary's certainty indicator in seg_out()
Ewan Young <kdbase.hack@gmail.com>
From: Ewan Young <kdbase.hack@gmail.com>
To: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Date: 2026-06-11T07:03:54Z
Lists: pgsql-hackers
Attachments
- 0001-seg-Fix-seg_out-to-preserve-the-upper-boundary-s-cer.patch (application/octet-stream) patch 0001
Hi Hackers,
While reviewing contrib/seg I noticed that seg_out() mishandles the
certainty indicator ('<', '>' or '~') on the upper boundary of an
interval, leading to wrong output and even silent data loss for valid
values.
For example, on current master:
regression=# SELECT '1.5 .. ~2.5'::seg;
seg
------------
1.5 .. 2.5 -- the '~' on the upper bound is dropped
regression=# SELECT '~6.5 .. 8.5'::seg;
seg
----------
~6.5 .. -- the upper bound 8.5 is lost entirely
The culprit is in seg_out() (contrib/seg/seg.c), where the code that
prints the upper boundary's indicator is:
if (seg->u_ext == '>' || seg->u_ext == '<' || seg->l_ext == '~')
p += sprintf(p, "%c", seg->u_ext);
The third test should examine u_ext, not l_ext -- it's a copy-and-paste
slip from the symmetric block that prints the lower boundary a few lines
above (which correctly tests l_ext three times).
This produces two distinct misbehaviours:
* A '~' on the upper boundary fails the (wrong) condition and is not
printed at all -> the indicator is dropped.
* When the lower boundary carries '~' but the upper boundary has no
indicator (u_ext == '\0'), the wrong test matches and
sprintf(p, "%c", seg->u_ext) writes a NUL byte into the output
buffer. PG_RETURN_CSTRING then stops at that NUL, truncating the
string and dropping the upper boundary value -> data loss.
Certainty indicators are documented to be preserved on output (they are
ignored by the operators, but kept as a comment), so this breaks the
input/output round-trip for the affected values.
The bug appears to date back to when seg was first added. It went
unnoticed because the existing regression tests only exercise certainty
indicators on single-point segs (e.g. '~6.5'), which are printed by a
different branch of seg_out() and never reach the buggy line.
The attached patch fixes the one-character typo and adds regression
tests that place each indicator on both boundaries of an interval, so
the upper-boundary case is now covered. make installcheck passes with
the fix and fails without it.
Regards,
Ewan Young
Commits
-
Adjust cross-version upgrade tests for seg_out() fix
- 7974f94a02a1 14 (unreleased) landed
- 8aede3600c84 15 (unreleased) landed
- d26641329346 16 (unreleased) landed
- c0d44e009406 17 (unreleased) landed
- 10e510423dc3 18 (unreleased) landed
- 3e3d7875e956 19 (unreleased) landed
-
seg: Fix seg_out() to preserve the upper boundary's certainty indicator
- 58b91fc73a86 14 (unreleased) landed
- b3aa2083a4da 15 (unreleased) landed
- 504ca05133db 16 (unreleased) landed
- bcbbd070d496 17 (unreleased) landed
- 0004cab4dc60 18 (unreleased) landed
- 0e1f1ed157e9 19 (unreleased) landed