]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
seg: Fix seg_out() to preserve the upper boundary's certainty indicator
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 11 Jun 2026 09:33:48 +0000 (12:33 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 11 Jun 2026 09:34:42 +0000 (12:34 +0300)
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.

Author: Ewan Young <kdbase.hack@gmail.com>
Discussion: https://www.postgresql.org/message-id/CAON2xHPYeRRCEVAv8XfE18KsEsEHCiYcJ5fOsoxFuMEfpxF1=g@mail.gmail.com
Backpatch-through: 14

contrib/seg/expected/seg.out
contrib/seg/seg.c
contrib/seg/sql/seg.sql

index 2320464dd4712555d6971355e9a3d7e264e4cc0a..9dbcf35e071619d5dd75c236332324d44711fd05 100644 (file)
@@ -263,7 +263,8 @@ SELECT '12.345678901234560000000000000000000000000000000000000000000000000000000
  12.3457
 (1 row)
 
--- Numbers with certainty indicators
+-- Numbers and ranges with certainty indicators.  Certainty indicators
+-- are stored and preserved on output, but ignored by operators.
 SELECT '~6.5'::seg AS seg;
  seg  
 ------
@@ -300,6 +301,48 @@ SELECT '> 6.5'::seg AS seg;
  >6.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)
+
+SELECT '1.5 .. >2.5'::seg AS seg;
+     seg     
+-------------
+ 1.5 .. >2.5
+(1 row)
+
 -- Open intervals
 SELECT '0..'::seg AS seg;
  seg  
index 91b8a796004773d9ed98af68219a8c7e3b7ff260..1f665051abfe3de7394b6ebd295f80347f98ee1e 100644 (file)
@@ -147,7 +147,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);
                }
index a027d4de97ed7613c3935c56ba9f5dae70f41b79..0081854c01697fed9166d96e7cb268c76c913679 100644 (file)
@@ -63,7 +63,8 @@ SELECT '12.34567890123456'::seg AS seg;
 -- Same, with a very long input
 SELECT '12.3456789012345600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'::seg AS seg;
 
--- Numbers with certainty indicators
+-- Numbers and ranges with certainty indicators.  Certainty indicators
+-- are stored and preserved on output, but ignored by operators.
 SELECT '~6.5'::seg AS seg;
 SELECT '<6.5'::seg AS seg;
 SELECT '>6.5'::seg AS seg;
@@ -71,6 +72,14 @@ SELECT '~ 6.5'::seg AS seg;
 SELECT '< 6.5'::seg AS seg;
 SELECT '> 6.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;
+SELECT '1.5 .. >2.5'::seg AS seg;
+
 -- Open intervals
 SELECT '0..'::seg AS seg;
 SELECT '0...'::seg AS seg;