diff options
| author | Tom Lane <tgl@sss.pgh.pa.us> | 2011-09-07 17:06:26 -0400 | 
|---|---|---|
| committer | Tom Lane <tgl@sss.pgh.pa.us> | 2011-09-07 17:06:26 -0400 | 
| commit | 6e7a3c364bf5df266bb7000ead399e779410962c (patch) | |
| tree | 7d8587c8798bebccfc9c4844e45daa2597e4398b /src | |
| parent | c3106a340f3177105b80a0a6e878eba1986ae9fe (diff) | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/backend/utils/adt/formatting.c | 27 | ||||
| -rw-r--r-- | src/test/regress/expected/numeric.out | 18 | ||||
| -rw-r--r-- | src/test/regress/sql/numeric.sql | 4 | 
3 files changed, 42 insertions, 7 deletions
| diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 08ddab214d3..d86e1140ce7 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -3673,6 +3673,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 * @@ -3686,7 +3689,8 @@ get_last_relevant_decnum(char *num)  #endif  	if (!p) -		p = num; +		return NULL; +  	result = p;  	while (*(++p)) @@ -4223,13 +4227,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) diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out index 857e1d83198..0803e7bae62 100644 --- a/src/test/regress/expected/numeric.out +++ b/src/test/regress/expected/numeric.out @@ -1154,6 +1154,24 @@ SELECT '' AS to_char_23, to_char(val, '9.999EEEE')				FROM num_data;              | -2.493e+07  (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'); diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql index 8814bba486b..f33a408f140 100644 --- a/src/test/regress/sql/numeric.sql +++ b/src/test/regress/sql/numeric.sql @@ -764,6 +764,10 @@ 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_23, to_char(val, '9.999EEEE')				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'); | 
