summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-09-07 17:06:19 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2011-09-07 17:07:33 -0400
commit5df20a6e4f5eee5eef1b28760ceca604f57d535b (patch)
tree907f38f464e6a56e1941624b10f4bd97fd9cca70
parent60765d86c3a91bf2fdf796ee66672120fd599d02 (diff)
downloadpostgresql-5df20a6e4f5eee5eef1b28760ceca604f57d535b.tar.gz
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.
-rw-r--r--src/backend/utils/adt/formatting.c27
-rw-r--r--src/test/regress/expected/numeric.out18
-rw-r--r--src/test/regress/sql/numeric.sql4
3 files changed, 42 insertions, 7 deletions
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 726a1f4552..1a52429624 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -3902,6 +3902,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 *
@@ -3915,7 +3918,8 @@ get_last_relevant_decnum(char *num)
#endif
if (!p)
- p = num;
+ return NULL;
+
result = p;
while (*(++p))
@@ -4452,13 +4456,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 d9927b7915..71c520e5e7 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 a1435ec85e..b2dc46fae2 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');