Skip to content

Commit 5e0b8f3

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 f7b69b1 commit 5e0b8f3

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 "ecpglib_extern.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
@@ -491,9 +491,16 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
491491
/* since the database gives the standard decimal point */
492492
/* (see comments in execute.c) */
493493
#ifdef HAVE_USELOCALE
494-
stmt.clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
495-
if (stmt.clocale != (locale_t) 0)
496-
stmt.oldlocale = uselocale(stmt.clocale);
494+
495+
/*
496+
* To get here, the above PQnfields() test must have found nonzero
497+
* fields. One needs a connection to create such a descriptor. (EXEC
498+
* SQL SET DESCRIPTOR can populate the descriptor's "items", but it
499+
* can't change the descriptor's PQnfields().) Any successful
500+
* connection initializes ecpg_clocale.
501+
*/
502+
Assert(ecpg_clocale);
503+
stmt.oldlocale = uselocale(ecpg_clocale);
497504
#else
498505
#ifdef HAVE__CONFIGTHREADLOCALE
499506
stmt.oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
@@ -509,8 +516,6 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
509516
#ifdef HAVE_USELOCALE
510517
if (stmt.oldlocale != (locale_t) 0)
511518
uselocale(stmt.oldlocale);
512-
if (stmt.clocale)
513-
freelocale(stmt.clocale);
514519
#else
515520
if (stmt.oldlocale)
516521
{

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
@@ -102,10 +102,7 @@ free_statement(struct statement *stmt)
102102
free_variable(stmt->outlist);
103103
ecpg_free(stmt->command);
104104
ecpg_free(stmt->name);
105-
#ifdef HAVE_USELOCALE
106-
if (stmt->clocale)
107-
freelocale(stmt->clocale);
108-
#else
105+
#ifndef HAVE_USELOCALE
109106
ecpg_free(stmt->oldlocale);
110107
#endif
111108
ecpg_free(stmt);
@@ -1969,6 +1966,15 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
19691966
return false;
19701967
}
19711968

1969+
#ifdef ENABLE_THREAD_SAFETY
1970+
ecpg_pthreads_init();
1971+
#endif
1972+
1973+
con = ecpg_get_connection(connection_name);
1974+
1975+
if (!ecpg_init(con, connection_name, lineno))
1976+
return false;
1977+
19721978
stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
19731979

19741980
if (stmt == NULL)
@@ -1983,13 +1989,13 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
19831989
* treat that situation as if the function doesn't exist.
19841990
*/
19851991
#ifdef HAVE_USELOCALE
1986-
stmt->clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
1987-
if (stmt->clocale == (locale_t) 0)
1988-
{
1989-
ecpg_do_epilogue(stmt);
1990-
return false;
1991-
}
1992-
stmt->oldlocale = uselocale(stmt->clocale);
1992+
1993+
/*
1994+
* Since ecpg_init() succeeded, we have a connection. Any successful
1995+
* connection initializes ecpg_clocale.
1996+
*/
1997+
Assert(ecpg_clocale);
1998+
stmt->oldlocale = uselocale(ecpg_clocale);
19931999
if (stmt->oldlocale == (locale_t) 0)
19942000
{
19952001
ecpg_do_epilogue(stmt);
@@ -2008,18 +2014,6 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
20082014
setlocale(LC_NUMERIC, "C");
20092015
#endif
20102016

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

0 commit comments

Comments
 (0)