mirror of
https://github.com/postgres/postgres.git
synced 2025-07-11 10:01:57 +03:00
Avoid thread-safety problem in ecpglib.
ecpglib attempts to force the LC_NUMERIC locale to "C" while reading server output, to avoid problems with strtod() and related functions. Historically it's just issued setlocale() calls to do that, but that has major problems if we're in a threaded application. setlocale() itself is not required by POSIX to be thread-safe (and indeed is not, on recent OpenBSD). Moreover, its effects are process-wide, so that we could cause unexpected results in other threads, or another thread could change our setting. On platforms having uselocale(), which is required by POSIX:2008, we can avoid these problems by using uselocale() instead. Windows goes its own way as usual, but we can make it safe by using _configthreadlocale(). Platforms having neither continue to use the old code, but that should be pretty much nobody among current systems. This should get back-patched, but let's see what the buildfarm thinks of it first. Michael Meskes and Tom Lane; thanks also to Takayuki Tsunakawa. Discussion: https://postgr.es/m/31420.1547783697@sss.pgh.pa.us
This commit is contained in:
@ -102,7 +102,12 @@ free_statement(struct statement *stmt)
|
||||
free_variable(stmt->outlist);
|
||||
ecpg_free(stmt->command);
|
||||
ecpg_free(stmt->name);
|
||||
#ifdef HAVE_USELOCALE
|
||||
if (stmt->clocale)
|
||||
freelocale(stmt->clocale);
|
||||
#else
|
||||
ecpg_free(stmt->oldlocale);
|
||||
#endif
|
||||
ecpg_free(stmt);
|
||||
}
|
||||
|
||||
@ -1771,8 +1776,32 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
|
||||
|
||||
/*
|
||||
* Make sure we do NOT honor the locale for numeric input/output since the
|
||||
* database wants the standard decimal point
|
||||
* database wants the standard decimal point. If available, use
|
||||
* uselocale() for this because it's thread-safe. Windows doesn't have
|
||||
* that, but it does have _configthreadlocale().
|
||||
*/
|
||||
#ifdef HAVE_USELOCALE
|
||||
stmt->clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
|
||||
if (stmt->clocale == (locale_t) 0)
|
||||
{
|
||||
ecpg_do_epilogue(stmt);
|
||||
return false;
|
||||
}
|
||||
stmt->oldlocale = uselocale(stmt->clocale);
|
||||
if (stmt->oldlocale == (locale_t) 0)
|
||||
{
|
||||
ecpg_do_epilogue(stmt);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
#ifdef WIN32
|
||||
stmt->oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
||||
if (stmt->oldthreadlocale == -1)
|
||||
{
|
||||
ecpg_do_epilogue(stmt);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
stmt->oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
|
||||
if (stmt->oldlocale == NULL)
|
||||
{
|
||||
@ -1780,6 +1809,7 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
|
||||
return false;
|
||||
}
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_THREAD_SAFETY
|
||||
ecpg_pthreads_init();
|
||||
@ -1982,8 +2012,18 @@ ecpg_do_epilogue(struct statement *stmt)
|
||||
if (stmt == NULL)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_USELOCALE
|
||||
if (stmt->oldlocale != (locale_t) 0)
|
||||
uselocale(stmt->oldlocale);
|
||||
#else
|
||||
if (stmt->oldlocale)
|
||||
{
|
||||
setlocale(LC_NUMERIC, stmt->oldlocale);
|
||||
#ifdef WIN32
|
||||
_configthreadlocale(stmt->oldthreadlocale);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
free_statement(stmt);
|
||||
}
|
||||
|
Reference in New Issue
Block a user