/* src/interfaces/ecpg/ecpglib/connect.c */
#define POSTGRES_ECPG_INTERNAL
#include "postgres_fe.h"
#include "ecpg-pthread-win32.h"
#include "ecpgerrno.h"
#include "ecpglib.h"
#include "ecpglib_extern.h"
#include "ecpgtype.h"
#include "sqlca.h"
#ifdef HAVE_USELOCALE
locale_t ecpg_clocale = (locale_t) 0 ;
#endif
static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_key_t actual_connection_key;
static pthread_once_t actual_connection_key_once = PTHREAD_ONCE_INIT;
static struct connection *actual_connection = NULL;
static struct connection *all_connections = NULL;
static void
ecpg_actual_connection_init(void )
{
pthread_key_create(&actual_connection_key, NULL);
}
void
ecpg_pthreads_init(void )
{
pthread_once(&actual_connection_key_once, ecpg_actual_connection_init);
}
static struct connection *
ecpg_get_connection_nr(const char *connection_name)
{
struct connection *ret = NULL;
if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT" ) == 0 ))
{
ecpg_pthreads_init(); /* ensure actual_connection_key is valid */
ret = pthread_getspecific(actual_connection_key);
/*
* if no connection in TSD for this thread , get the global default
* connection and hope the user knows what they ' re doing ( i . e . using
* their own mutex to protect that connection from concurrent accesses
*/
if (ret == NULL)
/* no TSD connection, going for global */
ret = actual_connection;
}
else
{
struct connection *con;
for (con = all_connections; con != NULL; con = con->next)
{
/*
* Check for the case of a NULL connection name , stored as such in
* the connection information by ECPGconnect ( ) when the database
* name is not specified by its caller .
*/
if (con->name != NULL && strcmp(connection_name, con->name) == 0 )
break ;
}
ret = con;
}
return ret;
}
struct connection *
ecpg_get_connection(const char *connection_name)
{
struct connection *ret = NULL;
if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT" ) == 0 ))
{
ecpg_pthreads_init(); /* ensure actual_connection_key is valid */
ret = pthread_getspecific(actual_connection_key);
/*
* if no connection in TSD for this thread , get the global default
* connection and hope the user knows what they ' re doing ( i . e . using
* their own mutex to protect that connection from concurrent accesses
*/
if (ret == NULL)
/* no TSD connection here either, using global */
ret = actual_connection;
}
else
{
pthread_mutex_lock(&connections_mutex);
ret = ecpg_get_connection_nr(connection_name);
pthread_mutex_unlock(&connections_mutex);
}
return ret;
}
static void
ecpg_finish(struct connection *act)
{
if (act != NULL)
{
struct ECPGtype_information_cache *cache,
*ptr;
ecpg_deallocate_all_conn(0 , ECPG_COMPAT_PGSQL, act);
PQfinish(act->connection);
/*
* no need to lock connections_mutex - we ' re always called by
* ECPGdisconnect or ECPGconnect , which are holding the lock
*/
/* remove act from the list */
if (act == all_connections)
all_connections = act->next;
else
{
struct connection *con;
for (con = all_connections; con->next && con->next != act; con = con->next);
if (con->next)
con->next = act->next;
}
if (pthread_getspecific(actual_connection_key) == act)
pthread_setspecific(actual_connection_key, all_connections);
if (actual_connection == act)
actual_connection = all_connections;
ecpg_log("ecpg_finish: connection %s closed\n" , act->name ? act->name : "(null)" );
for (cache = act->cache_head; cache; ptr = cache, cache = cache->next, ecpg_free(ptr));
ecpg_free(act->name);
ecpg_free(act);
/* delete cursor variables when last connection gets closed */
if (all_connections == NULL)
{
struct var_list *iv_ptr;
for (; ivlist; iv_ptr = ivlist, ivlist = ivlist->next, ecpg_free(iv_ptr));
}
}
else
ecpg_log("ecpg_finish: called an extra time\n" );
}
bool
ECPGsetcommit(int lineno, const char *mode, const char *connection_name)
{
struct connection *con = ecpg_get_connection(connection_name);
PGresult *results;
if (!ecpg_init(con, connection_name, lineno))
return false ;
ecpg_log("ECPGsetcommit on line %d: action \" %s\"; connection \" %s\"\n" , lineno, mode, con->name);
if (con->autocommit && strncmp(mode, "off" , strlen("off" )) == 0 )
{
if (PQtransactionStatus(con->connection) == PQTRANS_IDLE)
{
results = PQexec(con->connection, "begin transaction" );
if (!ecpg_check_PQresult(results, lineno, con->connection, ECPG_COMPAT_PGSQL))
return false ;
PQclear(results);
}
con->autocommit = false ;
}
else if (!con->autocommit && strncmp(mode, "on" , strlen("on" )) == 0 )
{
if (PQtransactionStatus(con->connection) != PQTRANS_IDLE)
{
results = PQexec(con->connection, "commit" );
if (!ecpg_check_PQresult(results, lineno, con->connection, ECPG_COMPAT_PGSQL))
return false ;
PQclear(results);
}
con->autocommit = true ;
}
return true ;
}
bool
ECPGsetconn(int lineno, const char *connection_name)
{
struct connection *con = ecpg_get_connection(connection_name);
if (!ecpg_init(con, connection_name, lineno))
return false ;
pthread_setspecific(actual_connection_key, con);
return true ;
}
static void
ECPGnoticeReceiver(void *arg, const PGresult *result)
{
char *sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
char *message = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
struct sqlca_t *sqlca = ECPGget_sqlca();
int sqlcode;
if (sqlca == NULL)
{
ecpg_log("out of memory" );
return ;
}
(void ) arg; /* keep the compiler quiet */
if (sqlstate == NULL)
sqlstate = ECPG_SQLSTATE_ECPG_INTERNAL_ERROR;
if (message == NULL) /* Shouldn't happen, but need to be sure */
message = ecpg_gettext("empty message text" );
/* these are not warnings */
if (strncmp(sqlstate, "00" , 2 ) == 0 )
return ;
ecpg_log("ECPGnoticeReceiver: %s\n" , message);
/* map to SQLCODE for backward compatibility */
if (strcmp(sqlstate, ECPG_SQLSTATE_INVALID_CURSOR_NAME) == 0 )
sqlcode = ECPG_WARNING_UNKNOWN_PORTAL;
else if (strcmp(sqlstate, ECPG_SQLSTATE_ACTIVE_SQL_TRANSACTION) == 0 )
sqlcode = ECPG_WARNING_IN_TRANSACTION;
else if (strcmp(sqlstate, ECPG_SQLSTATE_NO_ACTIVE_SQL_TRANSACTION) == 0 )
sqlcode = ECPG_WARNING_NO_TRANSACTION;
else if (strcmp(sqlstate, ECPG_SQLSTATE_DUPLICATE_CURSOR) == 0 )
sqlcode = ECPG_WARNING_PORTAL_EXISTS;
else
sqlcode = 0 ;
strncpy(sqlca->sqlstate, sqlstate, sizeof (sqlca->sqlstate));
sqlca->sqlcode = sqlcode;
sqlca->sqlwarn[2 ] = 'W' ;
sqlca->sqlwarn[0 ] = 'W' ;
strncpy(sqlca->sqlerrm.sqlerrmc, message, sizeof (sqlca->sqlerrm.sqlerrmc));
sqlca->sqlerrm.sqlerrmc[sizeof (sqlca->sqlerrm.sqlerrmc) - 1 ] = 0 ;
sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
ecpg_log("raising sqlcode %d\n" , sqlcode);
}
/* this contains some quick hacks, needs to be cleaned up, but it works */
bool
ECPGconnect(int lineno, int c, const char *name, const char *user, const char *passwd, const char *connection_name, int autocommit)
{
struct sqlca_t *sqlca = ECPGget_sqlca();
enum COMPAT_MODE compat = c;
struct connection *this ;
int i,
connect_params = 0 ;
char *dbname = name ? ecpg_strdup(name, lineno) : NULL,
*host = NULL,
*tmp,
*port = NULL,
*realname = NULL,
*options = NULL;
const char **conn_keywords;
const char **conn_values;
if (sqlca == NULL)
{
ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
ecpg_free(dbname);
return false ;
}
ecpg_init_sqlca(sqlca);
/*
* clear auto_mem structure because some error handling functions might
* access it
*/
ecpg_clear_auto_mem();
if (INFORMIX_MODE(compat))
{
char *envname;
/*
* Informix uses an environment variable DBPATH that overrides the
* connection parameters given here . We do the same with PG_DBPATH as
* the syntax is different .
*/
envname = getenv("PG_DBPATH" );
if (envname)
{
ecpg_free(dbname);
dbname = ecpg_strdup(envname, lineno);
}
}
if (dbname == NULL && connection_name == NULL)
connection_name = "DEFAULT" ;
ecpg_pthreads_init();
/* check if the identifier is unique */
if (ecpg_get_connection(connection_name))
{
ecpg_free(dbname);
ecpg_log("ECPGconnect: connection identifier %s is already in use\n" ,
connection_name);
return false ;
}
if ((this = (struct connection *) ecpg_alloc(sizeof (struct connection), lineno)) == NULL)
{
ecpg_free(dbname);
return false ;
}
if (dbname != NULL)
{
/* get the detail information from dbname */
if (strncmp(dbname, "tcp:" , 4 ) == 0 || strncmp(dbname, "unix:" , 5 ) == 0 )
{
int offset = 0 ;
/*
* only allow protocols tcp and unix
*/
if (strncmp(dbname, "tcp:" , 4 ) == 0 )
offset = 4 ;
else if (strncmp(dbname, "unix:" , 5 ) == 0 )
offset = 5 ;
if (strncmp(dbname + offset, "postgresql://", strlen("postgresql://")) == 0)
{
/*------
* new style :
* < tcp | unix > : postgresql : //server[:port][/db-name][?options]
* - - - - - -
*/
offset += strlen("postgresql://");
tmp = strrchr(dbname + offset, '?' );
if (tmp != NULL) /* options given */
{
options = ecpg_strdup(tmp + 1 , lineno);
*tmp = '\0' ;
}
tmp = last_dir_separator(dbname + offset);
if (tmp != NULL) /* database name given */
{
if (tmp[1 ] != '\0' ) /* non-empty database name */
{
realname = ecpg_strdup(tmp + 1 , lineno);
connect_params++;
}
*tmp = '\0' ;
}
tmp = strrchr(dbname + offset, ':' );
if (tmp != NULL) /* port number given */
{
*tmp = '\0' ;
port = ecpg_strdup(tmp + 1 , lineno);
connect_params++;
}
if (strncmp(dbname, "unix:" , 5 ) == 0 )
{
/*
* The alternative of using " 127 . 0 . 0 . 1 " here is deprecated
* and undocumented ; we ' ll keep it for backward
* compatibility ' s sake , but not extend it to allow IPv6 .
*/
if (strcmp(dbname + offset, "localhost" ) != 0 &&
strcmp(dbname + offset, "127.0.0.1" ) != 0 )
{
ecpg_log("ECPGconnect: non-localhost access via sockets on line %d\n" , lineno);
ecpg_raise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname : ecpg_gettext("<DEFAULT>" ));
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);
free(this );
return false ;
}
}
else
{
if (*(dbname + offset) != '\0' )
{
host = ecpg_strdup(dbname + offset, lineno);
connect_params++;
}
}
}
}
else
{
/* old style: dbname[@server][:port] */
tmp = strrchr(dbname, ':' );
if (tmp != NULL) /* port number given */
{
port = ecpg_strdup(tmp + 1 , lineno);
connect_params++;
*tmp = '\0' ;
}
tmp = strrchr(dbname, '@' );
if (tmp != NULL) /* host name given */
{
host = ecpg_strdup(tmp + 1 , lineno);
connect_params++;
*tmp = '\0' ;
}
if (strlen(dbname) > 0 )
{
realname = ecpg_strdup(dbname, lineno);
connect_params++;
}
else
realname = NULL;
}
}
else
realname = NULL;
/*
* Count options for the allocation done below ( this may produce an
* overestimate , it ' s ok ) .
*/
if (options)
for (i = 0 ; options[i]; i++)
if (options[i] == '=' )
connect_params++;
if (user && strlen(user) > 0 )
connect_params++;
if (passwd && strlen(passwd) > 0 )
connect_params++;
/*
* Allocate enough space for all connection parameters . These allocations
* are done before manipulating the list of connections to ease the error
* handling on failure .
*/
conn_keywords = (const char **) ecpg_alloc((connect_params + 1 ) * sizeof (char *), lineno);
conn_values = (const char **) ecpg_alloc(connect_params * sizeof (char *), lineno);
if (conn_keywords == NULL || conn_values == 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 ;
}
/* add connection to our list */
pthread_mutex_lock(&connections_mutex);
/*
* . . . 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)
{
pthread_mutex_unlock(&connections_mutex);
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)
this ->name = ecpg_strdup(connection_name, lineno);
else
this ->name = ecpg_strdup(realname, lineno);
this ->cache_head = NULL;
this ->prep_stmts = NULL;
if (all_connections == NULL)
this ->next = NULL;
else
this ->next = all_connections;
all_connections = this ;
pthread_setspecific(actual_connection_key, all_connections);
actual_connection = all_connections;
ecpg_log("ECPGconnect: opening database %s on %s port %s %s%s %s%s\n" ,
realname ? realname : "<DEFAULT>" ,
host ? host : "<DEFAULT>" ,
port ? (ecpg_internal_regression_mode ? "<REGRESSION_PORT>" : port) : "<DEFAULT>" ,
options ? "with options " : "" , options ? options : "" ,
(user && strlen(user) > 0 ) ? "for user " : "" , user ? user : "" );
i = 0 ;
if (realname)
{
conn_keywords[i] = "dbname" ;
conn_values[i] = realname;
i++;
}
if (host)
{
conn_keywords[i] = "host" ;
conn_values[i] = host;
i++;
}
if (port)
{
conn_keywords[i] = "port" ;
conn_values[i] = port;
i++;
}
if (user && strlen(user) > 0 )
{
conn_keywords[i] = "user" ;
conn_values[i] = user;
i++;
}
if (passwd && strlen(passwd) > 0 )
{
conn_keywords[i] = "password" ;
conn_values[i] = passwd;
i++;
}
if (options)
{
char *str;
/*
* The options string contains " keyword = value " pairs separated by
* ' & ' s . We must break this up into keywords and values to pass to
* libpq ( it ' s okay to scribble on the options string ) . We ignore
* spaces just before each keyword or value . ( The preprocessor used
* to add spaces around ' & ' s , making it necessary to ignore spaces
* before keywords here . While it no longer does that , we still must
* skip spaces to support code compiled with older preprocessors . )
*/
for (str = options; *str;)
{
int e,
a;
char *token1,
*token2;
/* Skip spaces before keyword */
for (token1 = str; *token1 == ' ' ; token1++)
/* skip */ ;
/* Find end of keyword */
for (e = 0 ; token1[e] && token1[e] != '=' ; e++)
/* skip */ ;
if (token1[e]) /* found "=" */
{
token1[e] = '\0' ;
/* Skip spaces before value */
for (token2 = token1 + e + 1 ; *token2 == ' ' ; token2++)
/* skip */ ;
/* Find end of value */
for (a = 0 ; token2[a] && token2[a] != '&' ; a++)
/* skip */ ;
if (token2[a]) /* found "&" => another option follows */
{
token2[a] = '\0' ;
str = token2 + a + 1 ;
}
else
str = token2 + a;
conn_keywords[i] = token1;
conn_values[i] = token2;
i++;
}
else
{
/* Bogus options syntax ... ignore trailing garbage */
str = token1 + e;
}
}
}
Assert(i <= connect_params);
conn_keywords[i] = NULL; /* terminator */
this ->connection = PQconnectdbParams(conn_keywords, conn_values, 0 );
if (host)
ecpg_free(host);
if (port)
ecpg_free(port);
if (options)
ecpg_free(options);
if (dbname)
ecpg_free(dbname);
ecpg_free(conn_values);
ecpg_free(conn_keywords);
if (PQstatus(this ->connection) == CONNECTION_BAD)
{
const char *errmsg = PQerrorMessage(this ->connection);
const char *db = realname ? realname : ecpg_gettext("<DEFAULT>" );
/* PQerrorMessage's result already has a trailing newline */
ecpg_log("ECPGconnect: %s" , errmsg);
ecpg_finish(this );
pthread_mutex_unlock(&connections_mutex);
ecpg_raise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, db);
if (realname)
ecpg_free(realname);
return false ;
}
if (realname)
ecpg_free(realname);
pthread_mutex_unlock(&connections_mutex);
this ->autocommit = autocommit;
PQsetNoticeReceiver(this ->connection, &ECPGnoticeReceiver, this );
return true ;
}
bool
ECPGdisconnect(int lineno, const char *connection_name)
{
struct sqlca_t *sqlca = ECPGget_sqlca();
struct connection *con;
if (sqlca == NULL)
{
ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
return false ;
}
pthread_mutex_lock(&connections_mutex);
if (strcmp(connection_name, "ALL" ) == 0 )
{
ecpg_init_sqlca(sqlca);
for (con = all_connections; con;)
{
struct connection *f = con;
con = con->next;
ecpg_finish(f);
}
}
else
{
con = ecpg_get_connection_nr(connection_name);
if (!ecpg_init(con, connection_name, lineno))
{
pthread_mutex_unlock(&connections_mutex);
return false ;
}
else
ecpg_finish(con);
}
pthread_mutex_unlock(&connections_mutex);
return true ;
}
PGconn *
ECPGget_PGconn(const char *connection_name)
{
struct connection *con;
con = ecpg_get_connection(connection_name);
if (con == NULL)
return NULL;
return con->connection;
}
Messung V0.5 in Prozent C=94 H=93 G=93
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland