]> granicus.if.org Git - postgresql/commitdiff
Fix to_number for the case of a trailing S.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 28 Oct 2004 18:55:08 +0000 (18:55 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 28 Oct 2004 18:55:08 +0000 (18:55 +0000)
Karel Zak

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

index a982a0071afa16290e285bd1a465d13d54e95cc1..26ebeaaac2373c4172b4b593f851544cd917d652 100644 (file)
@@ -1,7 +1,7 @@
 /* -----------------------------------------------------------------------
  * formatting.c
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.79 2004/10/13 01:25:11 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.80 2004/10/28 18:55:06 tgl Exp $
  *
  *
  *      Portions Copyright (c) 1999-2004, PostgreSQL Global Development Group
@@ -853,7 +853,8 @@ typedef struct NUMProc
                                num_pre,                /* space before first number    */
 
                                read_dec,               /* to_number - was read dec. point      */
-                               read_post;              /* to_number - number of dec. digit */
+                               read_post,              /* to_number - number of dec. digit */
+                               read_pre;               /* to_number - number non-dec. digit */
 
        char       *number,                     /* string with number   */
                           *number_p,           /* pointer to current number position */
@@ -3623,15 +3624,18 @@ get_last_relevant_decnum(char *num)
 static void
 NUM_numpart_from_char(NUMProc *Np, int id, int plen)
 {
-
+       bool isread = FALSE;
+               
 #ifdef DEBUG_TO_FROM_CHAR
-       elog(DEBUG_elog_output, " --- scan start --- ");
+       elog(DEBUG_elog_output, " --- scan start --- id=%s",
+               (id==NUM_0 || id==NUM_9) ? "NUM_0/9" : id==NUM_DEC ? "NUM_DEC" : "???");
 #endif
 
        if (*Np->inout_p == ' ')
                Np->inout_p++;
 
 #define OVERLOAD_TEST  (Np->inout_p >= Np->inout + plen)
+#define AMOUNT_TEST(_s)        (plen-(Np->inout_p-Np->inout) >= _s)
 
        if (*Np->inout_p == ' ')
                Np->inout_p++;
@@ -3640,68 +3644,73 @@ NUM_numpart_from_char(NUMProc *Np, int id, int plen)
                return;
 
        /*
-        * read sign
+        * read sign before number
         */
-       if (*Np->number == ' ' && (id == NUM_0 || id == NUM_9 || NUM_S))
+       if (*Np->number == ' ' && (id == NUM_0 || id == NUM_9 ) && 
+                       (Np->read_pre + Np->read_post)==0)
        {
 
 #ifdef DEBUG_TO_FROM_CHAR
-               elog(DEBUG_elog_output, "Try read sign (%c)", *Np->inout_p);
+               elog(DEBUG_elog_output, "Try read sign (%c), locale positive: %s, negative: %s", 
+                               *Np->inout_p, Np->L_positive_sign, Np->L_negative_sign);
 #endif
 
                /*
                 * locale sign
                 */
-               if (IS_LSIGN(Np->Num))
+               if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_PRE)
                {
-
-                       int                     x = strlen(Np->L_negative_sign);
-
+                       int x=0;
 #ifdef DEBUG_TO_FROM_CHAR
-                       elog(DEBUG_elog_output, "Try read locale sign (%c)", *Np->inout_p);
+                       elog(DEBUG_elog_output, "Try read locale pre-sign (%c)", *Np->inout_p);
 #endif
-                       if (!strncmp(Np->inout_p, Np->L_negative_sign, x))
+                       if ((x = strlen(Np->L_negative_sign)) && 
+                               AMOUNT_TEST(x) &&
+                               strncmp(Np->inout_p, Np->L_negative_sign, x)==0)
                        {
-                               Np->inout_p += x - 1;
+                               Np->inout_p += x;
                                *Np->number = '-';
-                               return;
                        }
-
-                       x = strlen(Np->L_positive_sign);
-                       if (!strncmp(Np->inout_p, Np->L_positive_sign, x))
+                       else if ((x = strlen(Np->L_positive_sign)) && 
+                               AMOUNT_TEST(x) &&
+                               strncmp(Np->inout_p, Np->L_positive_sign, x)==0)
                        {
-                               Np->inout_p += x - 1;
+                               Np->inout_p += x;
                                *Np->number = '+';
-                               return;
                        }
                }
-
+               else
+               {
 #ifdef DEBUG_TO_FROM_CHAR
-               elog(DEBUG_elog_output, "Try read simple sign (%c)", *Np->inout_p);
+                       elog(DEBUG_elog_output, "Try read simple sign (%c)", *Np->inout_p);
 #endif
+                       /*
+                        * simple + - < >
+                        */
+                       if (*Np->inout_p == '-' || (IS_BRACKET(Np->Num) &&
+                                                                               *Np->inout_p == '<'))
+                       {
 
-               /*
-                * simple + - < >
-                */
-               if (*Np->inout_p == '-' || (IS_BRACKET(Np->Num) &&
-                                                                       *Np->inout_p == '<'))
-               {
-
-                       *Np->number = '-';      /* set - */
-                       Np->inout_p++;
+                               *Np->number = '-';      /* set - */
+                               Np->inout_p++;
 
-               }
-               else if (*Np->inout_p == '+')
-               {
+                       }
+                       else if (*Np->inout_p == '+')
+                       {
 
-                       *Np->number = '+';      /* set + */
-                       Np->inout_p++;
+                               *Np->number = '+';      /* set + */
+                               Np->inout_p++;
+                       }
                }
        }
 
        if (OVERLOAD_TEST)
                return;
-
+       
+#ifdef DEBUG_TO_FROM_CHAR
+       elog(DEBUG_elog_output, "Scan for numbers (%c), current number: '%s'", *Np->inout_p, Np->number);
+#endif
+       
        /*
         * read digit
         */
@@ -3716,16 +3725,19 @@ NUM_numpart_from_char(NUMProc *Np, int id, int plen)
 
                if (Np->read_dec)
                        Np->read_post++;
+               else
+                       Np->read_pre++;
 
+               isread = TRUE;
+               
 #ifdef DEBUG_TO_FROM_CHAR
                elog(DEBUG_elog_output, "Read digit (%c)", *Np->inout_p);
 #endif
-
-               /*
-                * read decimal point
-                */
+       /*
+        * read decimal point
+        */
        }
-       else if (IS_DECIMAL(Np->Num))
+       else if (IS_DECIMAL(Np->Num) && Np->read_dec == FALSE)
        {
 
 #ifdef DEBUG_TO_FROM_CHAR
@@ -3737,7 +3749,7 @@ NUM_numpart_from_char(NUMProc *Np, int id, int plen)
                        *Np->number_p = '.';
                        Np->number_p++;
                        Np->read_dec = TRUE;
-
+                       isread = TRUE;
                }
                else
                {
@@ -3747,15 +3759,90 @@ NUM_numpart_from_char(NUMProc *Np, int id, int plen)
                        elog(DEBUG_elog_output, "Try read locale point (%c)",
                                 *Np->inout_p);
 #endif
-                       if (!strncmp(Np->inout_p, Np->decimal, x))
+                       if (x && AMOUNT_TEST(x) && strncmp(Np->inout_p, Np->decimal, x)==0)
                        {
                                Np->inout_p += x - 1;
                                *Np->number_p = '.';
                                Np->number_p++;
                                Np->read_dec = TRUE;
+                               isread = TRUE;
                        }
                }
        }
