]> 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:33:48 +0000 (12:33 +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 cd21139b5a7714348ad58f577fe300f83f4c449b..b7c3fba15978be24a20dfcb1a8d9f247829d214a 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 fcded0245aa2ecf803fd1c0356aa11eedf922d8c..c7b374825f884c1b3a706e1266f15b6307cd88d6 100644 (file)
@@ -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);
                }
index c30f1f6bef10d81d2cb4b08891a00eb5cd3f11d2..a74a42f7e3e50c694e64616c1b4232de6a08b436 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;