From: "Michael Meskes" <[email protected]>
On Tue, Dec 17, 2013 at 09:26:49PM +0900, MauMau wrote:[Problem] The ECPG app runs the statement:EXEC SQL CONNECT TO 'tcp:postgresql://?service=my_service'; ... ECPGconnect() parses the URI and produces an empty host name. It passes an empty string as the value for "host" connection parameter to PQconnectdbParams().Interestingly enough it works flawlessly on my system. Any idea where theempoty host name comes from? Before accepting the patch I'd like to find outwhy you got an empty host variable and I don't.
You can confirm it by adding the following code fragment to ecpglib/connect.c. The attached file contains this.
if (host)
printf("host=%s\n", host);
else
printf("host=NULL\n");
Build and run the attached sample program like this (this is an example on
Windows, but the result should be the same on Linux):
ecpg connect.pgccl /nologo /MD /I<pg_inst_dir>\include connect.c /link /libpath:<pg_inst_dir>\lib libecpg.lib
connect.exeThe added code in ecpglib/connect.c displays "host=", which shows that an empty host is passed to PQconnectdbParams(). Of course, on Linux, you can use gdb to run the sample program, set a breakpoint at PQconnectDbParams(), and display the keywords/values arrays of the argument.
An empty string is set to the host local variable at line 430 in ecpglib/connect.c (this line number is that of PostgreSQL 9.4). That is the else block shown below:
if (strncmp(dbname, "unix:", 5) == 0)
{
...
}
else
{
host = ecpg_strdup(dbname + offset, lineno);
connect_params++;
}
Regards
MauMau
connect.pgc
Description: Binary data
/* src/interfaces/ecpg/ecpglib/connect.c */
#define POSTGRES_ECPG_INTERNAL
#include "postgres_fe.h"
#include "ecpg-pthread-win32.h"
#include "ecpgtype.h"
#include "ecpglib.h"
#include "ecpgerrno.h"
#include "extern.h"
#include "sqlca.h"
#ifdef ENABLE_THREAD_SAFETY
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;
#endif
static struct connection *actual_connection = NULL;
static struct connection *all_connections = NULL;
#ifdef ENABLE_THREAD_SAFETY
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);
}
#endif
static struct connection *
ecpg_get_connection_nr(const char *connection_name)
{
struct connection *ret = NULL;
if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT") ==
0))
{
#ifdef ENABLE_THREAD_SAFETY
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 then we got the connection from TSD */
if (NULL == ret)
/* no TSD connection, going for global */
ret = actual_connection;
#else
ret = actual_connection;
#endif
}
else
{
struct connection *con;
for (con = all_connections; con != NULL; con = con->next)
{
if (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))
{
#ifdef ENABLE_THREAD_SAFETY
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 then we got the connection from TSD */
if (NULL == ret)
/* no TSD connection here either, using global */
ret = actual_connection;
#else
ret = actual_connection;
#endif
}
else
{
#ifdef ENABLE_THREAD_SAFETY
pthread_mutex_lock(&connections_mutex);
#endif
ret = ecpg_get_connection_nr(connection_name);
#ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock(&connections_mutex);
#endif
}
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;
}
#ifdef ENABLE_THREAD_SAFETY
if (pthread_getspecific(actual_connection_key) == act)
pthread_setspecific(actual_connection_key,
all_connections);
#endif
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);
#ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(actual_connection_key, con);
#else
actual_connection = con;
#endif
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;
(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;
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";
#if ENABLE_THREAD_SAFETY
ecpg_pthreads_init();
#endif
/* 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)
return false;
if (dbname != NULL)
{
/* get the detail information out of 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|:/unixsocket/path:]
* [/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 or Unix
socket path given */
{
char *tmp2;
*tmp = '\0';
if ((tmp2 = strchr(tmp + 1, ':')) !=
NULL)
{
*tmp2 = '\0';
host = ecpg_strdup(tmp + 1,
lineno);
connect_params++;
if (strncmp(dbname, "unix:", 5)
!= 0)
{
ecpg_log("ECPGconnect:
socketname %s given for TCP connection on line %d\n", host, lineno);
ecpg_raise(lineno, ECPG_CONNECT,
ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname :
ecpg_gettext("<DEFAULT>"));
if (host)
ecpg_free(host);
/*
* port not set yet 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
{
port = ecpg_strdup(tmp + 1,
lineno);
connect_params++;
}
}
if (strncmp(dbname, "unix:", 5) == 0)
{
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
{
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;
/* add connection to our list */
#ifdef ENABLE_THREAD_SAFETY
pthread_mutex_lock(&connections_mutex);
#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;
#ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(actual_connection_key, all_connections);
#endif
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 :
"");
if (options)
for (i = 0; options[i]; i++)
/* count options */
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 */
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;
}
i = 0;
if (realname)
{
conn_keywords[i] = "dbname";
conn_values[i] = realname;
i++;
}
if (host)
printf("host=%s\n", host);
else
printf("host=NULL\n");
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;
/* options look like this "option1 = value1 option2 = value2
... */
/* we have to break up the string into single options */
for (str = options; *str;)
{
int e,
a;
char *token1,
*token2;
for (token1 = str; *token1 && *token1 == ' '; token1++);
for (e = 0; token1[e] && token1[e] != '='; e++);
if (token1[e]) /* found "=" */
{
token1[e] = '\0';
for (token2 = token1 + e + 1; *token2 &&
*token2 == ' '; token2++);
for (a = 0; token2[a] && token2[a] != '&'; a++);
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
/* the parser should not be able to create this
invalid option */
str = token1 + e;
}
}
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>");
ecpg_log("ECPGconnect: could not open database: %s\n", errmsg);
ecpg_finish(this);
#ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock(&connections_mutex);
#endif
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);
#ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock(&connections_mutex);
#endif
this->autocommit = autocommit;
PQsetNoticeReceiver(this->connection, &ECPGnoticeReceiver, (void *)
this);
return true;
}
bool
ECPGdisconnect(int lineno, const char *connection_name)
{
struct sqlca_t *sqlca = ECPGget_sqlca();
struct connection *con;
#ifdef ENABLE_THREAD_SAFETY
pthread_mutex_lock(&connections_mutex);
#endif
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))
{
#ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock(&connections_mutex);
#endif
return (false);
}
else
ecpg_finish(con);
}
#ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock(&connections_mutex);
#endif
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;
}
-- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers
