]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix corner case bug in numeric to_char().
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 7 Sep 2011 21:06:39 +0000 (17:06 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 7 Sep 2011 21:06:39 +0000 (17:06 -0400)
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.

src/backend/utils/adt/formatting.c
src/test/regress/expected/numeric.out
src/test/regress/sql/numeric.sql

index 9c4441985aed308c6aa3d7fd0a45b8dcce2591b7..875b9bd3b0b8e151413588266f5fc90a8247d73f 100644 (file)
@@ -3961,6 +3961,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 *
@@ -3974,7 +3977,8 @@ get_last_relevant_decnum(char *num)
 #endif
 
        if (!p)
-               p = num;
+               return NULL;
+
        result = p;
 
        while (*(++p))
@@ -4502,13 +4506,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)
index 7840990bf7dfc0e2dec02fb6e6f9334a7bad7c69..82d207a940f0ce919fec12f0709d1745f0bfb70a 100644 (file)
@@ -1139,6 +1139,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');
index dc1452f9ef0a7c08770945742a0d890c4c061500..549d655329d1d5025315cb82fc77bd4ed160e476 100644 (file)
@@ -763,6 +763,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');