Skip to content

Commit eb9a98b

Browse files
committed
Fix corner case bug in numeric to_char().
Trailing-zero stripping applied by the FM specifier could strip zeroes to the left of the decimal point, for a format with no digit positions after the decimal point (such as "FM999."). Reported and diagnosed by Marti Raudsepp, though I didn't use his patch.
1 parent 8036097 commit eb9a98b

File tree

3 files changed

+42
-7
lines changed

3 files changed

+42
-7
lines changed

src/backend/utils/adt/formatting.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3891,6 +3891,9 @@ NUM_prepare_locale(NUMProc *Np)
38913891
/* ----------
38923892
* Return pointer of last relevant number after decimal point
38933893
* 12.0500 --> last relevant is '5'
3894+
* 12.0000 --> last relevant is '.'
3895+
* If there is no decimal point, return NULL (which will result in same
3896+
* behavior as if FM hadn't been specified).
38943897
* ----------
38953898
*/
38963899
static char *
@@ -3904,7 +3907,8 @@ get_last_relevant_decnum(char *num)
39043907
#endif
39053908

39063909
if (!p)
3907-
p = num;
3910+
return NULL;
3911+
39083912
result = p;
39093913

39103914
while (*(++p))
@@ -4432,13 +4436,22 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
44324436
{
44334437
Np->num_pre = plen;
44344438

4435-
if (IS_FILLMODE(Np->Num))
4439+
if (IS_FILLMODE(Np->Num) && IS_DECIMAL(Np->Num))
44364440
{
4437-
if (IS_DECIMAL(Np->Num))
4438-
Np->last_relevant = get_last_relevant_decnum(
4439-
Np->number +
4440-
((Np->Num->zero_end - Np->num_pre > 0) ?
4441-
Np->Num->zero_end - Np->num_pre : 0));
4441+
Np->last_relevant = get_last_relevant_decnum(Np->number);
4442+
4443+
/*
4444+
* If any '0' specifiers are present, make sure we don't strip
4445+
* those digits.
4446+
*/
4447+
if (Np->last_relevant && Np->Num->zero_end > Np->num_pre)
4448+
{
4449+
char *last_zero;
4450+
4451+
last_zero = Np->number + (Np->Num->zero_end - Np->num_pre);
4452+
if (Np->last_relevant < last_zero)
4453+
Np->last_relevant = last_zero;
4454+
}
44424455
}
44434456

44444457
if (Np->sign_wrote == FALSE && Np->num_pre == 0)

src/test/regress/expected/numeric.out

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,24 @@ SELECT '' AS to_char_22, to_char(val, 'FM9999999999999999.999999999999999') FROM
11171117
| -24926804.04504742
11181118
(10 rows)
11191119

1120+
SELECT '' AS to_char_24, to_char('100'::numeric, 'FM999.9');
1121+
to_char_24 | to_char
1122+
------------+---------
1123+
| 100.
1124+
(1 row)
1125+
1126+
SELECT '' AS to_char_25, to_char('100'::numeric, 'FM999.');
1127+
to_char_25 | to_char
1128+
------------+---------
1129+
| 100
1130+
(1 row)
1131+
1132+
SELECT '' AS to_char_26, to_char('100'::numeric, 'FM999');
1133+
to_char_26 | to_char
1134+
------------+---------
1135+
| 100
1136+
(1 row)
1137+
11201138
-- TO_NUMBER()
11211139
--
11221140
SELECT '' AS to_number_1, to_number('-34,338,492', '99G999G999');

src/test/regress/sql/numeric.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,10 @@ SELECT '' AS to_char_20, to_char(val, E'99999 "text" 9999 "9999" 999 "\\"text be
746746
SELECT '' AS to_char_21, to_char(val, '999999SG9999999999') FROM num_data;
747747
SELECT '' AS to_char_22, to_char(val, 'FM9999999999999999.999999999999999') FROM num_data;
748748

749+
SELECT '' AS to_char_24, to_char('100'::numeric, 'FM999.9');
750+
SELECT '' AS to_char_25, to_char('100'::numeric, 'FM999.');
751+
SELECT '' AS to_char_26, to_char('100'::numeric, 'FM999');
752+
749753
-- TO_NUMBER()
750754
--
751755
SELECT '' AS to_number_1, to_number('-34,338,492', '99G999G999');

0 commit comments

Comments
 (0)