From: "Michael Meskes" <mes...@postgresql.org>
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 (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers