mirror of
https://github.com/postgres/postgres.git
synced 2025-07-14 08:21:07 +03:00
Locale support is on by default. The choice of locale is done in initdb
and/or with GUC variables.
This commit is contained in:
@ -1,173 +1,144 @@
|
||||
/* -----------------------------------------------------------------------
|
||||
* pg_locale.c
|
||||
/*-----------------------------------------------------------------------
|
||||
*
|
||||
* The PostgreSQL locale utils.
|
||||
* PostgreSQL locale utilities
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.16 2002/04/03 05:39:31 petere Exp $
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.15 2002/03/06 06:10:14 momjian Exp $
|
||||
* Portions Copyright (c) 2002, PostgreSQL Global Development Group
|
||||
*
|
||||
* Portions Copyright (c) 1999-2000, PostgreSQL Global Development Group
|
||||
*
|
||||
* Karel Zak
|
||||
*
|
||||
* -----------------------------------------------------------------------
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#ifdef USE_LOCALE
|
||||
|
||||
#include "utils/pg_locale.h"
|
||||
#include <locale.h>
|
||||
|
||||
#include "utils/pg_locale.h"
|
||||
|
||||
/* #define DEBUG_LOCALE_UTILS */
|
||||
/* GUC storage area */
|
||||
|
||||
char * locale_messages;
|
||||
char * locale_monetary;
|
||||
char * locale_numeric;
|
||||
char * locale_time;
|
||||
|
||||
static bool CurrentLocaleConvValid = false;
|
||||
static struct lconv CurrentLocaleConv;
|
||||
/* GUC parse hooks */
|
||||
|
||||
|
||||
static void PGLC_setlocale(PG_LocaleCategories *lc);
|
||||
|
||||
/*------
|
||||
* Frees memory used in PG_LocaleCategories -- this memory is
|
||||
* allocated in PGLC_current().
|
||||
*------
|
||||
*/
|
||||
void
|
||||
PGLC_free_categories(PG_LocaleCategories *lc)
|
||||
{
|
||||
if (lc->lc_ctype)
|
||||
pfree(lc->lc_ctype);
|
||||
if (lc->lc_numeric)
|
||||
pfree(lc->lc_numeric);
|
||||
if (lc->lc_time)
|
||||
pfree(lc->lc_time);
|
||||
if (lc->lc_collate)
|
||||
pfree(lc->lc_collate);
|
||||
if (lc->lc_monetary);
|
||||
pfree(lc->lc_monetary);
|
||||
#ifdef LC_MESSAGES
|
||||
if (lc->lc_messages)
|
||||
pfree(lc->lc_messages);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*------
|
||||
* Return in PG_LocaleCategories the current locale settings.
|
||||
*
|
||||
* NB: strings are allocated in the current memory context!
|
||||
*------
|
||||
*/
|
||||
void
|
||||
PGLC_current(PG_LocaleCategories *lc)
|
||||
{
|
||||
lc->lang = getenv("LANG");
|
||||
|
||||
lc->lc_ctype = pstrdup(setlocale(LC_CTYPE, NULL));
|
||||
lc->lc_numeric = pstrdup(setlocale(LC_NUMERIC, NULL));
|
||||
lc->lc_time = pstrdup(setlocale(LC_TIME, NULL));
|
||||
lc->lc_collate = pstrdup(setlocale(LC_COLLATE, NULL));
|
||||
lc->lc_monetary = pstrdup(setlocale(LC_MONETARY, NULL));
|
||||
#ifdef LC_MESSAGES
|
||||
lc->lc_messages = pstrdup(setlocale(LC_MESSAGES, NULL));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_LOCALE_UTILS
|
||||
|
||||
/*------
|
||||
* Print a PG_LocaleCategories struct as DEBUG
|
||||
*------
|
||||
*/
|
||||
static void
|
||||
PGLC_debug_lc(PG_LocaleCategories *lc)
|
||||
bool locale_messages_check(const char *proposed)
|
||||
{
|
||||
#ifdef LC_MESSAGES
|
||||
elog(LOG, "CURRENT LOCALE ENVIRONMENT:\n\nLANG: \t%s\nLC_CTYPE:\t%s\nLC_NUMERIC:\t%s\nLC_TIME:\t%s\nLC_COLLATE:\t%s\nLC_MONETARY:\t%s\nLC_MESSAGES:\t%s\n",
|
||||
lc->lang,
|
||||
lc->lc_ctype,
|
||||
lc->lc_numeric,
|
||||
lc->lc_time,
|
||||
lc->lc_collate,
|
||||
lc->lc_monetary,
|
||||
lc->lc_messages);
|
||||
return chklocale(LC_MESSAGES, proposed);
|
||||
#else
|
||||
elog(LOG, "CURRENT LOCALE ENVIRONMENT:\n\nLANG: \t%s\nLC_CTYPE:\t%s\nLC_NUMERIC:\t%s\nLC_TIME:\t%s\nLC_COLLATE:\t%s\nLC_MONETARY:\t%s\n",
|
||||
lc->lang,
|
||||
lc->lc_ctype,
|
||||
lc->lc_numeric,
|
||||
lc->lc_time,
|
||||
lc->lc_collate,
|
||||
lc->lc_monetary);
|
||||
/* We return true here so LC_MESSAGES can be set in the
|
||||
configuration file on every system. */
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/*------
|
||||
* 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.
|
||||
*------
|
||||
*/
|
||||
static void
|
||||
PGLC_setlocale(PG_LocaleCategories *lc)
|
||||
bool locale_monetary_check(const char *proposed)
|
||||
{
|
||||
if (!setlocale(LC_COLLATE, lc->lc_collate))
|
||||
elog(WARNING, "pg_setlocale(): 'LC_COLLATE=%s' cannot be honored.",
|
||||
lc->lc_collate);
|
||||
return chklocale(LC_MONETARY, proposed);
|
||||
}
|
||||
|
||||
if (!setlocale(LC_CTYPE, lc->lc_ctype))
|
||||
elog(WARNING, "pg_setlocale(): 'LC_CTYPE=%s' cannot be honored.",
|
||||
lc->lc_ctype);
|
||||
bool locale_numeric_check(const char *proposed)
|
||||
{
|
||||
return chklocale(LC_NUMERIC, proposed);
|
||||
}
|
||||
|
||||
if (!setlocale(LC_NUMERIC, lc->lc_numeric))
|
||||
elog(WARNING, "pg_setlocale(): 'LC_NUMERIC=%s' cannot be honored.",
|
||||
lc->lc_numeric);
|
||||
bool locale_time_check(const char *proposed)
|
||||
{
|
||||
return chklocale(LC_TIME, proposed);
|
||||
}
|
||||
|
||||
if (!setlocale(LC_TIME, lc->lc_time))
|
||||
elog(WARNING, "pg_setlocale(): 'LC_TIME=%s' cannot be honored.",
|
||||
lc->lc_time);
|
||||
|
||||
if (!setlocale(LC_MONETARY, lc->lc_monetary))
|
||||
elog(WARNING, "pg_setlocale(): 'LC_MONETARY=%s' cannot be honored.",
|
||||
lc->lc_monetary);
|
||||
/* GUC assign hooks */
|
||||
|
||||
void locale_messages_assign(const char *value)
|
||||
{
|
||||
#ifdef LC_MESSAGES
|
||||
if (!setlocale(LC_MESSAGES, lc->lc_messages))
|
||||
elog(WARNING, "pg_setlocale(): 'LC_MESSAGES=%s' cannot be honored.",
|
||||
lc->lc_messages);
|
||||
setlocale(LC_MESSAGES, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*------
|
||||
* Return the POSIX lconv struct (contains number/money formatting information)
|
||||
* with locale information for all categories. Note that returned lconv
|
||||
* does not depend on currently active category settings, but on external
|
||||
* environment variables for locale.
|
||||
*------
|
||||
void locale_monetary_assign(const char *value)
|
||||
{
|
||||
setlocale(LC_MONETARY, value);
|
||||
}
|
||||
|
||||
void locale_numeric_assign(const char *value)
|
||||
{
|
||||
setlocale(LC_NUMERIC, value);
|
||||
}
|
||||
|
||||
void locale_time_assign(const char *value)
|
||||
{
|
||||
setlocale(LC_TIME, value);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns true if the proposed string represents a valid locale of
|
||||
* the given category. This is probably pretty slow, but it's not
|
||||
* called in critical places.
|
||||
*/
|
||||
bool
|
||||
chklocale(int category, const char *proposed)
|
||||
{
|
||||
char *save;
|
||||
|
||||
save = setlocale(category, NULL);
|
||||
if (!save)
|
||||
return false;
|
||||
|
||||
if (!setlocale(category, proposed))
|
||||
return false;
|
||||
|
||||
setlocale(category, save);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We'd like to cache whether LC_COLLATE is C (or POSIX), so we can
|
||||
* optimize a few code paths in various places.
|
||||
*/
|
||||
bool
|
||||
lc_collate_is_c(void)
|
||||
{
|
||||
/* Cache result so we only have to compute it once */
|
||||
static int result = -1;
|
||||
char *localeptr;
|
||||
|
||||
if (result >= 0)
|
||||
return (bool) result;
|
||||
localeptr = setlocale(LC_COLLATE, NULL);
|
||||
if (!localeptr)
|
||||
elog(PANIC, "Invalid LC_COLLATE setting");
|
||||
|
||||
if (strcmp(localeptr, "C") == 0)
|
||||
result = true;
|
||||
else if (strcmp(localeptr, "POSIX") == 0)
|
||||
result = true;
|
||||
else
|
||||
result = false;
|
||||
return (bool) result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Return the POSIX lconv struct (contains number/money formatting
|
||||
* information) with locale information for all categories.
|
||||
*/
|
||||
struct lconv *
|
||||
PGLC_localeconv(void)
|
||||
{
|
||||
PG_LocaleCategories lc;
|
||||
struct lconv *extlconv;
|
||||
static bool CurrentLocaleConvValid = false;
|
||||
static struct lconv CurrentLocaleConv;
|
||||
|
||||
/* Did we do it already? */
|
||||
if (CurrentLocaleConvValid)
|
||||
return &CurrentLocaleConv;
|
||||
|
||||
/* Save current locale setting to lc */
|
||||
PGLC_current(&lc);
|
||||
|
||||
/* Set all locale categories based on postmaster's environment vars */
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
/* Get formatting information for the external environment */
|
||||
extlconv = localeconv();
|
||||
|
||||
@ -187,14 +158,6 @@ PGLC_localeconv(void)
|
||||
CurrentLocaleConv.negative_sign = strdup(extlconv->negative_sign);
|
||||
CurrentLocaleConv.positive_sign = strdup(extlconv->positive_sign);
|
||||
|
||||
/* Restore Postgres' internal locale settings */
|
||||
PGLC_setlocale(&lc);
|
||||
|
||||
/* Deallocate category settings allocated in PGLC_current() */
|
||||
PGLC_free_categories(&lc);
|
||||
|
||||
CurrentLocaleConvValid = true;
|
||||
return &CurrentLocaleConv;
|
||||
}
|
||||
|
||||
#endif /* USE_LOCALE */
|
||||
|
Reference in New Issue
Block a user