Make PGLC_setlocale() static, and document that it can't be used safely
for any other purpose than PGLC_localeconv()'s internal save/restore of locale settings. Fix cash.c to call PGLC_localeconv() rather than making a direct call to localeconv() --- the old way, if PGLC_localeconv() had already cached a locale result, it would be overwritten by the first cash_in or cash_out operation, leading to wrong-locale results later. Probably no demonstrable bug today, since we only appear to be looking at the LC_MONETARY results which should be the same anyway, but definitely a gotcha waiting to strike.
This commit is contained in:
parent
74dc04a034
commit
e3269cab31
3 changed files with 64 additions and 53 deletions
|
@ -9,18 +9,23 @@
|
||||||
* workings can be found in the book "Software Solutions in C" by
|
* workings can be found in the book "Software Solutions in C" by
|
||||||
* Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
|
* Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
|
||||||
*
|
*
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.47 2000/11/25 20:33:52 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.48 2000/11/25 22:43:08 tgl Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#ifdef USE_LOCALE
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "postgres.h"
|
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/cash.h"
|
#include "utils/cash.h"
|
||||||
|
#include "utils/pg_locale.h"
|
||||||
|
|
||||||
|
|
||||||
static const char *num_word(Cash value);
|
static const char *num_word(Cash value);
|
||||||
|
|
||||||
|
@ -31,11 +36,6 @@ static const char *num_word(Cash value);
|
||||||
#define LAST_PAREN (TERMINATOR - 1)
|
#define LAST_PAREN (TERMINATOR - 1)
|
||||||
#define LAST_DIGIT (LAST_PAREN - 1)
|
#define LAST_DIGIT (LAST_PAREN - 1)
|
||||||
|
|
||||||
#ifdef USE_LOCALE
|
|
||||||
static struct lconv *lconvert = NULL;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cash is a pass-by-ref SQL type, so we must pass and return pointers.
|
* Cash is a pass-by-ref SQL type, so we must pass and return pointers.
|
||||||
|
@ -82,11 +82,11 @@ cash_in(PG_FUNCTION_ARGS)
|
||||||
ssymbol,
|
ssymbol,
|
||||||
psymbol,
|
psymbol,
|
||||||
*nsymbol;
|
*nsymbol;
|
||||||
|
#ifdef USE_LOCALE
|
||||||
|
struct lconv *lconvert = PGLC_localeconv();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_LOCALE
|
#ifdef USE_LOCALE
|
||||||
if (lconvert == NULL)
|
|
||||||
lconvert = localeconv();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* frac_digits will be CHAR_MAX in some locales, notably C. However,
|
* frac_digits will be CHAR_MAX in some locales, notably C. However,
|
||||||
* just testing for == CHAR_MAX is risky, because of compilers like
|
* just testing for == CHAR_MAX is risky, because of compilers like
|
||||||
|
@ -238,11 +238,11 @@ cash_out(PG_FUNCTION_ARGS)
|
||||||
dsymbol,
|
dsymbol,
|
||||||
*nsymbol;
|
*nsymbol;
|
||||||
char convention;
|
char convention;
|
||||||
|
#ifdef USE_LOCALE
|
||||||
|
struct lconv *lconvert = PGLC_localeconv();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_LOCALE
|
#ifdef USE_LOCALE
|
||||||
if (lconvert == NULL)
|
|
||||||
lconvert = localeconv();
|
|
||||||
|
|
||||||
/* see comments about frac_digits in cash_in() */
|
/* see comments about frac_digits in cash_in() */
|
||||||
points = lconvert->frac_digits;
|
points = lconvert->frac_digits;
|
||||||
if (points < 0 || points > 10)
|
if (points < 0 || points > 10)
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------
|
/* -----------------------------------------------------------------------
|
||||||
* pg_locale.c
|
* pg_locale.c
|
||||||
*
|
*
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.6 2000/08/29 04:41:47 momjian Exp $
|
* The PostgreSQL locale utils.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.7 2000/11/25 22:43:08 tgl Exp $
|
||||||
|
*
|
||||||
* Portions Copyright (c) 1999-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1999-2000, PostgreSQL, Inc
|
||||||
*
|
*
|
||||||
* The PostgreSQL locale utils.
|
|
||||||
*
|
|
||||||
* Karel Zak - Zakkr
|
* Karel Zak - Zakkr
|
||||||
*
|
*
|
||||||
* -----------------------------------------------------------------------
|
* -----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#ifdef USE_LOCALE
|
#ifdef USE_LOCALE
|
||||||
|
@ -28,11 +25,13 @@
|
||||||
|
|
||||||
static struct lconv *CurrentLocaleConv = NULL;
|
static struct lconv *CurrentLocaleConv = NULL;
|
||||||
|
|
||||||
|
static void PGLC_setlocale(PG_LocaleCategories * lc);
|
||||||
|
|
||||||
/*------
|
/*------
|
||||||
* Return in PG_LocaleCategories current locale setting
|
* Return in PG_LocaleCategories the current locale settings
|
||||||
*------
|
*------
|
||||||
*/
|
*/
|
||||||
PG_LocaleCategories *
|
void
|
||||||
PGLC_current(PG_LocaleCategories * lc)
|
PGLC_current(PG_LocaleCategories * lc)
|
||||||
{
|
{
|
||||||
lc->lang = getenv("LANG");
|
lc->lang = getenv("LANG");
|
||||||
|
@ -45,7 +44,6 @@ PGLC_current(PG_LocaleCategories * lc)
|
||||||
#ifdef LC_MESSAGES
|
#ifdef LC_MESSAGES
|
||||||
lc->lc_messages = setlocale(LC_MESSAGES, NULL);
|
lc->lc_messages = setlocale(LC_MESSAGES, NULL);
|
||||||
#endif
|
#endif
|
||||||
return lc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,7 +53,7 @@ PGLC_current(PG_LocaleCategories * lc)
|
||||||
* Print a PG_LocaleCategories struct as DEBUG
|
* Print a PG_LocaleCategories struct as DEBUG
|
||||||
*------
|
*------
|
||||||
*/
|
*/
|
||||||
PG_LocaleCategories *
|
static void
|
||||||
PGLC_debug_lc(PG_LocaleCategories * lc)
|
PGLC_debug_lc(PG_LocaleCategories * lc)
|
||||||
{
|
{
|
||||||
#ifdef LC_MESSAGES
|
#ifdef LC_MESSAGES
|
||||||
|
@ -73,72 +71,86 @@ PGLC_debug_lc(PG_LocaleCategories * lc)
|
||||||
, lc->lc_messages
|
, lc->lc_messages
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
return lc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*------
|
/*------
|
||||||
* Set locales via a PG_LocaleCategories struct
|
* Set locales via a PG_LocaleCategories struct
|
||||||
|
*
|
||||||
|
* NB: it would be very dangerous to set the locale values to any random
|
||||||
|
* choice of locale, since that could cause indexes to become corrupt, etc.
|
||||||
|
* Therefore this routine is NOT exported from this module. It should be
|
||||||
|
* used only to restore previous locale settings during PGLC_localeconv.
|
||||||
*------
|
*------
|
||||||
*/
|
*/
|
||||||
PG_LocaleCategories *
|
static void
|
||||||
PGLC_setlocale(PG_LocaleCategories * lc)
|
PGLC_setlocale(PG_LocaleCategories * lc)
|
||||||
{
|
{
|
||||||
|
if (!setlocale(LC_COLLATE, lc->lc_collate))
|
||||||
|
elog(NOTICE, "pg_setlocale(): 'LC_COLLATE=%s' cannot be honored.",
|
||||||
|
lc->lc_collate);
|
||||||
|
|
||||||
if (!setlocale(LC_CTYPE, lc->lc_ctype))
|
if (!setlocale(LC_CTYPE, lc->lc_ctype))
|
||||||
elog(NOTICE, "pg_setlocale(): 'LC_CTYPE=%s' cannot be honored.", lc->lc_ctype);
|
elog(NOTICE, "pg_setlocale(): 'LC_CTYPE=%s' cannot be honored.",
|
||||||
|
lc->lc_ctype);
|
||||||
|
|
||||||
if (!setlocale(LC_NUMERIC, lc->lc_numeric))
|
if (!setlocale(LC_NUMERIC, lc->lc_numeric))
|
||||||
elog(NOTICE, "pg_setlocale(): 'LC_NUMERIC=%s' cannot be honored.", lc->lc_numeric);
|
elog(NOTICE, "pg_setlocale(): 'LC_NUMERIC=%s' cannot be honored.",
|
||||||
|
lc->lc_numeric);
|
||||||
|
|
||||||
if (!setlocale(LC_TIME, lc->lc_time))
|
if (!setlocale(LC_TIME, lc->lc_time))
|
||||||
elog(NOTICE, "pg_setlocale(): 'LC_TIME=%s' cannot be honored.", lc->lc_time);
|
elog(NOTICE, "pg_setlocale(): 'LC_TIME=%s' cannot be honored.",
|
||||||
|
lc->lc_time);
|
||||||
if (!setlocale(LC_COLLATE, lc->lc_collate))
|
|
||||||
elog(NOTICE, "pg_setlocale(): 'LC_COLLATE=%s' cannot be honored.", lc->lc_collate);
|
|
||||||
|
|
||||||
if (!setlocale(LC_MONETARY, lc->lc_monetary))
|
if (!setlocale(LC_MONETARY, lc->lc_monetary))
|
||||||
elog(NOTICE, "pg_setlocale(): 'LC_MONETARY=%s' cannot be honored.", lc->lc_monetary);
|
elog(NOTICE, "pg_setlocale(): 'LC_MONETARY=%s' cannot be honored.",
|
||||||
|
lc->lc_monetary);
|
||||||
|
|
||||||
#ifdef LC_MESSAGES
|
#ifdef LC_MESSAGES
|
||||||
if (!setlocale(LC_MESSAGES, lc->lc_messages))
|
if (!setlocale(LC_MESSAGES, lc->lc_messages))
|
||||||
elog(NOTICE, "pg_setlocale(): 'LC_MESSAGE=%s' cannot be honored.", lc->lc_messages);
|
elog(NOTICE, "pg_setlocale(): 'LC_MESSAGE=%s' cannot be honored.",
|
||||||
|
lc->lc_messages);
|
||||||
#endif
|
#endif
|
||||||
return lc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*------
|
/*------
|
||||||
* Return the POSIX lconv struct (contains number/money formatting information)
|
* Return the POSIX lconv struct (contains number/money formatting information)
|
||||||
* with locale information for *all* categories.
|
* with locale information for all categories. Note that returned lconv
|
||||||
* => Returned lconv is *independent* on current locale catogories setting - in
|
* does not depend on currently active category settings, but on external
|
||||||
* contrast to standard localeconv().
|
* environment variables for locale.
|
||||||
*
|
*
|
||||||
* ! libc prepare memory space for lconv itself and all returned strings in
|
* XXX we assume that restoring old category settings via setlocale() will
|
||||||
* lconv are *static strings*.
|
* not immediately corrupt the static data returned by localeconv().
|
||||||
|
* How portable is this?
|
||||||
|
*
|
||||||
|
* XXX in any case, there certainly must not be any other calls to
|
||||||
|
* localeconv() anywhere in the backend, else the values reported here
|
||||||
|
* will be overwritten with the Postgres-internal locale settings.
|
||||||
*------
|
*------
|
||||||
*/
|
*/
|
||||||
struct lconv *
|
struct lconv *
|
||||||
PGLC_localeconv(void)
|
PGLC_localeconv(void)
|
||||||
{
|
{
|
||||||
PG_LocaleCategories lc;
|
PG_LocaleCategories lc;
|
||||||
|
|
||||||
|
/* Did we do it already? */
|
||||||
if (CurrentLocaleConv)
|
if (CurrentLocaleConv)
|
||||||
return CurrentLocaleConv;
|
return CurrentLocaleConv;
|
||||||
|
|
||||||
/* Save current locale setting to lc */
|
/* Save current locale setting to lc */
|
||||||
PGLC_current(&lc);
|
PGLC_current(&lc);
|
||||||
|
|
||||||
/* Set all locale category for current lang */
|
/* Set all locale categories based on postmaster's environment vars */
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
|
||||||
/* Get numeric formatting information */
|
/* Get formatting information for the external environment */
|
||||||
CurrentLocaleConv = localeconv();
|
CurrentLocaleConv = localeconv();
|
||||||
|
|
||||||
/* Set previous original locale */
|
/* Restore Postgres' internal locale settings */
|
||||||
PGLC_setlocale(&lc);
|
PGLC_setlocale(&lc);
|
||||||
|
|
||||||
return CurrentLocaleConv;
|
return CurrentLocaleConv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif /* USE_LOCALE */
|
#endif /* USE_LOCALE */
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------
|
/* -----------------------------------------------------------------------
|
||||||
* pg_locale.h
|
* pg_locale.h
|
||||||
*
|
*
|
||||||
* $Header: /cvsroot/pgsql/src/include/utils/pg_locale.h,v 1.4 2000/04/12 17:16:55 momjian Exp $
|
* The PostgreSQL locale utils.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
* $Id: pg_locale.h,v 1.5 2000/11/25 22:43:07 tgl Exp $
|
||||||
|
*
|
||||||
* Portions Copyright (c) 1999-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1999-2000, PostgreSQL, Inc
|
||||||
*
|
*
|
||||||
* The PostgreSQL locale utils.
|
|
||||||
*
|
|
||||||
* Karel Zak - Zakkr
|
* Karel Zak - Zakkr
|
||||||
*
|
*
|
||||||
* -----------------------------------------------------------------------
|
* -----------------------------------------------------------------------
|
||||||
|
@ -35,13 +34,13 @@ typedef struct PG_LocaleCategories
|
||||||
} PG_LocaleCategories;
|
} PG_LocaleCategories;
|
||||||
|
|
||||||
|
|
||||||
extern PG_LocaleCategories *PGLC_current(PG_LocaleCategories * lc);
|
extern void PGLC_current(PG_LocaleCategories * lc);
|
||||||
extern PG_LocaleCategories *PGLC_setlocale(PG_LocaleCategories * lc);
|
|
||||||
|
|
||||||
/*------
|
/*------
|
||||||
* Return the POSIX lconv struct (contains number/money formatting information)
|
* Return the POSIX lconv struct (contains number/money formatting information)
|
||||||
* with locale information for *all* categories. Returned lconv is *independent*
|
* with locale information for all categories. Note that returned lconv
|
||||||
* on current locale catogories setting - in contrast to standard localeconv().
|
* does not depend on currently active category settings, but on external
|
||||||
|
* environment variables for locale.
|
||||||
*------
|
*------
|
||||||
*/
|
*/
|
||||||
extern struct lconv *PGLC_localeconv(void);
|
extern struct lconv *PGLC_localeconv(void);
|
||||||
|
|
Loading…
Reference in a new issue