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:
Tom Lane 2000-11-25 22:43:08 +00:00
parent 74dc04a034
commit e3269cab31
3 changed files with 64 additions and 53 deletions

View file

@ -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)

View file

@ -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 */

View file

@ -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);