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

Commit 7a28f7c

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 a2b0719 commit 7a28f7c

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;
@@ -504,6 +508,42 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
504508
#ifdef ENABLE_THREAD_SAFETY
505509
pthread_mutex_lock(&connections_mutex);
506510
#endif
511+
512+
/*
513+
* ... but first, make certain we have created ecpg_clocale. Rely on
514+
* holding connections_mutex to ensure this is done by only one thread.
515+
*/
516+
#ifdef HAVE_USELOCALE
517+
if (!ecpg_clocale)
518+
{
519+
ecpg_clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
520+
if (!ecpg_clocale)
521+
{
522+
#ifdef ENABLE_THREAD_SAFETY
523+
pthread_mutex_unlock(&connections_mutex);
524+
#endif
525+
ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
526+
ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
527+
if (host)
528+
ecpg_free(host);
529+
if (port)
530+
ecpg_free(port);
531+
if (options)
532+
ecpg_free(options);
533+
if (realname)
534+
ecpg_free(realname);
535+
if (dbname)
536+
ecpg_free(dbname);
537+
if (conn_keywords)
538+
ecpg_free(conn_keywords);
539+
if (conn_values)
540+
ecpg_free(conn_values);
541+
free(this);
542+
return false;
543+
}
544+
}
545+
#endif
546+
507547
if (connection_name != NULL)
508548
this->name = ecpg_strdup(connection_name, lineno);
509549
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);
@@ -1965,6 +1962,15 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
19651962
return false;
19661963
}
19671964

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

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

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

0 commit comments

Comments
 (0)