+
+       if (OVERLOAD_TEST)
+               return;
+       
+       /*
+        * Read sign behind "last" number
+        *
+        * We need sign detection because determine exact position of 
+        * post-sign is difficult:
+        *
+        *      FM9999.9999999S         -> 123.001-
+        *      9.9S                    -> .5-
+        *      FM9.999999MI            -> 5.01-
+        */
+       if (*Np->number == ' ' && Np->read_pre + Np->read_post > 0)
+       {
+               /*
+                * locale sign (NUM_S) is always anchored behind a last number, if:
+                *      - locale sign expected
+                *      - last read char was NUM_0/9 or NUM_DEC
+                *      - and next char is not digit
+                */              
+               if (IS_LSIGN(Np->Num) && isread && 
+                               (Np->inout_p+1) <= Np->inout + plen &&
+                               isdigit(*(Np->inout_p+1))==0)
+               {
+                       int x;
+                       char *tmp = Np->inout_p++;
+                       
+#ifdef DEBUG_TO_FROM_CHAR
+                       elog(DEBUG_elog_output, "Try read locale post-sign (%c)", *Np->inout_p);
+#endif
+                       if ((x = strlen(Np->L_negative_sign)) && 
+                               AMOUNT_TEST(x) &&
+                               strncmp(Np->inout_p, Np->L_negative_sign, x)==0)
+                       {
+                               Np->inout_p += x-1;             /* -1 .. NUM_processor() do inout_p++ */
+                               *Np->number = '-';
+                       }
+                       else if ((x = strlen(Np->L_positive_sign)) && 
+                               AMOUNT_TEST(x) && 
+                               strncmp(Np->inout_p, Np->L_positive_sign, x)==0)
+                       {
+                               Np->inout_p += x-1;             /* -1 .. NUM_processor() do inout_p++ */
+                               *Np->number = '+';
+                       }
+                       if (*Np->number == ' ')
+                               /* no sign read */
+                               Np->inout_p = tmp;
+               }
+               
+               /*
+                * try read non-locale sign, it's happen only if format is not exact
+                * and we cannot determine sign position of MI/PL/SG, an example:
+                *
+                * FM9.999999MI            -> 5.01-
+                *
+                * if (.... && IS_LSIGN(Np->Num)==FALSE) prevents read wrong formats
+                * like to_number('1 -', '9S') where sign is not anchored to last number.
+                */
+               else if (isread==FALSE && IS_LSIGN(Np->Num)==FALSE && 
+                               (IS_PLUS(Np->Num) || IS_MINUS(Np->Num)))
+               {
+#ifdef DEBUG_TO_FROM_CHAR
+                       elog(DEBUG_elog_output, "Try read simple post-sign (%c)", *Np->inout_p);
+#endif
+                       /*
+                        * simple + -
+                        */
+                       if (*Np->inout_p == '-' || *Np->inout_p == '+')
+                               /* NUM_processor() do inout_p++ */
+                               *Np->number = *Np->inout_p;
+               }
+       }
 }
 
 #define IS_PREDEC_SPACE(_n) \
