Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5df20a6

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 60765d8 commit 5df20a6

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
@@ -3902,6 +3902,9 @@ NUM_prepare_locale(NUMProc *Np)
39023902
/* ----------
39033903
* Return pointer of last relevant number after decimal point
39043904
* 12.0500 --> last relevant is '5'
3905+
* 12.0000 --> last relevant is '.'
3906+
* If there is no decimal point, return NULL (which will result in same
3907+
* behavior as if FM hadn't been specified).
39053908
* ----------
39063909
*/
39073910
static char *
@@ -3915,7 +3918,8 @@ get_last_relevant_decnum(char *num)
39153918
#endif
39163919

39173920
if (!p)
3918-
p = num;
3921+
return NULL;
3922+
39193923
result = p;
39203924

39213925
while (*(++p))
@@ -4452,13 +4456,22 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
44524456
{
44534457
Np->num_pre = plen;
44544458

4455-
if (IS_FILLMODE(Np->Num))
4459+
if (IS_FILLMODE(Np->Num) && IS_DECIMAL(Np->Num))
44564460
{
4457-
if (IS_DECIMAL(Np->Num))
4458-
Np->last_relevant = get_last_relevant_decnum(
4459-
Np->number +
4460-
((Np->Num->zero_end - Np->num_pre > 0) ?
4461-
Np->Num->zero_end - Np->num_pre : 0));
4461+
Np->last_relevant = get_last_relevant_decnum(Np->number);
4462+
4463+
/*
4464+
* If any '0' specifiers are present, make sure we don't strip
4465+
* those digits.
4466+
*/
4467+
if (Np->last_relevant && Np->Num->zero_end > Np->num_pre)
4468+
{
4469+
char *last_zero;
4470+
4471+
last_zero = Np->number + (Np->Num->zero_end - Np->num_pre);
4472+
if (Np->last_relevant < last_zero)
4473+
Np->last_relevant = last_zero;
4474+
}
44624475
}
44634476

44644477
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
@@ -1154,6 +1154,24 @@ SELECT '' AS to_char_23, to_char(val, '9.999EEEE') FROM num_data;
11541154
| -2.493e+07
11551155
(10 rows)
11561156

1157+
SELECT '' AS to_char_24, to_char('100'::numeric, 'FM999.9');
1158+
to_char_24 | to_char
1159+
------------+---------
1160+
| 100.
1161+
(1 row)
1162+
1163+
SELECT '' AS to_char_25, to_char('100'::numeric, 'FM999.');
1164+
to_char_25 | to_char
1165+
------------+---------
1166+
| 100
1167+
(1 row)
1168+
1169+
SELECT '' AS to_char_26, to_char('100'::numeric, 'FM999');
1170+
to_char_26 | to_char
1171+
------------+---------
1172+
| 100
1173+
(1 row)
1174+
11571175
-- TO_NUMBER()
11581176
--
11591177
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
@@ -764,6 +764,10 @@ SELECT '' AS to_char_21, to_char(val, '999999SG9999999999') FROM num_data;
764764
SELECT '' AS to_char_22, to_char(val, 'FM9999999999999999.999999999999999') FROM num_data;
765765
SELECT '' AS to_char_23, to_char(val, '9.999EEEE') FROM num_data;
766766

767+
SELECT '' AS to_char_24, to_char('100'::numeric, 'FM999.9');
768+
SELECT '' AS to_char_25, to_char('100'::numeric, 'FM999.');
769+
SELECT '' AS to_char_26, to_char('100'::numeric, 'FM999');
770+
767771
-- TO_NUMBER()
768772
--
769773
SELECT '' AS to_number_1, to_number('-34,338,492', '99G999G999');

0 commit comments

Comments
 (0)