aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-09-07 17:06:44 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2011-09-07 17:06:44 -0400
commiteb9a98b6e2657ca65c4ddbe2536b26880864ff8f (patch)
tree89955c6d1f074578a5bf540b2b3911869c2eddd3 /src
parent80360976e6d55f532d6c404bedcf06d5b10d7801 (diff)
downloadpostgresql-eb9a98b6e2657ca65c4ddbe2536b26880864ff8f.tar.gz
postgresql-eb9a98b6e2657ca65c4ddbe2536b26880864ff8f.zip
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.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 dc0cf5989eb..cfdcb4aa878 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -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)
diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out
index 96c70a8a097..b452cfa395a 100644
--- a/src/test/regress/expected/numeric.out
+++ b/src/test/regress/expected/numeric.out
@@ -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');
diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql
index b6f69b6f7d2..fb4b6f13067 100644
--- a/src/test/regress/sql/numeric.sql
+++ b/src/test/regress/sql/numeric.sql
@@ -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');