@@ -3978,6 +4065,7 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
        Np->inout = inout;
        Np->last_relevant = NULL;
        Np->read_post = 0;
+       Np->read_pre = 0; 
        Np->read_dec = FALSE;
 
        if (Np->Num->zero_start)
@@ -4130,6 +4218,11 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
                {
                        /*
                         * Create/reading digit/zero/blank/sing
+                        *
+                        * 'NUM_S' note:
+                        *    The locale sign is anchored to number and we read/write it
+                        *    when we work with first or last number (NUM_0/NUM_9). This 
+                        *    is reason why NUM_S missing in follow switch().
                         */
                        switch (n->key->id)
                        {
index b95d79fb890c638cb8b8537fe175a0739b22620c..7d075a2a8eecfcbc4d81db54f2efffdb8de1feda 100644 (file)
@@ -1191,7 +1191,7 @@ SELECT '' AS to_number_12, to_number('.01-', '99.99S');
               |     -0.01
 (1 row)
 
-SELECT '' AS to_number_13, to_number(' . 0 1 -', ' 9 9 . 9 9 S');
+SELECT '' AS to_number_13, to_number(' . 0 1-', ' 9 9 . 9 9 S');
  to_number_13 | to_number 
 --------------+-----------
               |     -0.01
index 06f9dfd749d38484050cf3db6b5e06ce01435d4e..ffc16733390c690d35f5ac0827f8d08a446281ad 100644 (file)
@@ -760,7 +760,7 @@ SELECT '' AS to_number_9,  to_number('.0', '99999999.99999999');
 SELECT '' AS to_number_10, to_number('0', '99.99');
 SELECT '' AS to_number_11, to_number('.-01', 'S99.99');
 SELECT '' AS to_number_12, to_number('.01-', '99.99S');
-SELECT '' AS to_number_13, to_number(' . 0 1 -', ' 9 9 . 9 9 S');
+SELECT '' AS to_number_13, to_number(' . 0 1-', ' 9 9 . 9 9 S');
 
 --
 -- Input syntax