0001-seg-Fix-seg_out-to-preserve-the-upper-boundary-s-cer.patch
application/octet-stream
Filename: 0001-seg-Fix-seg_out-to-preserve-the-upper-boundary-s-cer.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch 0001
Subject: seg: Fix seg_out() to preserve the upper boundary's certainty indicator
| File | + | − |
|---|---|---|
| contrib/seg/expected/seg.out | 45 | 0 |
| contrib/seg/seg.c | 1 | 1 |
| contrib/seg/sql/seg.sql | 11 | 0 |
From 4e401b76c7c0f3f902b7a05d8bfaab8ffa700b0c Mon Sep 17 00:00:00 2001
From: Ewan Young <kdbase.hack@gmail.com>
Date: Thu, 11 Jun 2026 22:32:09 +0800
Subject: [PATCH] seg: Fix seg_out() to preserve the upper boundary's certainty
indicator
When printing the upper boundary of a seg interval, seg_out() decided
whether to emit the certainty indicator ('<', '>' or '~') by testing the
upper indicator (u_ext) for '<' and '>', but mistakenly tested the lower
indicator (l_ext) for '~'. This is a copy-and-paste slip from the
symmetric code that prints the lower boundary a few lines above.
The consequences for valid input were:
* A '~' on the upper boundary was dropped on output, e.g.
'1.5 .. ~2.5'::seg printed as '1.5 .. 2.5'.
* When the lower boundary carried '~' but the upper boundary had no
indicator, the wrong test matched and sprintf(p, "%c", seg->u_ext)
wrote a NUL byte (u_ext == '\0'), which truncated the result string
and silently lost the entire upper boundary, e.g.
'~6.5 .. 8.5'::seg printed as '~6.5 .. '.
Certainty indicators are documented to be preserved on output (they are
ignored by the operators, but kept as comments), so this broke the
input/output round-trip for the affected values.
The bug has existed since seg was added. It went unnoticed because the
existing regression tests only exercised certainty indicators on
single-point segs, which are printed by a different branch of seg_out().
Add tests that place indicators on both boundaries of an interval.
---
contrib/seg/expected/seg.out | 45 ++++++++++++++++++++++++++++++++++++
contrib/seg/seg.c | 2 +-
contrib/seg/sql/seg.sql | 11 +++++++++
3 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/contrib/seg/expected/seg.out b/contrib/seg/expected/seg.out
index cd21139b5a7..dd30156dfe8 100644
--- a/contrib/seg/expected/seg.out
+++ b/contrib/seg/expected/seg.out
@@ -300,6 +300,51 @@ SELECT '> 6.5'::seg AS seg;
>6.5
(1 row)
+-- Certainty indicators on interval boundaries must be preserved on output.
+-- (They are ignored by operators, but are kept as comments; in particular the
+-- indicator on the upper boundary must round-trip.)
+SELECT '~1.5 .. 2.5'::seg AS seg;
+ seg
+-------------
+ ~1.5 .. 2.5
+(1 row)
+
+SELECT '1.5 .. ~2.5'::seg AS seg;
+ seg
+-------------
+ 1.5 .. ~2.5
+(1 row)
+
+SELECT '~1.5 .. ~2.5'::seg AS seg;
+ seg
+--------------
+ ~1.5 .. ~2.5
+(1 row)
+
+SELECT '<1.5 .. 2.5'::seg AS seg;
+ seg
+-------------
+ <1.5 .. 2.5
+(1 row)
+
+SELECT '1.5 .. <2.5'::seg AS seg;
+ seg
+-------------
+ 1.5 .. <2.5
+(1 row)
+
+SELECT '>1.5 .. 2.5'::seg AS seg;
+ seg
+-------------
+ >1.5 .. 2.5
+(1 row)
+
+SELECT '1.5 .. >2.5'::seg AS seg;
+ seg
+-------------
+ 1.5 .. >2.5
+(1 row)
+
-- Open intervals
SELECT '0..'::seg AS seg;
seg
diff --git a/contrib/seg/seg.c b/contrib/seg/seg.c
index fcded0245aa..c7b374825f8 100644
--- a/contrib/seg/seg.c
+++ b/contrib/seg/seg.c
@@ -152,7 +152,7 @@ seg_out(PG_FUNCTION_ARGS)
{
/* print the upper boundary if exists */
p += sprintf(p, " ");
- if (seg->u_ext == '>' || seg->u_ext == '<' || seg->l_ext == '~')
+ if (seg->u_ext == '>' || seg->u_ext == '<' || seg->u_ext == '~')
p += sprintf(p, "%c", seg->u_ext);
p += restore(p, seg->upper, seg->u_sigd);
}
diff --git a/contrib/seg/sql/seg.sql b/contrib/seg/sql/seg.sql
index c30f1f6bef1..4fe1bb6d851 100644
--- a/contrib/seg/sql/seg.sql
+++ b/contrib/seg/sql/seg.sql
@@ -71,6 +71,17 @@ SELECT '~ 6.5'::seg AS seg;
SELECT '< 6.5'::seg AS seg;
SELECT '> 6.5'::seg AS seg;
+-- Certainty indicators on interval boundaries must be preserved on output.
+-- (They are ignored by operators, but are kept as comments; in particular the
+-- indicator on the upper boundary must round-trip.)
+SELECT '~1.5 .. 2.5'::seg AS seg;
+SELECT '1.5 .. ~2.5'::seg AS seg;
+SELECT '~1.5 .. ~2.5'::seg AS seg;
+SELECT '<1.5 .. 2.5'::seg AS seg;
+SELECT '1.5 .. <2.5'::seg AS seg;
+SELECT '>1.5 .. 2.5'::seg AS seg;
+SELECT '1.5 .. >2.5'::seg AS seg;
+
-- Open intervals
SELECT '0..'::seg AS seg;
SELECT '0...'::seg AS seg;
--
2.47.3