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.
This commit is contained in:
Tom Lane 2011-09-07 17:06:44 -04:00
parent 80360976e6
commit eb9a98b6e2
3 changed files with 42 additions and 7 deletions

View File

@ -3891,6 +3891,9 @@ NUM_prepare_locale(NUMProc *Np)
/* ----------
* Return pointer of last relevant number after decimal point
* 12.0500 --> last relevant is '5'
* 12.0000 --> last relevant is '.'
* If there is no decimal point, return NULL (which will result in same
* behavior as if FM hadn't been specified).
* ----------
*/
static char *
@ -3904,7 +3907,8 @@ get_last_relevant_decnum(char *num)
#endif
if (!p)
p = num;
return NULL;
result = p;
while (*(++p))
@ -4432,13 +4436,22 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
{
Np->num_pre = plen;
if (IS_FILLMODE(Np->Num))
if (IS_FILLMODE(Np->Num) && IS_DECIMAL(Np->Num))
{
if (IS_DECIMAL(Np->Num))
Np->last_relevant = get_last_relevant_decnum(
Np->number +
((Np->Num->zero_end - Np->num_pre > 0) ?
Np->Num->zero_end - Np->num_pre : 0));
Np->last_relevant = get_last_relevant_decnum(Np->number);
/*
* If any '0' specifiers are present, make sure we don't strip
* those digits.
*/
if (Np->last_relevant && Np->Num->zero_end > Np->num_pre)
{
char *last_zero;
last_zero = Np->number + (Np->Num->zero_end - Np->num_pre);
if (Np->last_relevant < last_zero)
Np->last_relevant = last_zero;
}
}
if (Np->sign_wrote == FALSE && Np->num_pre == 0)

View File

@ -1117,6 +1117,24 @@ SELECT '' AS to_char_22, to_char(val, 'FM9999999999999999.999999999999999') FROM
| -24926804.04504742
(10 rows)
SELECT '' AS to_char_24, to_char('100'::numeric, 'FM999.9');
to_char_24 | to_char
------------+---------
| 100.
(1 row)
SELECT '' AS to_char_25, to_char('100'::numeric, 'FM999.');
to_char_25 | to_char
------------+---------
| 100
(1 row)
SELECT '' AS to_char_26, to_char('100'::numeric, 'FM999');
to_char_26 | to_char
------------+---------
| 100
(1 row)
-- TO_NUMBER()
--
SELECT '' AS to_number_1, to_number('-34,338,492', '99G999G999');

View File

@ -746,6 +746,10 @@ SELECT '' AS to_char_20, to_char(val, E'99999 "text" 9999 "9999" 999 "\\"text be
SELECT '' AS to_char_21, to_char(val, '999999SG9999999999') FROM num_data;
SELECT '' AS to_char_22, to_char(val, 'FM9999999999999999.999999999999999') FROM num_data;
SELECT '' AS to_char_24, to_char('100'::numeric, 'FM999.9');
SELECT '' AS to_char_25, to_char('100'::numeric, 'FM999.');
SELECT '' AS to_char_26, to_char('100'::numeric, 'FM999');
-- TO_NUMBER()
--
SELECT '' AS to_number_1, to_number('-34,338,492', '99G999G999');