here is a patch fixing today's bug report:

> Date: Thu, 14 Dec 2000 12:44:47 +0100 (CET)
> From: Kovacs Zoltan Sandor <tip@pc10.radnoti-szeged.sulinet.hu>
> To: pgsql-bugs@postgresql.org
> Subject: [BUGS] to_char() causes backend to close connection
>
> Hi, this query gives different strange results:
>
> select to_char(now()::abstime,'YYMMDDHH24MI');
>
> I get e.g. a "backend closed the channel unexpectedly..." error with
> successful or failed resetting attempt (indeterministic)

 Again thanks Kovacs, you found really designing bug, that appear
if anyone write bad format template to "number" version of to_char()
(as you with 'DD').

                                        Karel
This commit is contained in:
Bruce Momjian 2000-12-15 19:15:09 +00:00
parent 0cf37659c4
commit ff783fbae0
1 changed files with 71 additions and 10 deletions

View File

@ -1,7 +1,7 @@
/* ----------------------------------------------------------------------- /* -----------------------------------------------------------------------
* formatting.c * formatting.c
* *
* $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.26 2000/12/03 20:45:35 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.27 2000/12/15 19:15:09 momjian Exp $
* *
* *
* Portions Copyright (c) 1999-2000, PostgreSQL, Inc * Portions Copyright (c) 1999-2000, PostgreSQL, Inc
@ -44,6 +44,17 @@
* *
* Karel Zak * Karel Zak
* *
* TODO (7.2):
* - replace some global values by struct that handle it
* - check last used entry in the cache_search
* - better number building (formatting)
* - add support for abstime
* - add support for roman number to standard number conversion
* - add support for number spelling
* - add support for string to string formatting (we must be better
* than Oracle :-),
* to_char('Hello', 'X X X X X') -> 'H e l l o'
*
* ----------------------------------------------------------------------- * -----------------------------------------------------------------------
*/ */
@ -334,11 +345,12 @@ static int DCHCounter = 0;
/* global cache for --- number part */ /* global cache for --- number part */
static NUMCacheEntry NUMCache[NUM_CACHE_FIELDS + 1]; static NUMCacheEntry NUMCache[NUM_CACHE_FIELDS + 1];
static NUMCacheEntry *last_NUMCacheEntry;
static int n_NUMCache = 0; /* number of entries */ static int n_NUMCache = 0; /* number of entries */
static int NUMCounter = 0; static int NUMCounter = 0;
#define MAX_INT32 (2147483640) #define MAX_INT32 (2147483600)
/* ---------- /* ----------
* For char->date/time conversion * For char->date/time conversion
@ -850,8 +862,10 @@ static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *nu
int plen, int sign, int type); int plen, int sign, int type);
static DCHCacheEntry *DCH_cache_search(char *str); static DCHCacheEntry *DCH_cache_search(char *str);
static DCHCacheEntry *DCH_cache_getnew(char *str); static DCHCacheEntry *DCH_cache_getnew(char *str);
static NUMCacheEntry *NUM_cache_search(char *str); static NUMCacheEntry *NUM_cache_search(char *str);
static NUMCacheEntry *NUM_cache_getnew(char *str); static NUMCacheEntry *NUM_cache_getnew(char *str);
static void NUM_cache_remove(NUMCacheEntry *ent);
/* ---------- /* ----------
@ -917,8 +931,10 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
case NUM_9: case NUM_9:
if (IS_BRACKET(num)) if (IS_BRACKET(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): '9' must be ahead of 'PR'."); elog(ERROR, "to_char/to_number(): '9' must be ahead of 'PR'.");
}
if (IS_MULTI(num)) if (IS_MULTI(num))
{ {
++num->multi; ++num->multi;
@ -932,8 +948,10 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
case NUM_0: case NUM_0:
if (IS_BRACKET(num)) if (IS_BRACKET(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): '0' must be ahead of 'PR'."); elog(ERROR, "to_char/to_number(): '0' must be ahead of 'PR'.");
}
if (!IS_ZERO(num) && !IS_DECIMAL(num)) if (!IS_ZERO(num) && !IS_DECIMAL(num))
{ {
num->flag |= NUM_F_ZERO; num->flag |= NUM_F_ZERO;
@ -957,9 +975,15 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
num->need_locale = TRUE; num->need_locale = TRUE;
case NUM_DEC: case NUM_DEC:
if (IS_DECIMAL(num)) if (IS_DECIMAL(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): not unique decimal poit."); elog(ERROR, "to_char/to_number(): not unique decimal poit.");
}
if (IS_MULTI(num)) if (IS_MULTI(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): can't use 'V' and decimal poin together."); elog(ERROR, "to_char/to_number(): can't use 'V' and decimal poin together.");
}
num->flag |= NUM_F_DECIMAL; num->flag |= NUM_F_DECIMAL;
break; break;
@ -969,11 +993,15 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
case NUM_S: case NUM_S:
if (IS_LSIGN(num)) if (IS_LSIGN(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): not unique 'S'."); elog(ERROR, "to_char/to_number(): not unique 'S'.");
}
if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num)) if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): can't use 'S' and 'PL'/'MI'/'SG'/'PR' together."); elog(ERROR, "to_char/to_number(): can't use 'S' and 'PL'/'MI'/'SG'/'PR' together.");
}
if (!IS_DECIMAL(num)) if (!IS_DECIMAL(num))
{ {
num->lsign = NUM_LSIGN_PRE; num->lsign = NUM_LSIGN_PRE;
@ -992,29 +1020,38 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
case NUM_MI: case NUM_MI:
if (IS_LSIGN(num)) if (IS_LSIGN(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): can't use 'S' and 'MI' together."); elog(ERROR, "to_char/to_number(): can't use 'S' and 'MI' together.");
}
num->flag |= NUM_F_MINUS; num->flag |= NUM_F_MINUS;
break; break;
case NUM_PL: case NUM_PL:
if (IS_LSIGN(num)) if (IS_LSIGN(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): can't use 'S' and 'PL' together."); elog(ERROR, "to_char/to_number(): can't use 'S' and 'PL' together.");
}
num->flag |= NUM_F_PLUS; num->flag |= NUM_F_PLUS;
break; break;
case NUM_SG: case NUM_SG:
if (IS_LSIGN(num)) if (IS_LSIGN(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): can't use 'S' and 'SG' together."); elog(ERROR, "to_char/to_number(): can't use 'S' and 'SG' together.");
}
num->flag |= NUM_F_MINUS; num->flag |= NUM_F_MINUS;
num->flag |= NUM_F_PLUS; num->flag |= NUM_F_PLUS;
break; break;
case NUM_PR: case NUM_PR:
if (IS_LSIGN(num) || IS_PLUS(num) || IS_MINUS(num)) if (IS_LSIGN(num) || IS_PLUS(num) || IS_MINUS(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): can't use 'PR' and 'S'/'PL'/'MI'/'SG' together."); elog(ERROR, "to_char/to_number(): can't use 'PR' and 'S'/'PL'/'MI'/'SG' together.");
}
num->flag |= NUM_F_BRACKET; num->flag |= NUM_F_BRACKET;
break; break;
@ -1030,11 +1067,15 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
case NUM_V: case NUM_V:
if (IS_DECIMAL(num)) if (IS_DECIMAL(num))
{
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): can't use 'V' and decimal poin together."); elog(ERROR, "to_char/to_number(): can't use 'V' and decimal poin together.");
}
num->flag |= NUM_F_MULTI; num->flag |= NUM_F_MULTI;
break; break;
case NUM_E: case NUM_E:
NUM_cache_remove(last_NUMCacheEntry);
elog(ERROR, "to_char/to_number(): 'E' is not supported."); elog(ERROR, "to_char/to_number(): 'E' is not supported.");
} }
@ -2988,6 +3029,14 @@ NUM_cache_getnew(char *str)
for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++) for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
{ {
/* entry removed via NUM_cache_remove()
* can be used here
*/
if (*ent->str == '\0')
{
old = ent;
break;
}
if (ent->age < old->age) if (ent->age < old->age)
old = ent; old = ent;
} }
@ -3015,6 +3064,7 @@ NUM_cache_getnew(char *str)
zeroize_NUM(&ent->Num); zeroize_NUM(&ent->Num);
last_NUMCacheEntry = ent;
return ent; /* never */ return ent; /* never */
} }
@ -3040,6 +3090,7 @@ NUM_cache_search(char *str)
if (strcmp(ent->str, str) == 0) if (strcmp(ent->str, str) == 0)
{ {
ent->age = (++NUMCounter); ent->age = (++NUMCounter);
last_NUMCacheEntry = ent;
return ent; return ent;
} }
i++; i++;
@ -3048,6 +3099,16 @@ NUM_cache_search(char *str)
return (NUMCacheEntry *) NULL; return (NUMCacheEntry *) NULL;
} }
static void
NUM_cache_remove(NUMCacheEntry *ent)
{
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "REMOVING ENTRY (%s)", ent->str);
#endif
*ent->str = '\0';
ent->age = 0;
}
/* ---------- /* ----------
* Cache routine for NUM to_char version * Cache routine for NUM to_char version
* ---------- * ----------
@ -3070,7 +3131,7 @@ NUM_cache(int len, NUMDesc *Num, char *pars_str, int *flag)
* Allocate new memory if format picture is bigger than static cache * Allocate new memory if format picture is bigger than static cache
* and not use cache (call parser always) - flag=1 show this variant * and not use cache (call parser always) - flag=1 show this variant
* ---------- * ----------
*/ */
if (len > NUM_CACHE_SIZE) if (len > NUM_CACHE_SIZE)
{ {