Skip to content

Commit 319902d

Browse files
committed
Fix to_number for the case of a trailing S.
Karel Zak
1 parent 5fef3c6 commit 319902d

File tree

3 files changed

+139
-46
lines changed

3 files changed

+139
-46
lines changed

src/backend/utils/adt/formatting.c

Lines changed: 137 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
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
853853
num_pre, /* space before first number */
854854

855855
read_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

858859
char *number, /* string with number */
859860
*number_p, /* pointer to current number position */
@@ -3623,15 +3624,18 @@ get_last_relevant_decnum(char *num)
36233624
static void
36243625
NUM_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

36313634
if (*Np->inout_p == ' ')
36323635
Np->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

36363640
if (*Np->inout_p == ' ')
36373641
Np->inout_p++;
@@ -3640,68 +3644,73 @@ NUM_numpart_from_char(NUMProc *Np, int id, int plen)
36403644
return;
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 locale pre-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

37023707
if (OVERLOAD_TEST)
37033708
return;
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

37173726
if (Np->read_dec)
37183727
Np->read_post++;
3728+
else
3729+
Np->read_pre++;
37193730

3731+
isread = TRUE;
3732+
37203733
#ifdef DEBUG_TO_FROM_CHAR
37213734
elog(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 = '.';
37383750
Np->number_p++;
37393751
Np->read_dec = TRUE;
3740-
3752+
isread = TRUE;
37413753
}
37423754
else
37433755
{
@@ -3747,15 +3759,90 @@ NUM_numpart_from_char(NUMProc *Np, int id, int plen)
37473759
elog(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
{
37523764
Np->inout_p += x - 1;
37533765
*Np->number_p = '.';
37543766
Np->number_p++;
37553767
Np->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,
39784065
Np->inout = inout;
39794066
Np->last_relevant = NULL;
39804067
Np->read_post = 0;
4068+
Np->read_pre = 0;
39814069
Np->read_dec = FALSE;
39824070

39834071
if (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
*/
41344227
switch (n->key->id)
41354228
{

src/test/regress/expected/numeric.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1191,7 +1191,7 @@ SELECT '' AS to_number_12, to_number('.01-', '99.99S');
11911191
| -0.01
11921192
(1 row)
11931193

1194-
SELECT '' AS to_number_13, to_number(' . 0 1 -', ' 9 9 . 9 9 S');
1194+
SELECT '' AS to_number_13, to_number(' . 0 1-', ' 9 9 . 9 9 S');
11951195
to_number_13 | to_number
11961196
--------------+-----------
11971197
| -0.01

src/test/regress/sql/numeric.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ SELECT '' AS to_number_9, to_number('.0', '99999999.99999999');
760760
SELECT '' AS to_number_10, to_number('0', '99.99');
761761
SELECT '' AS to_number_11, to_number('.-01', 'S99.99');
762762
SELECT '' AS to_number_12, to_number('.01-', '99.99S');
763-
SELECT '' AS to_number_13, to_number(' . 0 1 -', ' 9 9 . 9 9 S');
763+
SELECT '' AS to_number_13, to_number(' . 0 1-', ' 9 9 . 9 9 S');
764764

765765
--
766766
-- Input syntax

0 commit comments

Comments
 (0)