mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-30 13:37:18 +02:00
ecpglib: call newlocale() once per process.
ecpglib has been calling it once per SQL query and once per EXEC SQL GET DESCRIPTOR. Instead, if newlocale() has not succeeded before, call it while establishing a connection. This mitigates three problems: - If newlocale() failed in EXEC SQL GET DESCRIPTOR, the command silently proceeded without the intended locale change. - On AIX, each newlocale()+freelocale() cycle leaked memory. - newlocale() CPU usage may have been nontrivial. Fail the connection attempt if newlocale() fails. Rearrange ecpg_do_prologue() to validate the connection before its uselocale(). The sort of program that may regress is one running in an environment where newlocale() fails. If that program establishes connections without running SQL statements, it will stop working in response to this change. I'm betting against the importance of such an ECPG use case. Most SQL execution (any using ECPGdo()) has long required newlocale() success, so there's little a connection could do without newlocale(). Back-patch to v10 (all supported versions). Reviewed by Tom Lane. Reported by Guillaume Lelarge. Discussion: https://postgr.es/m/20220101074055.GA54621@rfd.leadboat.com
This commit is contained in:
parent
b436047dc6
commit
b4d7e92bd5
@ -10,6 +10,10 @@
|
|||||||
#include "ecpgtype.h"
|
#include "ecpgtype.h"
|
||||||
#include "sqlca.h"
|
#include "sqlca.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_USELOCALE
|
||||||
|
locale_t ecpg_clocale;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_THREAD_SAFETY
|
#ifdef ENABLE_THREAD_SAFETY
|
||||||
static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static pthread_key_t actual_connection_key;
|
static pthread_key_t actual_connection_key;
|
||||||
@ -531,6 +535,42 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
|
|||||||
#ifdef ENABLE_THREAD_SAFETY
|
#ifdef ENABLE_THREAD_SAFETY
|
||||||
pthread_mutex_lock(&connections_mutex);
|
pthread_mutex_lock(&connections_mutex);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ... but first, make certain we have created ecpg_clocale. Rely on
|
||||||
|
* holding connections_mutex to ensure this is done by only one thread.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_USELOCALE
|
||||||
|
if (!ecpg_clocale)
|
||||||
|
{
|
||||||
|
ecpg_clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
|
||||||
|
if (!ecpg_clocale)
|
||||||
|
{
|
||||||
|
#ifdef ENABLE_THREAD_SAFETY
|
||||||
|
pthread_mutex_unlock(&connections_mutex);
|
||||||
|
#endif
|
||||||
|
ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
|
||||||
|
ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
|
||||||
|
if (host)
|
||||||
|
ecpg_free(host);
|
||||||
|
if (port)
|
||||||
|
ecpg_free(port);
|
||||||
|
if (options)
|
||||||
|
ecpg_free(options);
|
||||||
|
if (realname)
|
||||||
|
ecpg_free(realname);
|
||||||
|
if (dbname)
|
||||||
|
ecpg_free(dbname);
|
||||||
|
if (conn_keywords)
|
||||||
|
ecpg_free(conn_keywords);
|
||||||
|
if (conn_values)
|
||||||
|
ecpg_free(conn_values);
|
||||||
|
free(this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (connection_name != NULL)
|
if (connection_name != NULL)
|
||||||
this->name = ecpg_strdup(connection_name, lineno);
|
this->name = ecpg_strdup(connection_name, lineno);
|
||||||
else
|
else
|
||||||
|
@ -486,9 +486,16 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
|
|||||||
/* since the database gives the standard decimal point */
|
/* since the database gives the standard decimal point */
|
||||||
/* (see comments in execute.c) */
|
/* (see comments in execute.c) */
|
||||||
#ifdef HAVE_USELOCALE
|
#ifdef HAVE_USELOCALE
|
||||||
stmt.clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
|
|
||||||
if (stmt.clocale != (locale_t) 0)
|
/*
|
||||||
stmt.oldlocale = uselocale(stmt.clocale);
|
* To get here, the above PQnfields() test must have found nonzero
|
||||||
|
* fields. One needs a connection to create such a descriptor. (EXEC
|
||||||
|
* SQL SET DESCRIPTOR can populate the descriptor's "items", but it
|
||||||
|
* can't change the descriptor's PQnfields().) Any successful
|
||||||
|
* connection initializes ecpg_clocale.
|
||||||
|
*/
|
||||||
|
Assert(ecpg_clocale);
|
||||||
|
stmt.oldlocale = uselocale(ecpg_clocale);
|
||||||
#else
|
#else
|
||||||
#ifdef HAVE__CONFIGTHREADLOCALE
|
#ifdef HAVE__CONFIGTHREADLOCALE
|
||||||
stmt.oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
stmt.oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
||||||
@ -504,8 +511,6 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
|
|||||||
#ifdef HAVE_USELOCALE
|
#ifdef HAVE_USELOCALE
|
||||||
if (stmt.oldlocale != (locale_t) 0)
|
if (stmt.oldlocale != (locale_t) 0)
|
||||||
uselocale(stmt.oldlocale);
|
uselocale(stmt.oldlocale);
|
||||||
if (stmt.clocale)
|
|
||||||
freelocale(stmt.clocale);
|
|
||||||
#else
|
#else
|
||||||
if (stmt.oldlocale)
|
if (stmt.oldlocale)
|
||||||
{
|
{
|
||||||
|
@ -59,6 +59,10 @@ struct ECPGtype_information_cache
|
|||||||
enum ARRAY_TYPE isarray;
|
enum ARRAY_TYPE isarray;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_USELOCALE
|
||||||
|
extern locale_t ecpg_clocale; /* LC_NUMERIC=C */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* structure to store one statement */
|
/* structure to store one statement */
|
||||||
struct statement
|
struct statement
|
||||||
{
|
{
|
||||||
@ -73,7 +77,6 @@ struct statement
|
|||||||
struct variable *inlist;
|
struct variable *inlist;
|
||||||
struct variable *outlist;
|
struct variable *outlist;
|
||||||
#ifdef HAVE_USELOCALE
|
#ifdef HAVE_USELOCALE
|
||||||
locale_t clocale;
|
|
||||||
locale_t oldlocale;
|
locale_t oldlocale;
|
||||||
#else
|
#else
|
||||||
char *oldlocale;
|
char *oldlocale;
|
||||||
|
@ -101,10 +101,7 @@ free_statement(struct statement *stmt)
|
|||||||
free_variable(stmt->outlist);
|
free_variable(stmt->outlist);
|
||||||
ecpg_free(stmt->command);
|
ecpg_free(stmt->command);
|
||||||
ecpg_free(stmt->name);
|
ecpg_free(stmt->name);
|
||||||
#ifdef HAVE_USELOCALE
|
#ifndef HAVE_USELOCALE
|
||||||
if (stmt->clocale)
|
|
||||||
freelocale(stmt->clocale);
|
|
||||||
#else
|
|
||||||
ecpg_free(stmt->oldlocale);
|
ecpg_free(stmt->oldlocale);
|
||||||
#endif
|
#endif
|
||||||
ecpg_free(stmt);
|
ecpg_free(stmt);
|
||||||
@ -1966,6 +1963,15 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_THREAD_SAFETY
|
||||||
|
ecpg_pthreads_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
con = ecpg_get_connection(connection_name);
|
||||||
|
|
||||||
|
if (!ecpg_init(con, connection_name, lineno))
|
||||||
|
return false;
|
||||||
|
|
||||||
stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
|
stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
|
||||||
|
|
||||||
if (stmt == NULL)
|
if (stmt == NULL)
|
||||||
@ -1980,13 +1986,13 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
|
|||||||
* treat that situation as if the function doesn't exist.
|
* treat that situation as if the function doesn't exist.
|
||||||
*/
|
*/
|
||||||
#ifdef HAVE_USELOCALE
|
#ifdef HAVE_USELOCALE
|
||||||
stmt->clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
|
|
||||||
if (stmt->clocale == (locale_t) 0)
|
/*
|
||||||
{
|
* Since ecpg_init() succeeded, we have a connection. Any successful
|
||||||
ecpg_do_epilogue(stmt);
|
* connection initializes ecpg_clocale.
|
||||||
return false;
|
*/
|
||||||
}
|
Assert(ecpg_clocale);
|
||||||
stmt->oldlocale = uselocale(stmt->clocale);
|
stmt->oldlocale = uselocale(ecpg_clocale);
|
||||||
if (stmt->oldlocale == (locale_t) 0)
|
if (stmt->oldlocale == (locale_t) 0)
|
||||||
{
|
{
|
||||||
ecpg_do_epilogue(stmt);
|
ecpg_do_epilogue(stmt);
|
||||||
@ -2005,18 +2011,6 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
|
|||||||
setlocale(LC_NUMERIC, "C");
|
setlocale(LC_NUMERIC, "C");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_THREAD_SAFETY
|
|
||||||
ecpg_pthreads_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
con = ecpg_get_connection(connection_name);
|
|
||||||
|
|
||||||
if (!ecpg_init(con, connection_name, lineno))
|
|
||||||
{
|
|
||||||
ecpg_do_epilogue(stmt);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If statement type is ECPGst_prepnormal we are supposed to prepare the
|
* If statement type is ECPGst_prepnormal we are supposed to prepare the
|
||||||
* statement before executing them
|
* statement before executing them
|
||||||
|
Loading…
Reference in New Issue
Block a user