mirror of
https://github.com/postgres/postgres.git
synced 2026-06-13 02:30:19 -04:00
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.
Author: Ewan Young <kdbase.hack@gmail.com>
Discussion: https://www.postgresql.org/message-id/CAON2xHPYeRRCEVAv8XfE18KsEsEHCiYcJ5fOsoxFuMEfpxF1=g@mail.gmail.com
Backpatch-through: 14
This commit is contained in:
parent
eb4e7224a1
commit
0e1f1ed157
3 changed files with 55 additions and 3 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue