11/* -----------------------------------------------------------------------
22 * formatting.c
33 *
4- * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.79 2004/10/13 01:25:11 neilc Exp $
4+ * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.80 2004/10/28 18:55:06 tgl Exp $
55 *
66 *
77 * Portions Copyright (c) 1999-2004, PostgreSQL Global Development Group
@@ -853,7 +853,8 @@ typedef struct NUMProc
853853num_pre ,/* space before first number*/
854854
855855read_dec ,/* to_number - was read dec. point*/
856- read_post ;/* to_number - number of dec. digit */
856+ read_post ,/* to_number - number of dec. digit */
857+ read_pre ;/* to_number - number non-dec. digit */
857858
858859char * number ,/* string with number*/
859860* number_p ,/* pointer to current number position */
@@ -3623,15 +3624,18 @@ get_last_relevant_decnum(char *num)
36233624static void
36243625NUM_numpart_from_char (NUMProc * Np ,int id ,int plen )
36253626{
3626-
3627+ bool isread = FALSE;
3628+
36273629#ifdef DEBUG_TO_FROM_CHAR
3628- elog (DEBUG_elog_output ," --- scan start --- " );
3630+ elog (DEBUG_elog_output ," --- scan start --- id=%s" ,
3631+ (id == NUM_0 || id == NUM_9 ) ?"NUM_0/9" :id == NUM_DEC ?"NUM_DEC" :"???" );
36293632#endif
36303633
36313634if (* Np -> inout_p == ' ' )
36323635Np -> inout_p ++ ;
36333636
36343637#define OVERLOAD_TEST (Np->inout_p >= Np->inout + plen)
3638+ #define AMOUNT_TEST (_s )(plen-(Np->inout_p-Np->inout) >= _s)
36353639
36363640if (* Np -> inout_p == ' ' )
36373641Np -> inout_p ++ ;
@@ -3640,68 +3644,73 @@ NUM_numpart_from_char(NUMProc *Np, int id, int plen)
36403644return ;
36413645
36423646/*
3643- * read sign
3647+ * read sign before number
36443648 */
3645- if (* Np -> number == ' ' && (id == NUM_0 || id == NUM_9 || NUM_S ))
3649+ if (* Np -> number == ' ' && (id == NUM_0 || id == NUM_9 )&&
3650+ (Np -> read_pre + Np -> read_post )== 0 )
36463651{
36473652
36483653#ifdef DEBUG_TO_FROM_CHAR
3649- elog (DEBUG_elog_output ,"Try read sign (%c)" ,* Np -> inout_p );
3654+ elog (DEBUG_elog_output ,"Try read sign (%c), locale positive: %s, negative: %s" ,
3655+ * Np -> inout_p ,Np -> L_positive_sign ,Np -> L_negative_sign );
36503656#endif
36513657
36523658/*
36533659 * locale sign
36543660 */
3655- if (IS_LSIGN (Np -> Num ))
3661+ if (IS_LSIGN (Np -> Num )&& Np -> Num -> lsign == NUM_LSIGN_PRE )
36563662{
3657-
3658- int x = strlen (Np -> L_negative_sign );
3659-
3663+ int x = 0 ;
36603664#ifdef DEBUG_TO_FROM_CHAR
3661- elog (DEBUG_elog_output ,"Try read locale sign (%c)" ,* Np -> inout_p );
3665+ elog (DEBUG_elog_output ,"Try read localepre- sign (%c)" ,* Np -> inout_p );
36623666#endif
3663- if (!strncmp (Np -> inout_p ,Np -> L_negative_sign ,x ))
3667+ if ((x = strlen (Np -> L_negative_sign ))&&
3668+ AMOUNT_TEST (x )&&
3669+ strncmp (Np -> inout_p ,Np -> L_negative_sign ,x )== 0 )
36643670{
3665- Np -> inout_p += x - 1 ;
3671+ Np -> inout_p += x ;
36663672* Np -> number = '-' ;
3667- return ;
36683673}
3669-
3670- x = strlen ( Np -> L_positive_sign );
3671- if (! strncmp (Np -> inout_p ,Np -> L_positive_sign ,x ))
3674+ else if (( x = strlen ( Np -> L_positive_sign )) &&
3675+ AMOUNT_TEST ( x ) &&
3676+ strncmp (Np -> inout_p ,Np -> L_positive_sign ,x )== 0 )
36723677{
3673- Np -> inout_p += x - 1 ;
3678+ Np -> inout_p += x ;
36743679* Np -> number = '+' ;
3675- return ;
36763680}
36773681}
3678-
3682+ else
3683+ {
36793684#ifdef DEBUG_TO_FROM_CHAR
3680- elog (DEBUG_elog_output ,"Try read simple sign (%c)" ,* Np -> inout_p );
3685+ elog (DEBUG_elog_output ,"Try read simple sign (%c)" ,* Np -> inout_p );
36813686#endif
3687+ /*
3688+ * simple + - < >
3689+ */
3690+ if (* Np -> inout_p == '-' || (IS_BRACKET (Np -> Num )&&
3691+ * Np -> inout_p == '<' ))
3692+ {
36823693
3683- /*
3684- * simple + - < >
3685- */
3686- if (* Np -> inout_p == '-' || (IS_BRACKET (Np -> Num )&&
3687- * Np -> inout_p == '<' ))
3688- {
3689-
3690- * Np -> number = '-' ;/* set - */
3691- Np -> inout_p ++ ;
3694+ * Np -> number = '-' ;/* set - */
3695+ Np -> inout_p ++ ;
36923696
3693- }
3694- else if (* Np -> inout_p == '+' )
3695- {
3697+ }
3698+ else if (* Np -> inout_p == '+' )
3699+ {
36963700
3697- * Np -> number = '+' ;/* set + */
3698- Np -> inout_p ++ ;
3701+ * Np -> number = '+' ;/* set + */
3702+ Np -> inout_p ++ ;
3703+ }
36993704}
37003705}
37013706
37023707if (OVERLOAD_TEST )
37033708return ;
3704-
3709+
3710+ #ifdef DEBUG_TO_FROM_CHAR
3711+ elog (DEBUG_elog_output ,"Scan for numbers (%c), current number: '%s'" ,* Np -> inout_p ,Np -> number );
3712+ #endif
3713+
37053714/*
37063715 * read digit
37073716 */
@@ -3716,16 +3725,19 @@ NUM_numpart_from_char(NUMProc *Np, int id, int plen)
37163725
37173726if (Np -> read_dec )
37183727Np -> read_post ++ ;
3728+ else
3729+ Np -> read_pre ++ ;
37193730
3731+ isread = TRUE;
3732+
37203733#ifdef DEBUG_TO_FROM_CHAR
37213734elog (DEBUG_elog_output ,"Read digit (%c)" ,* Np -> inout_p );
37223735#endif
3723-
3724- /*
3725- * read decimal point
3726- */
3736+ /*
3737+ * read decimal point
3738+ */
37273739}
3728- else if (IS_DECIMAL (Np -> Num ))
3740+ else if (IS_DECIMAL (Np -> Num )&& Np -> read_dec == FALSE )
37293741{
37303742
37313743#ifdef DEBUG_TO_FROM_CHAR
@@ -3737,7 +3749,7 @@ NUM_numpart_from_char(NUMProc *Np, int id, int plen)
37373749* Np -> number_p = '.' ;
37383750Np -> number_p ++ ;
37393751Np -> read_dec = TRUE;
3740-
3752+ isread = TRUE;
37413753}
37423754else
37433755{
@@ -3747,15 +3759,90 @@ NUM_numpart_from_char(NUMProc *Np, int id, int plen)
37473759elog (DEBUG_elog_output ,"Try read locale point (%c)" ,
37483760* Np -> inout_p );
37493761#endif
3750- if (! strncmp (Np -> inout_p ,Np -> decimal ,x ))
3762+ if (x && AMOUNT_TEST ( x ) && strncmp (Np -> inout_p ,Np -> decimal ,x )== 0 )
37513763{
37523764Np -> inout_p += x - 1 ;
37533765* Np -> number_p = '.' ;
37543766Np -> number_p ++ ;
37553767Np -> read_dec = TRUE;
3768+ isread = TRUE;
37563769}
37573770}
37583771}
3772+
3773+ if (OVERLOAD_TEST )
3774+ return ;
3775+
3776+ /*
3777+ * Read sign behind "last" number
3778+ *
3779+ * We need sign detection because determine exact position of
3780+ * post-sign is difficult:
3781+ *
3782+ * FM9999.9999999S -> 123.001-
3783+ * 9.9S-> .5-
3784+ * FM9.999999MI-> 5.01-
3785+ */
3786+ if (* Np -> number == ' ' && Np -> read_pre + Np -> read_post > 0 )
3787+ {
3788+ /*
3789+ * locale sign (NUM_S) is always anchored behind a last number, if:
3790+ *- locale sign expected
3791+ *- last read char was NUM_0/9 or NUM_DEC
3792+ *- and next char is not digit
3793+ */
3794+ if (IS_LSIGN (Np -> Num )&& isread &&
3795+ (Np -> inout_p + 1 ) <=Np -> inout + plen &&
3796+ isdigit (* (Np -> inout_p + 1 ))== 0 )
3797+ {
3798+ int x ;
3799+ char * tmp = Np -> inout_p ++ ;
3800+
3801+ #ifdef DEBUG_TO_FROM_CHAR
3802+ elog (DEBUG_elog_output ,"Try read locale post-sign (%c)" ,* Np -> inout_p );
3803+ #endif
3804+ if ((x = strlen (Np -> L_negative_sign ))&&
3805+ AMOUNT_TEST (x )&&
3806+ strncmp (Np -> inout_p ,Np -> L_negative_sign ,x )== 0 )
3807+ {
3808+ Np -> inout_p += x - 1 ;/* -1 .. NUM_processor() do inout_p++ */
3809+ * Np -> number = '-' ;
3810+ }
3811+ else if ((x = strlen (Np -> L_positive_sign ))&&
3812+ AMOUNT_TEST (x )&&
3813+ strncmp (Np -> inout_p ,Np -> L_positive_sign ,x )== 0 )
3814+ {
3815+ Np -> inout_p += x - 1 ;/* -1 .. NUM_processor() do inout_p++ */
3816+ * Np -> number = '+' ;
3817+ }
3818+ if (* Np -> number == ' ' )
3819+ /* no sign read */
3820+ Np -> inout_p = tmp ;
3821+ }
3822+
3823+ /*
3824+ * try read non-locale sign, it's happen only if format is not exact
3825+ * and we cannot determine sign position of MI/PL/SG, an example:
3826+ *
3827+ * FM9.999999MI -> 5.01-
3828+ *
3829+ * if (.... && IS_LSIGN(Np->Num)==FALSE) prevents read wrong formats
3830+ * like to_number('1 -', '9S') where sign is not anchored to last number.
3831+ */
3832+ else if (isread == FALSE&& IS_LSIGN (Np -> Num )== FALSE&&
3833+ (IS_PLUS (Np -> Num )|| IS_MINUS (Np -> Num )))
3834+ {
3835+ #ifdef DEBUG_TO_FROM_CHAR
3836+ elog (DEBUG_elog_output ,"Try read simple post-sign (%c)" ,* Np -> inout_p );
3837+ #endif
3838+ /*
3839+ * simple + -
3840+ */
3841+ if (* Np -> inout_p == '-' || * Np -> inout_p == '+' )
3842+ /* NUM_processor() do inout_p++ */
3843+ * Np -> number = * Np -> inout_p ;
3844+ }
3845+ }
37593846}
37603847
37613848#define IS_PREDEC_SPACE (_n ) \
@@ -3978,6 +4065,7 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
39784065Np -> inout = inout ;
39794066Np -> last_relevant = NULL ;
39804067Np -> read_post = 0 ;
4068+ Np -> read_pre = 0 ;
39814069Np -> read_dec = FALSE;
39824070
39834071if (Np -> Num -> zero_start )
@@ -4130,6 +4218,11 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
41304218{
41314219/*
41324220 * Create/reading digit/zero/blank/sing
4221+ *
4222+ * 'NUM_S' note:
4223+ * The locale sign is anchored to number and we read/write it
4224+ * when we work with first or last number (NUM_0/NUM_9). This
4225+ * is reason why NUM_S missing in follow switch().
41334226 */
41344227switch (n -> key -> id )
41354228{