Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b4d7e92

Browse files
committed
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
1 parent b436047 commit b4d7e92

File tree

4 files changed

+71
-29
lines changed

4 files changed

+71
-29
lines changed

src/interfaces/ecpg/ecpglib/connect.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
#include "ecpgtype.h"
1111
#include "sqlca.h"
1212

13+
#ifdef HAVE_USELOCALE
14+
locale_t ecpg_clocale;
15+
#endif
16+
1317
#ifdef ENABLE_THREAD_SAFETY
1418
static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER;
1519
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
531535
#ifdef ENABLE_THREAD_SAFETY
532536
pthread_mutex_lock(&connections_mutex);
533537
#endif
538+
539+
/*
540+
* ... but first, make certain we have created ecpg_clocale. Rely on
541+
* holding connections_mutex to ensure this is done by only one thread.
542+
*/
543+
#ifdef HAVE_USELOCALE
544+
if (!ecpg_clocale)
545+
{
546+
ecpg_clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
547+
if (!ecpg_clocale)
548+
{
549+
#ifdef ENABLE_THREAD_SAFETY
550+
pthread_mutex_unlock(&connections_mutex);
551+
#endif
552+
ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
553+
ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
554+
if (host)
555+
ecpg_free(host);
556+
if (port)
557+
ecpg_free(port);
558+
if (options)
559+
ecpg_free(options);
560+
if (realname)
561+
ecpg_free(realname);
562+
if (dbname)
563+
ecpg_free(dbname);
564+
if (conn_keywords)
565+
ecpg_free(conn_keywords);
566+
if (conn_values)
567+
ecpg_free(conn_values);
568+
free(this);
569+
return false;
570+
}
571+
}
572+
#endif
573+
534574
if (connection_name != NULL)
535575
this->name = ecpg_strdup(connection_name, lineno);
536576
else

src/interfaces/ecpg/ecpglib/descriptor.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -486,9 +486,16 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
486486
/* since the database gives the standard decimal point */
487487
/* (see comments in execute.c) */
488488
#ifdef HAVE_USELOCALE
489-
stmt.clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
490-
if (stmt.clocale != (locale_t) 0)
491-
stmt.oldlocale = uselocale(stmt.clocale);
489+
490+
/*
491+
* To get here, the above PQnfields() test must have found nonzero
492+
* fields. One needs a connection to create such a descriptor. (EXEC
493+
* SQL SET DESCRIPTOR can populate the descriptor's "items", but it
494+
* can't change the descriptor's PQnfields().) Any successful
495+
* connection initializes ecpg_clocale.
496+
*/
497+
Assert(ecpg_clocale);
498+
stmt.oldlocale = uselocale(ecpg_clocale);
492499
#else
493500
#ifdef HAVE__CONFIGTHREADLOCALE
494501
stmt.oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
@@ -504,8 +511,6 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
504511
#ifdef HAVE_USELOCALE
505512
if (stmt.oldlocale != (locale_t) 0)
506513
uselocale(stmt.oldlocale);
507-
if (stmt.clocale)
508-
freelocale(stmt.clocale);
509514
#else
510515
if (stmt.oldlocale)
511516
{

src/interfaces/ecpg/ecpglib/ecpglib_extern.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ struct ECPGtype_information_cache
5959
enum ARRAY_TYPE isarray;
6060
};
6161

62+
#ifdef HAVE_USELOCALE
63+
extern locale_t ecpg_clocale; /* LC_NUMERIC=C */
64+
#endif
65+
6266
/* structure to store one statement */
6367
struct statement
6468
{
@@ -73,7 +77,6 @@ struct statement
7377
struct variable *inlist;
7478
struct variable *outlist;
7579
#ifdef HAVE_USELOCALE
76-
locale_t clocale;
7780
locale_t oldlocale;
7881
#else
7982
char *oldlocale;

src/interfaces/ecpg/ecpglib/execute.c

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,7 @@ free_statement(struct statement *stmt)
101101
free_variable(stmt->outlist);
102102
ecpg_free(stmt->command);
103103
ecpg_free(stmt->name);
104-
#ifdef HAVE_USELOCALE
105-
if (stmt->clocale)
106-
freelocale(stmt->clocale);
107-
#else
104+
#ifndef HAVE_USELOCALE
108105
ecpg_free(stmt->oldlocale);
109106
#endif
110107
ecpg_free(stmt);
@@ -1966,6 +1963,15 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
19661963
return false;
19671964
}
19681965

1966+
#ifdef ENABLE_THREAD_SAFETY
1967+
ecpg_pthreads_init();
1968+
#endif
1969+
1970+
con = ecpg_get_connection(connection_name);
1971+
1972+
if (!ecpg_init(con, connection_name, lineno))
1973+
return false;
1974+
19691975
stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
19701976

19711977
if (stmt == NULL)
@@ -1980,13 +1986,13 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
19801986
* treat that situation as if the function doesn't exist.
19811987
*/
19821988
#ifdef HAVE_USELOCALE
1983-
stmt->clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
1984-
if (stmt->clocale == (locale_t) 0)
1985-
{
1986-
ecpg_do_epilogue(stmt);
1987-
return false;
1988-
}
1989-
stmt->oldlocale = uselocale(stmt->clocale);
1989+
1990+
/*
1991+
* Since ecpg_init() succeeded, we have a connection. Any successful
1992+
* connection initializes ecpg_clocale.
1993+
*/
1994+
Assert(ecpg_clocale);
1995+
stmt->oldlocale = uselocale(ecpg_clocale);
19901996
if (stmt->oldlocale == (locale_t) 0)
19911997
{
19921998
ecpg_do_epilogue(stmt);
@@ -2005,18 +2011,6 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
20052011
setlocale(LC_NUMERIC, "C");
20062012
#endif
20072013

2008-
#ifdef ENABLE_THREAD_SAFETY
2009-
ecpg_pthreads_init();
2010-
#endif
2011-
2012-
con = ecpg_get_connection(connection_name);
2013-
2014-
if (!ecpg_init(con, connection_name, lineno))
2015-
{
2016-
ecpg_do_epilogue(stmt);
2017-
return false;
2018-
}
2019-
20202014
/*
20212015
* If statement type is ECPGst_prepnormal we are supposed to prepare the
20222016
* statement before executing them

0 commit comments

Comments
 (0)