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 the
empoty host name comes from? Before accepting the patch I'd like to find out
why 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.pgc
cl /nologo /MD /I<pg_inst_dir>\include connect.c /link /libpath:<pg_inst_dir>\lib libecpg.lib
connect.exe

The 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

Attachment: 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

Reply via email to