Fix to_number for the case of a trailing S.

Karel Zak
This commit is contained in:
Tom Lane 2004-10-28 18:55:08 +00:00
parent 5fef3c6ef0
commit 319902dc8c
3 changed files with 139 additions and 46 deletions

View 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)
{

View 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

View 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