From: Tom Lane Date: Thu, 5 Jan 2006 00:55:07 +0000 (+0000) Subject: Arrange to set the LC_XXX environment variables to match our locale setup. X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=7b373dff10fbfe531d85c32fd2777fa400fd5233;p=users%2Fbernd%2Fpostgres.git 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. --- diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index d4cd215f81..16aca9915c 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -41,6 +41,7 @@ #include "storage/spin.h" #include "utils/builtins.h" #include "utils/guc.h" +#include "utils/pg_locale.h" #include "utils/relcache.h" @@ -63,8 +64,13 @@ #endif #endif +#if defined(O_DSYNC) #if defined(OPEN_SYNC_FLAG) -#if defined(O_DSYNC) && (O_DSYNC != OPEN_SYNC_FLAG) +#if O_DSYNC != OPEN_SYNC_FLAG +#define OPEN_DATASYNC_FLAG O_DSYNC +#endif +#else /* !defined(OPEN_SYNC_FLAG) */ +/* Win32 only has O_DSYNC */ #define OPEN_DATASYNC_FLAG O_DSYNC #endif #endif @@ -79,7 +85,11 @@ #define DEFAULT_SYNC_METHOD SYNC_METHOD_FDATASYNC #define DEFAULT_SYNC_FLAGBIT 0 #else +#ifndef FSYNC_IS_WRITE_THROUGH #define DEFAULT_SYNC_METHOD_STR "fsync" +#else +#define DEFAULT_SYNC_METHOD_STR "fsync_writethrough" +#endif #define DEFAULT_SYNC_METHOD SYNC_METHOD_FSYNC #define DEFAULT_SYNC_FLAGBIT 0 #endif @@ -414,8 +424,8 @@ static char *readRecordBuf = NULL; static uint32 readRecordBufSize = 0; /* State information for XLOG reading */ -static XLogRecPtr ReadRecPtr; -static XLogRecPtr EndRecPtr; +static XLogRecPtr ReadRecPtr; /* start of last record read */ +static XLogRecPtr EndRecPtr; /* end+1 of last record read */ static XLogRecord *nextRecord = NULL; static TimeLineID lastPageTLI = 0; @@ -2494,6 +2504,37 @@ got_record:; record->xl_rmid, RecPtr->xlogid, RecPtr->xrecoff))); goto next_record_is_invalid; } + if (randAccess) + { + /* + * We can't exactly verify the prev-link, but surely it should be + * less than the record's own address. + */ + if (!XLByteLT(record->xl_prev, *RecPtr)) + { + ereport(emode, + (errmsg("record with incorrect prev-link %X/%X at %X/%X", + record->xl_prev.xlogid, record->xl_prev.xrecoff, + RecPtr->xlogid, RecPtr->xrecoff))); + goto next_record_is_invalid; + } + } + else + { + /* + * Record's prev-link should exactly match our previous location. + * This check guards against torn WAL pages where a stale but + * valid-looking WAL record starts on a sector boundary. + */ + if (!XLByteEQ(record->xl_prev, ReadRecPtr)) + { + ereport(emode, + (errmsg("record with incorrect prev-link %X/%X at %X/%X", + record->xl_prev.xlogid, record->xl_prev.xrecoff, + RecPtr->xlogid, RecPtr->xrecoff))); + goto next_record_is_invalid; + } + } /* * Compute total length of record including any appended backup @@ -3307,14 +3348,14 @@ ReadControlFile(void) " but the server was compiled with LOCALE_NAME_BUFLEN %d.", ControlFile->localeBuflen, LOCALE_NAME_BUFLEN), errhint("It looks like you need to recompile or initdb."))); - if (setlocale(LC_COLLATE, ControlFile->lc_collate) == NULL) + if (pg_perm_setlocale(LC_COLLATE, ControlFile->lc_collate) == NULL) ereport(FATAL, (errmsg("database files are incompatible with operating system"), errdetail("The database cluster was initialized with LC_COLLATE \"%s\"," " which is not recognized by setlocale().", ControlFile->lc_collate), errhint("It looks like you need to initdb or install locale support."))); - if (setlocale(LC_CTYPE, ControlFile->lc_ctype) == NULL) + if (pg_perm_setlocale(LC_CTYPE, ControlFile->lc_ctype) == NULL) ereport(FATAL, (errmsg("database files are incompatible with operating system"), errdetail("The database cluster was initialized with LC_CTYPE \"%s\"," @@ -5154,7 +5195,12 @@ assign_xlog_sync_method(const char *method, bool doit, GucSource source) int new_sync_method; int new_sync_bit; +#ifndef FSYNC_IS_WRITE_THROUGH if (pg_strcasecmp(method, "fsync") == 0) +#else + /* Win32 fsync() == _commit(), which writes through a write cache */ + if (pg_strcasecmp(method, "fsync_writethrough") == 0) +#endif { new_sync_method = SYNC_METHOD_FSYNC; new_sync_bit = 0; diff --git a/src/backend/main/main.c b/src/backend/main/main.c index 12a55bbc4c..778b263c4f 100644 --- a/src/backend/main/main.c +++ b/src/backend/main/main.c @@ -19,7 +19,6 @@ */ #include "postgres.h" -#include #include #include @@ -40,6 +39,7 @@ #include "postmaster/postmaster.h" #include "tcop/tcopprot.h" #include "utils/help_config.h" +#include "utils/pg_locale.h" #include "utils/ps_status.h" #ifdef WIN32 #include "libpq/pqsignal.h" @@ -167,30 +167,37 @@ main(int argc, char *argv[]) */ if ((env_locale = getenv("LC_COLLATE")) != NULL) - setlocale(LC_COLLATE, env_locale); + pg_perm_setlocale(LC_COLLATE, env_locale); else - setlocale(LC_COLLATE, ""); + pg_perm_setlocale(LC_COLLATE, ""); if ((env_locale = getenv("LC_CTYPE")) != NULL) - setlocale(LC_CTYPE, env_locale); + pg_perm_setlocale(LC_CTYPE, env_locale); else - setlocale(LC_CTYPE, ""); + pg_perm_setlocale(LC_CTYPE, ""); #else - setlocale(LC_COLLATE, ""); - setlocale(LC_CTYPE, ""); + pg_perm_setlocale(LC_COLLATE, ""); + pg_perm_setlocale(LC_CTYPE, ""); #endif #ifdef LC_MESSAGES - setlocale(LC_MESSAGES, ""); + pg_perm_setlocale(LC_MESSAGES, ""); #endif /* * We keep these set to "C" always, except transiently in pg_locale.c; * see that file for explanations. */ - 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"); /* * Skip permission checks if we're just trying to do --help or diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index fc172fe456..3839ed7b2d 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -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 */ @@ -123,49 +234,25 @@ locale_time_assign(const char *value, bool doit, GucSource source) const char * locale_messages_assign(const char *value, bool doit, GucSource source) { -#ifndef WIN32 /* * LC_MESSAGES category does not exist everywhere, but accept it * anyway + * + * On Windows, we can't even check the value, so the non-doit case + * is a no-op */ #ifdef LC_MESSAGES if (doit) { - if (!setlocale(LC_MESSAGES, value)) + if (!pg_perm_setlocale(LC_MESSAGES, value)) return NULL; } +#ifndef WIN32 else value = locale_xxx_assign(LC_MESSAGES, value, false, source); +#endif /* WIN32 */ #endif /* LC_MESSAGES */ return value; - -#else /* WIN32 */ - - /* - * Win32 does not have working setlocale() for LC_MESSAGES. We can only - * use environment variables to change it (per gettext FAQ). This - * means we can't actually check the supplied value, so always assume - * it's good. Also, ignore attempts to set to "", which really means - * "keep using the old value". (Actually it means "use the environment - * value", but we are too lazy to try to implement that exactly.) - */ - if (doit && value[0]) - { - /* - * We need to modify both the process environment and the cached - * version in msvcrt - */ - static char env[128]; - - if (!SetEnvironmentVariable("LC_MESSAGES", value)) - return NULL; - - snprintf(env, sizeof(env)-1, "LC_MESSAGES=%s", value); - if (_putenv(env)) - return NULL; - } - return value; -#endif /* WIN32 */ } @@ -196,6 +283,33 @@ lc_collate_is_c(void) } +/* + * We'd like to cache whether LC_CTYPE is C (or POSIX), so we can + * optimize a few code paths in various places. + */ +bool +lc_ctype_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_CTYPE, NULL); + if (!localeptr) + elog(ERROR, "invalid LC_CTYPE setting"); + + if (strcmp(localeptr, "C") == 0) + result = true; + else if (strcmp(localeptr, "POSIX") == 0) + result = true; + else + result = false; + return (bool) result; +} + + /* * Frees the malloced content of a struct lconv. (But not the struct * itself.) diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index 2a8225b61b..4841da276c 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -31,7 +31,10 @@ extern const char *locale_numeric_assign(const char *value, extern const char *locale_time_assign(const char *value, bool doit, GucSource source); +extern char *pg_perm_setlocale(int category, const char *locale); + extern bool lc_collate_is_c(void); +extern bool lc_ctype_is_c(void); /* * Return the POSIX lconv struct (contains number/money formatting