From 040d3e63a7eb2840e957a036209496492febd5f7 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 5 Jan 2006 00:55:36 +0000 Subject: [PATCH] Arrange to set the LC_XXX environment variables to match our locale setup. Back-patch of previous fix in HEAD for plperl-vs-locale issue. --- src/backend/access/transam/xlog.c | 7 +- src/backend/main/main.c | 22 ++++-- src/backend/utils/adt/pg_locale.c | 123 ++++++++++++++++++++++++++++-- src/include/utils/pg_locale.h | 4 +- 4 files changed, 139 insertions(+), 17 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 169d5037ba0..927d723e09a 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.109.2.5 2005/05/31 19:11:14 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.109.2.6 2006/01/05 00:55:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,6 +37,7 @@ #include "storage/sinval.h" #include "storage/spin.h" #include "utils/builtins.h" +#include "utils/pg_locale.h" #include "utils/relcache.h" #include "miscadmin.h" @@ -2283,13 +2284,13 @@ ReadControlFile(void) "\tIt looks like you need to initdb.", ControlFile->localeBuflen, LOCALE_NAME_BUFLEN); - if (setlocale(LC_COLLATE, ControlFile->lc_collate) == NULL) + if (pg_perm_setlocale(LC_COLLATE, ControlFile->lc_collate) == NULL) elog(PANIC, "The database cluster was initialized with LC_COLLATE '%s',\n" "\twhich is not recognized by setlocale().\n" "\tIt looks like you need to initdb.", ControlFile->lc_collate); - if (setlocale(LC_CTYPE, ControlFile->lc_ctype) == NULL) + if (pg_perm_setlocale(LC_CTYPE, ControlFile->lc_ctype) == NULL) elog(PANIC, "The database cluster was initialized with LC_CTYPE '%s',\n" "\twhich is not recognized by setlocale().\n" diff --git a/src/backend/main/main.c b/src/backend/main/main.c index 163d57d0df2..fa494658c4e 100644 --- a/src/backend/main/main.c +++ b/src/backend/main/main.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/main/main.c,v 1.55 2002/10/31 22:37:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/main/main.c,v 1.55.2.1 2006/01/05 00:55:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,6 +38,7 @@ #include "miscadmin.h" #include "bootstrap/bootstrap.h" #include "tcop/tcopprot.h" +#include "utils/pg_locale.h" #include "utils/ps_status.h" @@ -130,19 +131,26 @@ main(int argc, char *argv[]) * get already localized behavior during startup (e.g., error * messages). */ - setlocale(LC_COLLATE, ""); - setlocale(LC_CTYPE, ""); + pg_perm_setlocale(LC_COLLATE, ""); + pg_perm_setlocale(LC_CTYPE, ""); #ifdef LC_MESSAGES - setlocale(LC_MESSAGES, ""); + pg_perm_setlocale(LC_MESSAGES, ""); #endif /* * We don't use these during startup. See also pg_locale.c about why * these are set to "C". */ - setlocale(LC_MONETARY, "C"); - setlocale(LC_NUMERIC, "C"); - setlocale(LC_TIME, "C"); + pg_perm_setlocale(LC_MONETARY, "C"); + pg_perm_setlocale(LC_NUMERIC, "C"); + pg_perm_setlocale(LC_TIME, "C"); + + /* + * Now that we have absorbed as much as we wish to from the locale + * environment, remove any LC_ALL setting, so that the environment + * variables installed by pg_perm_setlocale have force. + */ + unsetenv("LC_ALL"); #ifdef ENABLE_NLS bindtextdomain("postgres", LOCALEDIR); diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index 2ebea73036a..f1a362a9626 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -4,7 +4,7 @@ * * Portions Copyright (c) 2002, PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.20 2002/10/18 20:44:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.20.2.1 2006/01/05 00:55:36 tgl Exp $ * *----------------------------------------------------------------------- */ @@ -50,13 +50,10 @@ #include +#include "catalog/pg_control.h" #include "utils/pg_locale.h" -/* indicated whether locale information cache is valid */ -static bool CurrentLocaleConvValid = false; - - /* GUC storage area */ char *locale_messages; @@ -64,6 +61,120 @@ char *locale_monetary; char *locale_numeric; char *locale_time; +/* indicates whether locale information cache is valid */ +static bool CurrentLocaleConvValid = false; + +/* Environment variable storage area */ + +#define LC_ENV_BUFSIZE (LOCALE_NAME_BUFLEN + 20) + +static char lc_collate_envbuf[LC_ENV_BUFSIZE]; +static char lc_ctype_envbuf[LC_ENV_BUFSIZE]; +#ifdef LC_MESSAGES +static char lc_messages_envbuf[LC_ENV_BUFSIZE]; +#endif +static char lc_monetary_envbuf[LC_ENV_BUFSIZE]; +static char lc_numeric_envbuf[LC_ENV_BUFSIZE]; +static char lc_time_envbuf[LC_ENV_BUFSIZE]; + + +/* + * pg_perm_setlocale + * + * This is identical to the libc function setlocale(), with the addition + * that if the operation is successful, the corresponding LC_XXX environment + * variable is set to match. By setting the environment variable, we ensure + * that any subsequent use of setlocale(..., "") will preserve the settings + * made through this routine. Of course, LC_ALL must also be unset to fully + * ensure that, but that has to be done elsewhere after all the individual + * LC_XXX variables have been set correctly. (Thank you Perl for making this + * kluge necessary.) + */ +char * +pg_perm_setlocale(int category, const char *locale) +{ + char *result; + const char *envvar; + char *envbuf; + +#ifndef WIN32 + result = setlocale(category, locale); +#else + /* + * On Windows, setlocale(LC_MESSAGES) does not work, so just assume + * that the given value is good and set it in the environment variables. + * We must ignore attempts to set to "", which means "keep using the + * old environment value". + */ +#ifdef LC_MESSAGES + if (category == LC_MESSAGES) + { + result = (char *) locale; + if (locale == NULL || locale[0] == '\0') + return result; + } + else +#endif + result = setlocale(category, locale); +#endif /* WIN32 */ + + if (result == NULL) + return result; /* fall out immediately on failure */ + + switch (category) + { + case LC_COLLATE: + envvar = "LC_COLLATE"; + envbuf = lc_collate_envbuf; + break; + case LC_CTYPE: + envvar = "LC_CTYPE"; + envbuf = lc_ctype_envbuf; + break; +#ifdef LC_MESSAGES + case LC_MESSAGES: + envvar = "LC_MESSAGES"; + envbuf = lc_messages_envbuf; + break; +#endif + case LC_MONETARY: + envvar = "LC_MONETARY"; + envbuf = lc_monetary_envbuf; + break; + case LC_NUMERIC: + envvar = "LC_NUMERIC"; + envbuf = lc_numeric_envbuf; + break; + case LC_TIME: + envvar = "LC_TIME"; + envbuf = lc_time_envbuf; + break; + default: + elog(FATAL, "unrecognized LC category: %d", category); + envvar = NULL; /* keep compiler quiet */ + envbuf = NULL; + break; + } + + snprintf(envbuf, LC_ENV_BUFSIZE-1, "%s=%s", envvar, result); + +#ifndef WIN32 + if (putenv(envbuf)) + return NULL; +#else + /* + * On Windows, we need to modify both the process environment and the + * cached version in msvcrt + */ + if (!SetEnvironmentVariable(envvar, result)) + return NULL; + if (_putenv(envbuf)) + return NULL; +#endif + + return result; +} + /* GUC assign hooks */ @@ -130,7 +241,7 @@ locale_messages_assign(const char *value, bool doit, bool interactive) #ifdef LC_MESSAGES if (doit) { - if (!setlocale(LC_MESSAGES, value)) + if (!pg_perm_setlocale(LC_MESSAGES, value)) return NULL; } else diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index 6c7b30758b5..5d06c60dbf0 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -2,7 +2,7 @@ * * PostgreSQL locale utilities * - * $Id: pg_locale.h,v 1.14 2002/09/04 20:31:45 momjian Exp $ + * $Id: pg_locale.h,v 1.14.2.1 2006/01/05 00:55:36 tgl Exp $ * * Copyright (c) 2002, PostgreSQL Global Development Group * @@ -28,6 +28,8 @@ extern const char *locale_numeric_assign(const char *value, extern const char *locale_time_assign(const char *value, bool doit, bool interactive); +extern char *pg_perm_setlocale(int category, const char *locale); + extern bool lc_collate_is_c(void); /*