Skip to content
Snippets Groups Projects
Select Git revision
  • benchmark-tools
  • postgres-lambda
  • master default
  • REL9_4_25
  • REL9_5_20
  • REL9_6_16
  • REL_10_11
  • REL_11_6
  • REL_12_1
  • REL_12_0
  • REL_12_RC1
  • REL_12_BETA4
  • REL9_4_24
  • REL9_5_19
  • REL9_6_15
  • REL_10_10
  • REL_11_5
  • REL_12_BETA3
  • REL9_4_23
  • REL9_5_18
  • REL9_6_14
  • REL_10_9
  • REL_11_4
23 results

fe-connect.c

Blame
    • Bruce Momjian's avatar
      6cc4175b
      Attached is a patch that takes care of the PATHSEP issue. I made a more · 6cc4175b
      Bruce Momjian authored
      extensive change then what was suggested. I found the file path.c that
      contained a lot of "Unix/Windows" agnostic functions so I added a function
      there instead and removed the PATHSEP declaration in exec.c altogether. All
      to keep things from scattering all over the code.
      
      I also took the liberty of changing the name of the functions
      "first_path_sep" and "last_path_sep". Where I come from (and I'm apparently
      not alone given the former macro name PATHSEP), they should be called
      "first_dir_sep" and "last_dir_sep". The new function I introduced, that
      actually finds path separators, is now the "first_path_sep". The patch
      contains changes on all affected places of course.
      
      I also changed the documentation on dynamic_library_path to reflect the
      chagnes.
      
      Thomas Hallgren
      6cc4175b
      History
      Attached is a patch that takes care of the PATHSEP issue. I made a more
      Bruce Momjian authored
      extensive change then what was suggested. I found the file path.c that
      contained a lot of "Unix/Windows" agnostic functions so I added a function
      there instead and removed the PATHSEP declaration in exec.c altogether. All
      to keep things from scattering all over the code.
      
      I also took the liberty of changing the name of the functions
      "first_path_sep" and "last_path_sep". Where I come from (and I'm apparently
      not alone given the former macro name PATHSEP), they should be called
      "first_dir_sep" and "last_dir_sep". The new function I introduced, that
      actually finds path separators, is now the "first_path_sep". The patch
      contains changes on all affected places of course.
      
      I also changed the documentation on dynamic_library_path to reflect the
      chagnes.
      
      Thomas Hallgren
    fe-connect.c 74.18 KiB
    /*-------------------------------------------------------------------------
     *
     * fe-connect.c
     *	  functions related to setting up a connection to the backend
     *
     * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
     * Portions Copyright (c) 1994, Regents of the University of California
     *
     *
     * IDENTIFICATION
     *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.274 2004/06/10 22:26:24 momjian Exp $
     *
     *-------------------------------------------------------------------------
     */
    
    #include "postgres_fe.h"
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <ctype.h>
    #include <time.h>
    
    #ifndef HAVE_STRDUP
    #include "strdup.h"
    #endif
    
    #include "libpq-fe.h"
    #include "libpq-int.h"
    #include "fe-auth.h"
    #include "pg_config_paths.h"
    
    #ifdef WIN32
    #include "win32.h"
    #else
    #include <sys/socket.h>
    #include <unistd.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #ifdef HAVE_NETINET_TCP_H
    #include <netinet/tcp.h>
    #endif
    #include <arpa/inet.h>
    #endif
    
    #ifdef ENABLE_THREAD_SAFETY
    #include <pthread.h>
    #endif
    
    #include "libpq/ip.h"
    #include "mb/pg_wchar.h"
    
    #define PGPASSFILE ".pgpass"
    
    /* fall back options if they are not specified by arguments or defined
       by environment variables */
    #define DefaultHost		"localhost"
    #define DefaultTty		""
    #define DefaultOption	""
    #define DefaultAuthtype		  ""
    #define DefaultPassword		  ""
    #ifdef USE_SSL
    #define DefaultSSLMode	"prefer"
    #else
    #define DefaultSSLMode	"disable"
    #endif
    
    /* ----------
     * Definition of the conninfo parameters and their fallback resources.
     *
     * If Environment-Var and Compiled-in are specified as NULL, no
     * fallback is available. If after all no value can be determined
     * for an option, an error is returned.
     *
     * The value for the username is treated specially in conninfo_parse.
     * If the Compiled-in resource is specified as a NULL value, the
     * user is determined by fe_getauthname().
     *
     * The Label and Disp-Char entries are provided for applications that
     * want to use PQconndefaults() to create a generic database connection
     * dialog. Disp-Char is defined as follows:
     *		""		Normal input field
     *		"*"		Password field - hide value
     *		"D"		Debug option - don't show by default
     *
     * PQconninfoOptions[] is a constant static array that we use to initialize
     * a dynamically allocated working copy.  All the "val" fields in
     * PQconninfoOptions[] *must* be NULL.	In a working copy, non-null "val"
     * fields point to malloc'd strings that should be freed when the working
     * array is freed (see PQconninfoFree).
     * ----------
     */
    static const PQconninfoOption PQconninfoOptions[] = {
    	/*
    	 * "authtype" is no longer used, so mark it "don't show".  We keep it
    	 * in the array so as not to reject conninfo strings from old apps
    	 * that might still try to set it.
    	 */
    	{"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL,
    	"Database-Authtype", "D", 20},
    
    	{"service", "PGSERVICE", NULL, NULL,
    	"Database-Service", "", 20},
    
    	{"user", "PGUSER", NULL, NULL,
    	"Database-User", "", 20},
    
    	{"password", "PGPASSWORD", NULL, NULL,
    	"Database-Password", "*", 20},
    
    	{"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL,
    	"Connect-timeout", "", 10}, /* strlen(INT32_MAX) == 10 */
    
    	{"dbname", "PGDATABASE", NULL, NULL,
    	"Database-Name", "", 20},
    
    	{"host", "PGHOST", NULL, NULL,
    	"Database-Host", "", 40},
    
    	{"hostaddr", "PGHOSTADDR", NULL, NULL,
    	"Database-Host-IP-Address", "", 45},
    
    	{"port", "PGPORT", DEF_PGPORT_STR, NULL,
    	"Database-Port", "", 6},
    
    	/*
    	 * "tty" is no longer used either, but keep it present for backwards
    	 * compatibility.
    	 */
    	{"tty", "PGTTY", DefaultTty, NULL,
    	"Backend-Debug-TTY", "D", 40},
    
    	{"options", "PGOPTIONS", DefaultOption, NULL,
    	"Backend-Debug-Options", "D", 40},
    
    #ifdef USE_SSL
    
    	/*
    	 * "requiressl" is deprecated, its purpose having been taken over by
    	 * "sslmode". It remains for backwards compatibility.
    	 */
    	{"requiressl", "PGREQUIRESSL", "0", NULL,
    	"Require-SSL", "D", 1},
    #endif
    
    	/*
    	 * "sslmode" option is allowed even without client SSL support because
    	 * the client can still handle SSL modes "disable" and "allow".
    	 */
    	{"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
    	"SSL-Mode", "", 8},			/* sizeof("disable") == 8 */
    
    	/* Terminating entry --- MUST BE LAST */
    	{NULL, NULL, NULL, NULL,
    	NULL, NULL, 0}
    };
    
    static const PQEnvironmentOption EnvironmentOptions[] =
    {
    	/* common user-interface settings */
    	{
    		"PGDATESTYLE", "datestyle"
    	},
    	{
    		"PGTZ", "timezone"
    	},
    	{
    		"PGCLIENTENCODING", "client_encoding"
    	},
    	/* internal performance-related settings */
    	{
    		"PGGEQO", "geqo"
    	},
    	{
    		NULL, NULL
    	}
    };
    
    
    static bool connectOptions1(PGconn *conn, const char *conninfo);
    static bool connectOptions2(PGconn *conn);
    static int	connectDBStart(PGconn *conn);
    static int	connectDBComplete(PGconn *conn);
    static PGconn *makeEmptyPGconn(void);
    static void freePGconn(PGconn *conn);
    static void closePGconn(PGconn *conn);
    static PQconninfoOption *conninfo_parse(const char *conninfo,
    			   PQExpBuffer errorMessage);
    static char *conninfo_getval(PQconninfoOption *connOptions,
    				const char *keyword);
    static void defaultNoticeReceiver(void *arg, const PGresult *res);
    static void defaultNoticeProcessor(void *arg, const char *message);
    static int parseServiceInfo(PQconninfoOption *options,
    				 PQExpBuffer errorMessage);
    static char *pwdfMatchesString(char *buf, char *token);
    static char *PasswordFromFile(char *hostname, char *port, char *dbname,
    				 char *username);
    
    
    /*
     *		Connecting to a Database
     *
     * There are now four different ways a user of this API can connect to the
     * database.  Two are not recommended for use in new code, because of their
     * lack of extensibility with respect to the passing of options to the
     * backend.  These are PQsetdb and PQsetdbLogin (the former now being a macro
     * to the latter).
     *
     * If it is desired to connect in a synchronous (blocking) manner, use the
     * function PQconnectdb.
     *
     * To connect in an asynchronous (non-blocking) manner, use the functions
     * PQconnectStart, and PQconnectPoll.
     *
     * Internally, the static functions connectDBStart, connectDBComplete
     * are part of the connection procedure.
     */
    
    /*
     *		PQconnectdb
     *
     * establishes a connection to a postgres backend through the postmaster
     * using connection information in a string.
     *
     * The conninfo string is a white-separated list of
     *
     *	   option = value
     *
     * definitions. Value might be a single value containing no whitespaces or
     * a single quoted string. If a single quote should appear anywhere in
     * the value, it must be escaped with a backslash like \'
     *
     * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
     * if a memory allocation failed.
     * If the status field of the connection returned is CONNECTION_BAD,
     * then some fields may be null'ed out instead of having valid values.
     *
     * You should call PQfinish (if conn is not NULL) regardless of whether this
     * call succeeded.
     */
    PGconn *
    PQconnectdb(const char *conninfo)
    {
    	PGconn	   *conn = PQconnectStart(conninfo);
    
    	if (conn && conn->status != CONNECTION_BAD)
    		(void) connectDBComplete(conn);
    
    	return conn;
    }
    
    /*
     *		PQconnectStart
     *
     * Begins the establishment of a connection to a postgres backend through the
     * postmaster using connection information in a string.
     *
     * See comment for PQconnectdb for the definition of the string format.
     *
     * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
     * you should not attempt to proceed with this connection.	If the status
     * field of the connection returned is CONNECTION_BAD, an error has
     * occurred. In this case you should call PQfinish on the result, (perhaps
     * inspecting the error message first).  Other fields of the structure may not
     * be valid if that occurs.  If the status field is not CONNECTION_BAD, then
     * this stage has succeeded - call PQconnectPoll, using select(2) to see when
     * this is necessary.
     *
     * See PQconnectPoll for more info.
     */
    PGconn *
    PQconnectStart(const char *conninfo)
    {
    	PGconn	   *conn;
    
    	/*
    	 * Allocate memory for the conn structure
    	 */
    	conn = makeEmptyPGconn();
    	if (conn == NULL)
    		return NULL;
    
    	/*
    	 * Parse the conninfo string
    	 */
    	if (!connectOptions1(conn, conninfo))
    		return conn;
    
    	/*
    	 * Compute derived options
    	 */
    	if (!connectOptions2(conn))
    		return conn;
    
    	/*
    	 * Connect to the database
    	 */
    	if (!connectDBStart(conn))
    	{
    		/* Just in case we failed to set it in connectDBStart */
    		conn->status = CONNECTION_BAD;
    	}
    
    	return conn;
    }
    
    /*
     *		connectOptions1
     *
     * Internal subroutine to set up connection parameters given an already-
     * created PGconn and a conninfo string.  Derived settings should be
     * processed by calling connectOptions2 next.  (We split them because
     * PQsetdbLogin overrides defaults in between.)
     *
     * Returns true if OK, false if trouble (in which case errorMessage is set
     * and so is conn->status).
     */
    static bool
    connectOptions1(PGconn *conn, const char *conninfo)
    {
    	PQconninfoOption *connOptions;
    	char	   *tmp;
    
    	/*
    	 * Parse the conninfo string
    	 */
    	connOptions = conninfo_parse(conninfo, &conn->errorMessage);
    	if (connOptions == NULL)
    	{
    		conn->status = CONNECTION_BAD;
    		/* errorMessage is already set */
    		return false;
    	}
    
    	/*
    	 * Move option values into conn structure
    	 *
    	 * Don't put anything cute here --- intelligence should be in
    	 * connectOptions2 ...
    	 */
    	tmp = conninfo_getval(connOptions, "hostaddr");
    	conn->pghostaddr = tmp ? strdup(tmp) : NULL;
    	tmp = conninfo_getval(connOptions, "host");
    	conn->pghost = tmp ? strdup(tmp) : NULL;
    	tmp = conninfo_getval(connOptions, "port");
    	conn->pgport = tmp ? strdup(tmp) : NULL;
    	tmp = conninfo_getval(connOptions, "tty");
    	conn->pgtty = tmp ? strdup(tmp) : NULL;
    	tmp = conninfo_getval(connOptions, "options");
    	conn->pgoptions = tmp ? strdup(tmp) : NULL;
    	tmp = conninfo_getval(connOptions, "dbname");
    	conn->dbName = tmp ? strdup(tmp) : NULL;
    	tmp = conninfo_getval(connOptions, "user");
    	conn->pguser = tmp ? strdup(tmp) : NULL;
    	tmp = conninfo_getval(connOptions, "password");
    	conn->pgpass = tmp ? strdup(tmp) : NULL;
    	tmp = conninfo_getval(connOptions, "connect_timeout");
    	conn->connect_timeout = tmp ? strdup(tmp) : NULL;
    	tmp = conninfo_getval(connOptions, "sslmode");
    	conn->sslmode = tmp ? strdup(tmp) : NULL;
    #ifdef USE_SSL
    	tmp = conninfo_getval(connOptions, "requiressl");
    	if (tmp && tmp[0] == '1')
    	{
    		/* here warn that the requiressl option is deprecated? */
    		if (conn->sslmode)
    			free(conn->sslmode);
    		conn->sslmode = strdup("require");
    	}
    #endif
    
    	/*
    	 * Free the option info - all is in conn now
    	 */
    	PQconninfoFree(connOptions);
    
    	return true;
    }
    
    /*
     *		connectOptions2
     *
     * Compute derived connection options after absorbing all user-supplied info.
     *
     * Returns true if OK, false if trouble (in which case errorMessage is set
     * and so is conn->status).
     */
    static bool
    connectOptions2(PGconn *conn)
    {
    	/*
    	 * If database name was not given, default it to equal user name
    	 */
    	if ((conn->dbName == NULL || conn->dbName[0] == '\0')
    		&& conn->pguser != NULL)
    	{
    		if (conn->dbName)
    			free(conn->dbName);
    		conn->dbName = strdup(conn->pguser);
    	}
    
    	/*
    	 * Supply default password if none given
    	 */
    	if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
    	{
    		if (conn->pgpass)
    			free(conn->pgpass);
    		conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport,
    										conn->dbName, conn->pguser);
    		if (conn->pgpass == NULL)
    			conn->pgpass = strdup(DefaultPassword);
    	}
    
    	/*
    	 * Allow unix socket specification in the host name
    	 */
    	if (conn->pghost && is_absolute_path(conn->pghost))
    	{
    		if (conn->pgunixsocket)
    			free(conn->pgunixsocket);
    		conn->pgunixsocket = conn->pghost;
    		conn->pghost = NULL;
    	}
    
    #ifdef NOT_USED
    
    	/*
    	 * parse dbName to get all additional info in it, if any
    	 */
    	if (update_db_info(conn) != 0)
    	{
    		conn->status = CONNECTION_BAD;
    		/* errorMessage is already set */
    		return false;
    	}
    #endif
    
    	/*
    	 * validate sslmode option
    	 */
    	if (conn->sslmode)
    	{
    		if (strcmp(conn->sslmode, "disable") != 0
    			&& strcmp(conn->sslmode, "allow") != 0
    			&& strcmp(conn->sslmode, "prefer") != 0
    			&& strcmp(conn->sslmode, "require") != 0)
    		{
    			conn->status = CONNECTION_BAD;
    			printfPQExpBuffer(&conn->errorMessage,
    						 libpq_gettext("invalid sslmode value: \"%s\"\n"),
    							  conn->sslmode);
    			return false;
    		}
    
    #ifndef USE_SSL
    		switch (conn->sslmode[0])
    		{
    			case 'a':			/* "allow" */
    			case 'p':			/* "prefer" */
    
    				/*
    				 * warn user that an SSL connection will never be
    				 * negotiated since SSL was not compiled in?
    				 */
    				break;
    
    			case 'r':			/* "require" */
    				conn->status = CONNECTION_BAD;
    				printfPQExpBuffer(&conn->errorMessage,
    								  libpq_gettext("sslmode value \"%s\" invalid when SSL support is not compiled in\n"),
    								  conn->sslmode);
    				return false;
    		}
    #endif
    	}
    	else
    		conn->sslmode = strdup(DefaultSSLMode);
    
    	return true;
    }
    
    /*
     *		PQconndefaults
     *
     * Parse an empty string like PQconnectdb() would do and return the
     * working connection options array.
     *
     * Using this function, an application may determine all possible options
     * and their current default values.
     *
     * NOTE: as of PostgreSQL 7.0, the returned array is dynamically allocated
     * and should be freed when no longer needed via PQconninfoFree().	(In prior
     * versions, the returned array was static, but that's not thread-safe.)
     * Pre-7.0 applications that use this function will see a small memory leak
     * until they are updated to call PQconninfoFree.
     */
    PQconninfoOption *
    PQconndefaults(void)
    {
    	PQExpBufferData errorBuf;
    	PQconninfoOption *connOptions;
    
    	initPQExpBuffer(&errorBuf);
    	connOptions = conninfo_parse("", &errorBuf);
    	termPQExpBuffer(&errorBuf);
    	return connOptions;
    }
    
    /* ----------------
     *		PQsetdbLogin
     *
     * establishes a connection to a postgres backend through the postmaster
     * at the specified host and port.
     *
     * returns a PGconn* which is needed for all subsequent libpq calls
     *
     * if the status field of the connection returned is CONNECTION_BAD,
     * then only the errorMessage is likely to be useful.
     * ----------------
     */
    PGconn *
    PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
    			 const char *pgtty, const char *dbName, const char *login,
    			 const char *pwd)
    {
    	PGconn	   *conn;
    
    	/*
    	 * Allocate memory for the conn structure
    	 */
    	conn = makeEmptyPGconn();
    	if (conn == NULL)
    		return NULL;
    
    	/*
    	 * Parse an empty conninfo string in order to set up the same defaults
    	 * that PQconnectdb() would use.
    	 */
    	if (!connectOptions1(conn, ""))
    		return conn;
    
    	/*
    	 * Absorb specified options into conn structure, overriding defaults
    	 */
    	if (pghost && pghost[0] != '\0')
    	{
    		if (conn->pghost)
    			free(conn->pghost);
    		conn->pghost = strdup(pghost);
    	}
    
    	if (pgport && pgport[0] != '\0')
    	{
    		if (conn->pgport)
    			free(conn->pgport);
    		conn->pgport = strdup(pgport);
    	}
    
    	if (pgoptions && pgoptions[0] != '\0')
    	{
    		if (conn->pgoptions)
    			free(conn->pgoptions);
    		conn->pgoptions = strdup(pgoptions);
    	}
    
    	if (pgtty && pgtty[0] != '\0')
    	{
    		if (conn->pgtty)
    			free(conn->pgtty);
    		conn->pgtty = strdup(pgtty);
    	}
    
    	if (dbName && dbName[0] != '\0')
    	{
    		if (conn->dbName)
    			free(conn->dbName);
    		conn->dbName = strdup(dbName);
    	}
    
    	if (login && login[0] != '\0')
    	{
    		if (conn->pguser)
    			free(conn->pguser);
    		conn->pguser = strdup(login);
    	}
    
    	if (pwd && pwd[0] != '\0')
    	{
    		if (conn->pgpass)
    			free(conn->pgpass);
    		conn->pgpass = strdup(pwd);
    	}
    
    	/*
    	 * Compute derived options
    	 */
    	if (!connectOptions2(conn))
    		return conn;
    
    	/*
    	 * Connect to the database
    	 */
    	if (connectDBStart(conn))
    		(void) connectDBComplete(conn);
    
    	return conn;
    }
    
    
    #ifdef NOT_USED					/* because it's broken */
    /*
     * update_db_info -
     * get all additional info out of dbName
     */
    static int
    update_db_info(PGconn *conn)
    {
    	char	   *tmp,
    			   *tmp2,
    			   *old = conn->dbName;
    
    	if (strchr(conn->dbName, '@') != NULL)
    	{
    		/* old style: dbname[@server][:port] */
    		tmp = strrchr(conn->dbName, ':');
    		if (tmp != NULL)		/* port number given */
    		{
    			if (conn->pgport)
    				free(conn->pgport);
    			conn->pgport = strdup(tmp + 1);
    			*tmp = '\0';
    		}
    
    		tmp = strrchr(conn->dbName, '@');
    		if (tmp != NULL)		/* host name given */
    		{
    			if (conn->pghost)
    				free(conn->pghost);
    			conn->pghost = strdup(tmp + 1);
    			*tmp = '\0';
    		}
    
    		conn->dbName = strdup(old);
    		free(old);
    	}
    	else
    	{
    		int			offset;
    
    		/*
    		 * only allow protocols tcp and unix
    		 */
    		if (strncmp(conn->dbName, "tcp:", 4) == 0)
    			offset = 4;
    		else if (strncmp(conn->dbName, "unix:", 5) == 0)
    			offset = 5;
    		else
    			return 0;
    
    		if (strncmp(conn->dbName + offset, "postgresql://", strlen("postgresql://")) == 0)
    		{
    
    			/*-------
    			 * new style:
    			 *	<tcp|unix>:postgresql://server[:port|:/unixsocket/path:]
    			 *	[/db name][?options]
    			 *-------
    			 */
    			offset += strlen("postgresql://");
    
    			tmp = strrchr(conn->dbName + offset, '?');
    			if (tmp != NULL)	/* options given */
    			{
    				if (conn->pgoptions)
    					free(conn->pgoptions);
    				conn->pgoptions = strdup(tmp + 1);
    				*tmp = '\0';
    			}
    
    			tmp = last_dir_separator(conn->dbName + offset);
    			if (tmp != NULL)	/* database name given */
    			{
    				if (conn->dbName)
    					free(conn->dbName);
    				conn->dbName = strdup(tmp + 1);
    				*tmp = '\0';
    			}
    			else
    			{
    				/*
    				 * Why do we default only this value from the environment
    				 * again?
    				 */
    				if ((tmp = getenv("PGDATABASE")) != NULL)
    				{
    					if (conn->dbName)
    						free(conn->dbName);
    					conn->dbName = strdup(tmp);
    				}
    				else if (conn->pguser)
    				{
    					if (conn->dbName)
    						free(conn->dbName);
    					conn->dbName = strdup(conn->pguser);
    				}
    			}
    
    			tmp = strrchr(old + offset, ':');
    			if (tmp != NULL)	/* port number or Unix socket path given */
    			{
    				*tmp = '\0';
    				if ((tmp2 = strchr(tmp + 1, ':')) != NULL)
    				{
    					if (strncmp(old, "unix:", 5) != 0)
    					{
    						printfPQExpBuffer(&conn->errorMessage,
    										  "connectDBStart() -- "
    								"socket name can only be specified with "
    										  "non-TCP\n");
    						return 1;
    					}
    					*tmp2 = '\0';
    					if (conn->pgunixsocket)
    						free(conn->pgunixsocket);
    					conn->pgunixsocket = strdup(tmp + 1);
    				}
    				else
    				{
    					if (conn->pgport)
    						free(conn->pgport);
    					conn->pgport = strdup(tmp + 1);
    					if (conn->pgunixsocket)
    						free(conn->pgunixsocket);
    					conn->pgunixsocket = NULL;
    				}
    			}
    
    			if (strncmp(old, "unix:", 5) == 0)
    			{
    				if (conn->pghost)
    					free(conn->pghost);
    				conn->pghost = NULL;
    				if (strcmp(old + offset, "localhost") != 0)
    				{
    					printfPQExpBuffer(&conn->errorMessage,
    									  "connectDBStart() -- "
    									  "non-TCP access only possible on "
    									  "localhost\n");
    					return 1;
    				}
    			}
    			else
    			{
    				if (conn->pghost)
    					free(conn->pghost);
    				conn->pghost = strdup(old + offset);
    			}
    			free(old);
    		}
    	}
    
    	return 0;
    }
    #endif   /* NOT_USED */
    
    
    /* ----------
     * connectMakeNonblocking -
     * Make a connection non-blocking.
     * Returns 1 if successful, 0 if not.
     * ----------
     */
    static int
    connectMakeNonblocking(PGconn *conn)
    {
    	if (!set_noblock(conn->sock))
    	{
    		char		sebuf[256];
    
    		printfPQExpBuffer(&conn->errorMessage,
    		libpq_gettext("could not set socket to non-blocking mode: %s\n"),
    						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
    		return 0;
    	}
    
    	return 1;
    }
    
    /* ----------
     * connectNoDelay -
     * Sets the TCP_NODELAY socket option.
     * Returns 1 if successful, 0 if not.
     * ----------
     */
    static int
    connectNoDelay(PGconn *conn)
    {
    #ifdef	TCP_NODELAY
    	int			on = 1;
    
    	if (setsockopt(conn->sock, IPPROTO_TCP, TCP_NODELAY,
    				   (char *) &on,
    				   sizeof(on)) < 0)
    	{
    		char		sebuf[256];
    
    		printfPQExpBuffer(&conn->errorMessage,
    		libpq_gettext("could not set socket to TCP no delay mode: %s\n"),
    						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
    		return 0;
    	}
    #endif
    
    	return 1;
    }
    
    
    /* ----------
     * connectFailureMessage -
     * create a friendly error message on connection failure.
     * ----------
     */
    static void
    connectFailureMessage(PGconn *conn, int errorno)
    {
    	char		sebuf[256];
    
    #ifdef HAVE_UNIX_SOCKETS
    	if (IS_AF_UNIX(conn->raddr.addr.ss_family))
    	{
    		char		service[NI_MAXHOST];
    
    		getnameinfo_all(&conn->raddr.addr, conn->raddr.salen,
    						NULL, 0,
    						service, sizeof(service),
    						NI_NUMERICSERV);
    		printfPQExpBuffer(&conn->errorMessage,
    						  libpq_gettext(
    									  "could not connect to server: %s\n"
    						"\tIs the server running locally and accepting\n"
    						  "\tconnections on Unix domain socket \"%s\"?\n"
    										),
    						  SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
    						  service);
    	}
    	else
    #endif   /* HAVE_UNIX_SOCKETS */
    	{
    		printfPQExpBuffer(&conn->errorMessage,
    						  libpq_gettext(
    									  "could not connect to server: %s\n"
    				 "\tIs the server running on host \"%s\" and accepting\n"
    									 "\tTCP/IP connections on port %s?\n"
    										),
    						  SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
    						  conn->pghostaddr
    						  ? conn->pghostaddr
    						  : (conn->pghost
    							 ? conn->pghost
    							 : "???"),
    						  conn->pgport);
    	}
    }
    
    
    /* ----------
     * connectDBStart -
     *		Begin the process of making a connection to the backend.
     *
     * Returns 1 if successful, 0 if not.
     * ----------
     */
    static int
    connectDBStart(PGconn *conn)
    {
    	int			portnum;
    	char		portstr[128];
    	struct addrinfo *addrs = NULL;
    	struct addrinfo hint;
    	const char *node = NULL;
    	int			ret;
    #ifdef ENABLE_THREAD_SAFETY
    	static pthread_once_t check_sigpipe_once = PTHREAD_ONCE_INIT;
    
    	/* Check only on first connection request */
    	pthread_once(&check_sigpipe_once, check_sigpipe_handler);
    #endif
    
    	if (!conn)
    		return 0;
    
    	/* Ensure our buffers are empty */
    	conn->inStart = conn->inCursor = conn->inEnd = 0;
    	conn->outCount = 0;
    
    	/*
    	 * Determine the parameters to pass to getaddrinfo_all.
    	 */
    
    	/* Initialize hint structure */
    	MemSet(&hint, 0, sizeof(hint));
    	hint.ai_socktype = SOCK_STREAM;
    	hint.ai_family = AF_UNSPEC;
    
    	/* Set up port number as a string */
    	if (conn->pgport != NULL && conn->pgport[0] != '\0')
    		portnum = atoi(conn->pgport);
    	else
    		portnum = DEF_PGPORT;
    	snprintf(portstr, sizeof(portstr), "%d", portnum);
    
    	if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')
    	{
    		/* Using pghostaddr avoids a hostname lookup */
    		node = conn->pghostaddr;
    		hint.ai_family = AF_UNSPEC;
    		hint.ai_flags = AI_NUMERICHOST;
    	}
    	else if (conn->pghost != NULL && conn->pghost[0] != '\0')
    	{
    		/* Using pghost, so we have to look-up the hostname */
    		node = conn->pghost;
    		hint.ai_family = AF_UNSPEC;
    	}
    #ifdef HAVE_UNIX_SOCKETS
    	else
    	{
    		/* pghostaddr and pghost are NULL, so use Unix domain socket */
    		node = NULL;
    		hint.ai_family = AF_UNIX;
    		UNIXSOCK_PATH(portstr, portnum, conn->pgunixsocket);
    	}
    #endif   /* HAVE_UNIX_SOCKETS */
    
    	/* Use getaddrinfo_all() to resolve the address */
    	ret = getaddrinfo_all(node, portstr, &hint, &addrs);
    	if (ret || !addrs)
    	{
    		if (node)
    			printfPQExpBuffer(&conn->errorMessage,
    							  libpq_gettext("could not translate host name \"%s\" to address: %s\n"),
    							  node, gai_strerror(ret));
    		else
    			printfPQExpBuffer(&conn->errorMessage,
    							  libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),
    							  portstr, gai_strerror(ret));
    		if (addrs)
    			freeaddrinfo_all(hint.ai_family, addrs);
    		goto connect_errReturn;
    	}
    
    #ifdef USE_SSL
    	/* setup values based on SSL mode */
    	if (conn->sslmode[0] == 'd')	/* "disable" */
    		conn->allow_ssl_try = false;
    	else if (conn->sslmode[0] == 'a')	/* "allow" */
    		conn->wait_ssl_try = true;
    #endif
    
    	/*
    	 * Set up to try to connect, with protocol 3.0 as the first attempt.
    	 */
    	conn->addrlist = addrs;
    	conn->addr_cur = addrs;
    	conn->addrlist_family = hint.ai_family;
    	conn->pversion = PG_PROTOCOL(3, 0);
    	conn->status = CONNECTION_NEEDED;
    
    	/*
    	 * The code for processing CONNECTION_NEEDED state is in
    	 * PQconnectPoll(), so that it can easily be re-executed if needed
    	 * again during the asynchronous startup process.  However, we must
    	 * run it once here, because callers expect a success return from this
    	 * routine to mean that we are in PGRES_POLLING_WRITING connection
    	 * state.
    	 */
    	if (PQconnectPoll(conn) == PGRES_POLLING_WRITING)
    		return 1;
    
    connect_errReturn:
    	if (conn->sock >= 0)
    	{
    		pqsecure_close(conn);
    		closesocket(conn->sock);
    		conn->sock = -1;
    	}
    	conn->status = CONNECTION_BAD;
    	return 0;
    }
    
    
    /*
     *		connectDBComplete
     *
     * Block and complete a connection.
     *
     * Returns 1 on success, 0 on failure.
     */
    static int
    connectDBComplete(PGconn *conn)
    {
    	PostgresPollingStatusType flag = PGRES_POLLING_WRITING;
    	time_t		finish_time = ((time_t) -1);
    
    	if (conn == NULL || conn->status == CONNECTION_BAD)
    		return 0;
    
    	/*
    	 * Set up a time limit, if connect_timeout isn't zero.
    	 */
    	if (conn->connect_timeout != NULL)
    	{
    		int			timeout = atoi(conn->connect_timeout);
    
    		if (timeout > 0)
    		{
    			/*
    			 * Rounding could cause connection to fail; need at least 2
    			 * secs
    			 */
    			if (timeout < 2)
    				timeout = 2;
    			/* calculate the finish time based on start + timeout */
    			finish_time = time(NULL) + timeout;
    		}
    	}
    
    	for (;;)
    	{
    		/*
    		 * Wait, if necessary.	Note that the initial state (just after
    		 * PQconnectStart) is to wait for the socket to select for
    		 * writing.
    		 */
    		switch (flag)
    		{
    			case PGRES_POLLING_OK:
    				return 1;		/* success! */
    
    			case PGRES_POLLING_READING:
    				if (pqWaitTimed(1, 0, conn, finish_time))
    				{
    					conn->status = CONNECTION_BAD;
    					return 0;
    				}
    				break;
    
    			case PGRES_POLLING_WRITING:
    				if (pqWaitTimed(0, 1, conn, finish_time))
    				{
    					conn->status = CONNECTION_BAD;
    					return 0;
    				}
    				break;
    
    			default:
    				/* Just in case we failed to set it in PQconnectPoll */
    				conn->status = CONNECTION_BAD;
    				return 0;
    		}
    
    		/*
    		 * Now try to advance the state machine.
    		 */
    		flag = PQconnectPoll(conn);
    	}
    }
    
    /* ----------------
     *		PQconnectPoll
     *
     * Poll an asynchronous connection.
     *
     * Returns a PostgresPollingStatusType.
     * Before calling this function, use select(2) to determine when data
     * has arrived..
     *
     * You must call PQfinish whether or not this fails.
     *
     * This function and PQconnectStart are intended to allow connections to be
     * made without blocking the execution of your program on remote I/O. However,
     * there are a number of caveats:
     *
     *	 o	If you call PQtrace, ensure that the stream object into which you trace
     *		will not block.
     *	 o	If you do not supply an IP address for the remote host (i.e. you
     *		supply a host name instead) then PQconnectStart will block on
     *		gethostbyname.	You will be fine if using Unix sockets (i.e. by
     *		supplying neither a host name nor a host address).
     *	 o	If your backend wants to use Kerberos authentication then you must
     *		supply both a host name and a host address, otherwise this function
     *		may block on gethostname.
     *
     * ----------------
     */
    PostgresPollingStatusType
    PQconnectPoll(PGconn *conn)
    {
    	PGresult   *res;
    	char		sebuf[256];
    
    	if (conn == NULL)
    		return PGRES_POLLING_FAILED;
    
    	/* Get the new data */
    	switch (conn->status)
    	{
    			/*
    			 * We really shouldn't have been polled in these two cases,
    			 * but we can handle it.
    			 */
    		case CONNECTION_BAD:
    			return PGRES_POLLING_FAILED;
    		case CONNECTION_OK:
    			return PGRES_POLLING_OK;
    
    			/* These are reading states */
    		case CONNECTION_AWAITING_RESPONSE:
    		case CONNECTION_AUTH_OK:
    			{
    				/* Load waiting data */
    				int			n = pqReadData(conn);
    
    				if (n < 0)
    					goto error_return;
    				if (n == 0)
    					return PGRES_POLLING_READING;
    
    				break;
    			}
    
    			/* These are writing states, so we just proceed. */
    		case CONNECTION_STARTED:
    		case CONNECTION_MADE:
    			break;
    
    			/* We allow pqSetenvPoll to decide whether to proceed. */
    		case CONNECTION_SETENV:
    			break;
    
    			/* Special cases: proceed without waiting. */
    		case CONNECTION_SSL_STARTUP:
    		case CONNECTION_NEEDED:
    			break;
    
    		default:
    			printfPQExpBuffer(&conn->errorMessage,
    							  libpq_gettext(
    											"invalid connection state, "
    							 "probably indicative of memory corruption\n"
    											));
    			goto error_return;
    	}
    
    
    keep_going:						/* We will come back to here until there
    								 * is nothing left to do. */
    	switch (conn->status)
    	{
    		case CONNECTION_NEEDED:
    			{
    				/*
    				 * Try to initiate a connection to one of the addresses
    				 * returned by getaddrinfo_all().  conn->addr_cur is the
    				 * next one to try.  We fail when we run out of addresses
    				 * (reporting the error returned for the *last*
    				 * alternative, which may not be what users expect :-().
    				 */
    				while (conn->addr_cur != NULL)
    				{
    					struct addrinfo *addr_cur = conn->addr_cur;
    
    					/* Remember current address for possible error msg */
    					memcpy(&conn->raddr.addr, addr_cur->ai_addr,
    						   addr_cur->ai_addrlen);
    					conn->raddr.salen = addr_cur->ai_addrlen;
    
    					/* Open a socket */
    					conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
    					if (conn->sock < 0)
    					{
    						/*
    						 * ignore socket() failure if we have more
    						 * addresses to try
    						 */
    						if (addr_cur->ai_next != NULL)
    						{
    							conn->addr_cur = addr_cur->ai_next;
    							continue;
    						}
    						printfPQExpBuffer(&conn->errorMessage,
    						  libpq_gettext("could not create socket: %s\n"),
    						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
    						break;
    					}
    
    					/*
    					 * Select socket options: no delay of outgoing data
    					 * for TCP sockets, and nonblock mode.	Fail if this
    					 * fails.
    					 */
    					if (!IS_AF_UNIX(addr_cur->ai_family))
    					{
    						if (!connectNoDelay(conn))
    						{
    							closesocket(conn->sock);
    							conn->sock = -1;
    							conn->addr_cur = addr_cur->ai_next;
    							continue;
    						}
    					}
    					if (connectMakeNonblocking(conn) == 0)
    					{
    						closesocket(conn->sock);
    						conn->sock = -1;
    						conn->addr_cur = addr_cur->ai_next;
    						continue;
    					}
    
    					/*
    					 * Start/make connection.  This should not block,
    					 * since we are in nonblock mode.  If it does, well,
    					 * too bad.
    					 */
    			retry_connect:
    					if (connect(conn->sock, addr_cur->ai_addr,
    								addr_cur->ai_addrlen) < 0)
    					{
    						if (SOCK_ERRNO == EINTR)
    							/* Interrupted system call - just try again */
    							goto retry_connect;
    						if (SOCK_ERRNO == EINPROGRESS ||
    							SOCK_ERRNO == EWOULDBLOCK ||
    							SOCK_ERRNO == 0)
    						{
    							/*
    							 * This is fine - we're in non-blocking mode,
    							 * and the connection is in progress.  Tell
    							 * caller to wait for write-ready on socket.
    							 */
    							conn->status = CONNECTION_STARTED;
    							return PGRES_POLLING_WRITING;
    						}
    						/* otherwise, trouble */
    					}
    					else
    					{
    						/*
    						 * Hm, we're connected already --- seems the
    						 * "nonblock connection" wasn't.  Advance the
    						 * state machine and go do the next stuff.
    						 */
    						conn->status = CONNECTION_STARTED;
    						goto keep_going;
    					}
    
    					/*
    					 * This connection failed --- set up error report,
    					 * then close socket (do it this way in case close()
    					 * affects the value of errno...).	We will ignore the
    					 * connect() failure and keep going if there are more
    					 * addresses.
    					 */
    					connectFailureMessage(conn, SOCK_ERRNO);
    					if (conn->sock >= 0)
    					{
    						closesocket(conn->sock);
    						conn->sock = -1;
    					}
    
    					/*
    					 * Try the next address, if any.
    					 */
    					conn->addr_cur = addr_cur->ai_next;
    				}				/* loop over addresses */
    
    				/*
    				 * Ooops, no more addresses.  An appropriate error message
    				 * is already set up, so just set the right status.
    				 */
    				goto error_return;
    			}
    
    		case CONNECTION_STARTED:
    			{
    				int			optval;
    				ACCEPT_TYPE_ARG3 optlen = sizeof(optval);
    
    				/*
    				 * Write ready, since we've made it here, so the
    				 * connection has been made ... or has failed.
    				 */
    
    				/*
    				 * Now check (using getsockopt) that there is not an error
    				 * state waiting for us on the socket.
    				 */
    
    				if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR,
    							   (char *) &optval, &optlen) == -1)
    				{
    					printfPQExpBuffer(&conn->errorMessage,
    									  libpq_gettext("could not get socket error status: %s\n"),
    						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
    					goto error_return;
    				}
    				else if (optval != 0)
    				{
    					/*
    					 * When using a nonblocking connect, we will typically
    					 * see connect failures at this point, so provide a
    					 * friendly error message.
    					 */
    					connectFailureMessage(conn, optval);
    
    					/*
    					 * If more addresses remain, keep trying, just as in
    					 * the case where connect() returned failure
    					 * immediately.
    					 */
    					if (conn->addr_cur->ai_next != NULL)
    					{
    						if (conn->sock >= 0)
    						{
    							closesocket(conn->sock);
    							conn->sock = -1;
    						}
    						conn->addr_cur = conn->addr_cur->ai_next;
    						conn->status = CONNECTION_NEEDED;
    						goto keep_going;
    					}
    					goto error_return;
    				}
    
    				/* Fill in the client address */
    				conn->laddr.salen = sizeof(conn->laddr.addr);
    				if (getsockname(conn->sock,
    								(struct sockaddr *) & conn->laddr.addr,
    								&conn->laddr.salen) < 0)
    				{
    					printfPQExpBuffer(&conn->errorMessage,
    									  libpq_gettext("could not get client address from socket: %s\n"),
    						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
    					goto error_return;
    				}
    
    				/*
    				 * Make sure we can write before advancing to next step.
    				 */
    				conn->status = CONNECTION_MADE;
    				return PGRES_POLLING_WRITING;
    			}
    
    		case CONNECTION_MADE:
    			{
    				char	   *startpacket;
    				int			packetlen;
    
    #ifdef USE_SSL
    
    				/*
    				 * If SSL is enabled and we haven't already got it
    				 * running, request it instead of sending the startup
    				 * message.
    				 */
    				if (IS_AF_UNIX(conn->raddr.addr.ss_family))
    				{
    					/* Don't bother requesting SSL over a Unix socket */
    					conn->allow_ssl_try = false;
    				}
    				if (conn->allow_ssl_try && !conn->wait_ssl_try &&
    					conn->ssl == NULL)
    				{
    					ProtocolVersion pv;
    
    					/*
    					 * Send the SSL request packet.
    					 *
    					 * Theoretically, this could block, but it really
    					 * shouldn't since we only got here if the socket is
    					 * write-ready.
    					 */
    					pv = htonl(NEGOTIATE_SSL_CODE);
    					if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK)
    					{
    						printfPQExpBuffer(&conn->errorMessage,
    										  libpq_gettext("could not send SSL negotiation packet: %s\n"),
    						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
    						goto error_return;
    					}
    					/* Ok, wait for response */
    					conn->status = CONNECTION_SSL_STARTUP;
    					return PGRES_POLLING_READING;
    				}
    #endif   /* USE_SSL */
    
    				/*
    				 * Build the startup packet.
    				 */
    				if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
    					startpacket = pqBuildStartupPacket3(conn, &packetlen,
    													 EnvironmentOptions);
    				else
    					startpacket = pqBuildStartupPacket2(conn, &packetlen,
    													 EnvironmentOptions);
    				if (!startpacket)
    				{
    					printfPQExpBuffer(&conn->errorMessage,
    									  libpq_gettext("out of memory\n"));
    					goto error_return;
    				}
    
    				/*
    				 * Send the startup packet.
    				 *
    				 * Theoretically, this could block, but it really shouldn't
    				 * since we only got here if the socket is write-ready.
    				 */
    				if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK)
    				{
    					printfPQExpBuffer(&conn->errorMessage,
    					libpq_gettext("could not send startup packet: %s\n"),
    						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
    					free(startpacket);
    					goto error_return;
    				}
    
    				free(startpacket);
    
    				conn->status = CONNECTION_AWAITING_RESPONSE;
    				return PGRES_POLLING_READING;
    			}
    
    			/*
    			 * Handle SSL negotiation: wait for postmaster messages and
    			 * respond as necessary.
    			 */
    		case CONNECTION_SSL_STARTUP:
    			{
    #ifdef USE_SSL
    				PostgresPollingStatusType pollres;
    
    				/*
    				 * On first time through, get the postmaster's response to
    				 * our SSL negotiation packet.	Be careful to read only
    				 * one byte (if there's more, it could be SSL data).
    				 */
    				if (conn->ssl == NULL)
    				{
    					char		SSLok;
    					int			nread;
    
    			retry_ssl_read:
    					nread = recv(conn->sock, &SSLok, 1, 0);
    					if (nread < 0)
    					{
    						if (SOCK_ERRNO == EINTR)
    							/* Interrupted system call - just try again */
    							goto retry_ssl_read;
    
    						printfPQExpBuffer(&conn->errorMessage,
    										  libpq_gettext("could not receive server response to SSL negotiation packet: %s\n"),
    						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
    						goto error_return;
    					}
    					if (nread == 0)
    						/* caller failed to wait for data */
    						return PGRES_POLLING_READING;
    					if (SSLok == 'S')
    					{
    						/* Do one-time setup; this creates conn->ssl */
    						if (pqsecure_initialize(conn) == -1)
    							goto error_return;
    					}
    					else if (SSLok == 'N')
    					{
    						if (conn->sslmode[0] == 'r')	/* "require" */
    						{
    							/* Require SSL, but server does not want it */
    							printfPQExpBuffer(&conn->errorMessage,
    											  libpq_gettext("server does not support SSL, but SSL was required\n"));
    							goto error_return;
    						}
    						/* Otherwise, proceed with normal startup */
    						conn->allow_ssl_try = false;
    						conn->status = CONNECTION_MADE;
    						return PGRES_POLLING_WRITING;
    					}
    					else if (SSLok == 'E')
    					{
    						/* Received error - probably protocol mismatch */
    						if (conn->Pfdebug)
    							fprintf(conn->Pfdebug, "Postmaster reports error, attempting fallback to pre-7.0.\n");
    						if (conn->sslmode[0] == 'r')	/* "require" */
    						{
    							/* Require SSL, but server is too old */
    							printfPQExpBuffer(&conn->errorMessage,
    											  libpq_gettext("server does not support SSL, but SSL was required\n"));
    							goto error_return;
    						}
    						/* Otherwise, try again without SSL */
    						conn->allow_ssl_try = false;
    						/* Assume it ain't gonna handle protocol 3, either */
    						conn->pversion = PG_PROTOCOL(2, 0);
    						/* Must drop the old connection */
    						closesocket(conn->sock);
    						conn->sock = -1;
    						conn->status = CONNECTION_NEEDED;
    						goto keep_going;
    					}
    					else
    					{
    						printfPQExpBuffer(&conn->errorMessage,
    										  libpq_gettext("received invalid response to SSL negotiation: %c\n"),
    										  SSLok);
    						goto error_return;
    					}
    				}
    
    				/*
    				 * Begin or continue the SSL negotiation process.
    				 */
    				pollres = pqsecure_open_client(conn);
    				if (pollres == PGRES_POLLING_OK)
    				{
    					/* SSL handshake done, ready to send startup packet */
    					conn->status = CONNECTION_MADE;
    					return PGRES_POLLING_WRITING;
    				}
    				return pollres;
    #else							/* !USE_SSL */
    				/* can't get here */
    				goto error_return;
    #endif   /* USE_SSL */
    			}
    
    			/*
    			 * Handle authentication exchange: wait for postmaster
    			 * messages and respond as necessary.
    			 */
    		case CONNECTION_AWAITING_RESPONSE:
    			{
    				char		beresp;
    				int			msgLength;
    				int			avail;
    				AuthRequest areq;
    
    				/*
    				 * Scan the message from current point (note that if we
    				 * find the message is incomplete, we will return without
    				 * advancing inStart, and resume here next time).
    				 */
    				conn->inCursor = conn->inStart;
    
    				/* Read type byte */
    				if (pqGetc(&beresp, conn))
    				{
    					/* We'll come back when there is more data */
    					return PGRES_POLLING_READING;
    				}
    
    				/*
    				 * Validate message type: we expect only an authentication
    				 * request or an error here.  Anything else probably means
    				 * it's not Postgres on the other end at all.
    				 */
    				if (!(beresp == 'R' || beresp == 'E'))
    				{
    					printfPQExpBuffer(&conn->errorMessage,
    									  libpq_gettext(
    								  "expected authentication request from "
    											"server, but received %c\n"),
    									  beresp);
    					goto error_return;
    				}
    
    				if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
    				{
    					/* Read message length word */
    					if (pqGetInt(&msgLength, 4, conn))
    					{
    						/* We'll come back when there is more data */
    						return PGRES_POLLING_READING;
    					}
    				}
    				else
    				{
    					/* Set phony message length to disable checks below */
    					msgLength = 8;
    				}
    
    				/*
    				 * Try to validate message length before using it.
    				 * Authentication requests can't be very large.  Errors
    				 * can be a little larger, but not huge.  If we see a
    				 * large apparent length in an error, it means we're
    				 * really talking to a pre-3.0-protocol server; cope.
    				 */
    				if (beresp == 'R' && (msgLength < 8 || msgLength > 100))
    				{
    					printfPQExpBuffer(&conn->errorMessage,
    									  libpq_gettext(
    								  "expected authentication request from "
    											"server, but received %c\n"),
    									  beresp);
    					goto error_return;
    				}
    
    				if (beresp == 'E' && (msgLength < 8 || msgLength > 30000))
    				{
    					/* Handle error from a pre-3.0 server */
    					conn->inCursor = conn->inStart + 1; /* reread data */
    					if (pqGets(&conn->errorMessage, conn))
    					{
    						/* We'll come back when there is more data */
    						return PGRES_POLLING_READING;
    					}
    					/* OK, we read the message; mark data consumed */
    					conn->inStart = conn->inCursor;
    
    					/*
    					 * The postmaster typically won't end its message with
    					 * a newline, so add one to conform to libpq
    					 * conventions.
    					 */
    					appendPQExpBufferChar(&conn->errorMessage, '\n');
    
    					/*
    					 * If we tried to open the connection in 3.0 protocol,
    					 * fall back to 2.0 protocol.
    					 */
    					if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
    					{
    						conn->pversion = PG_PROTOCOL(2, 0);
    						/* Must drop the old connection */
    						pqsecure_close(conn);
    						closesocket(conn->sock);
    						conn->sock = -1;
    						conn->status = CONNECTION_NEEDED;
    						goto keep_going;
    					}
    
    					goto error_return;
    				}
    
    				/*
    				 * Can't process if message body isn't all here yet.
    				 *
    				 * (In protocol 2.0 case, we are assuming messages carry at
    				 * least 4 bytes of data.)
    				 */
    				msgLength -= 4;
    				avail = conn->inEnd - conn->inCursor;
    				if (avail < msgLength)
    				{
    					/*
    					 * Before returning, try to enlarge the input buffer
    					 * if needed to hold the whole message; see notes in
    					 * pqParseInput3.
    					 */
    					if (pqCheckInBufferSpace(conn->inCursor + msgLength, conn))
    						goto error_return;
    					/* We'll come back when there is more data */
    					return PGRES_POLLING_READING;
    				}
    
    				/* Handle errors. */
    				if (beresp == 'E')
    				{
    					if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
    					{
    						if (pqGetErrorNotice3(conn, true))
    						{
    							/* We'll come back when there is more data */
    							return PGRES_POLLING_READING;
    						}
    					}
    					else
    					{
    						if (pqGets(&conn->errorMessage, conn))
    						{
    							/* We'll come back when there is more data */
    							return PGRES_POLLING_READING;
    						}
    					}
    					/* OK, we read the message; mark data consumed */
    					conn->inStart = conn->inCursor;
    
    #ifdef USE_SSL
    
    					/*
    					 * if sslmode is "allow" and we haven't tried an SSL
    					 * connection already, then retry with an SSL
    					 * connection
    					 */
    					if (conn->sslmode[0] == 'a' /* "allow" */
    						&& conn->ssl == NULL
    						&& conn->allow_ssl_try
    						&& conn->wait_ssl_try)
    					{
    						/* only retry once */
    						conn->wait_ssl_try = false;
    						/* Must drop the old connection */
    						closesocket(conn->sock);
    						conn->sock = -1;
    						conn->status = CONNECTION_NEEDED;
    						goto keep_going;
    					}
    
    					/*
    					 * if sslmode is "prefer" and we're in an SSL
    					 * connection, then do a non-SSL retry
    					 */
    					if (conn->sslmode[0] == 'p' /* "prefer" */
    						&& conn->ssl
    						&& conn->allow_ssl_try	/* redundant? */
    						&& !conn->wait_ssl_try) /* redundant? */
    					{
    						/* only retry once */
    						conn->allow_ssl_try = false;
    						/* Must drop the old connection */
    						pqsecure_close(conn);
    						closesocket(conn->sock);
    						conn->sock = -1;
    						conn->status = CONNECTION_NEEDED;
    						goto keep_going;
    					}
    #endif
    
    					goto error_return;
    				}
    
    				/* It is an authentication request. */
    				/* Get the type of request. */
    				if (pqGetInt((int *) &areq, 4, conn))
    				{
    					/* We'll come back when there are more data */
    					return PGRES_POLLING_READING;
    				}
    
    				/* Get the password salt if there is one. */
    				if (areq == AUTH_REQ_MD5)
    				{
    					if (pqGetnchar(conn->md5Salt,
    								   sizeof(conn->md5Salt), conn))
    					{
    						/* We'll come back when there are more data */
    						return PGRES_POLLING_READING;
    					}
    				}
    				if (areq == AUTH_REQ_CRYPT)
    				{
    					if (pqGetnchar(conn->cryptSalt,
    								   sizeof(conn->cryptSalt), conn))
    					{
    						/* We'll come back when there are more data */
    						return PGRES_POLLING_READING;
    					}
    				}
    
    				/*
    				 * OK, we successfully read the message; mark data
    				 * consumed
    				 */
    				conn->inStart = conn->inCursor;
    
    				/* Respond to the request if necessary. */
    
    				/*
    				 * Note that conn->pghost must be non-NULL if we are going
    				 * to avoid the Kerberos code doing a hostname look-up.
    				 */
    
    				/*
    				 * XXX fe-auth.c has not been fixed to support
    				 * PQExpBuffers, so:
    				 */
    				if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
    								conn->errorMessage.data) != STATUS_OK)
    				{
    					conn->errorMessage.len = strlen(conn->errorMessage.data);
    					goto error_return;
    				}
    				conn->errorMessage.len = strlen(conn->errorMessage.data);
    
    				/*
    				 * Just make sure that any data sent by fe_sendauth is
    				 * flushed out.  Although this theoretically could block,
    				 * it really shouldn't since we don't send large auth
    				 * responses.
    				 */
    				if (pqFlush(conn))
    					goto error_return;
    
    				if (areq == AUTH_REQ_OK)
    				{
    					/* We are done with authentication exchange */
    					conn->status = CONNECTION_AUTH_OK;
    
    					/*
    					 * Set asyncStatus so that PQsetResult will think that
    					 * what comes back next is the result of a query.  See
    					 * below.
    					 */
    					conn->asyncStatus = PGASYNC_BUSY;
    				}
    
    				/* Look to see if we have more data yet. */
    				goto keep_going;
    			}
    
    		case CONNECTION_AUTH_OK:
    			{
    				/*
    				 * Now we expect to hear from the backend. A ReadyForQuery
    				 * message indicates that startup is successful, but we
    				 * might also get an Error message indicating failure.
    				 * (Notice messages indicating nonfatal warnings are also
    				 * allowed by the protocol, as are ParameterStatus and
    				 * BackendKeyData messages.) Easiest way to handle this is
    				 * to let PQgetResult() read the messages. We just have to
    				 * fake it out about the state of the connection, by
    				 * setting asyncStatus = PGASYNC_BUSY (done above).
    				 */
    
    				if (PQisBusy(conn))
    					return PGRES_POLLING_READING;
    
    				res = PQgetResult(conn);
    
    				/*
    				 * NULL return indicating we have gone to IDLE state is
    				 * expected
    				 */
    				if (res)
    				{
    					if (res->resultStatus != PGRES_FATAL_ERROR)
    						printfPQExpBuffer(&conn->errorMessage,
    										  libpq_gettext("unexpected message from server during startup\n"));
    
    					/*
    					 * if the resultStatus is FATAL, then
    					 * conn->errorMessage already has a copy of the error;
    					 * needn't copy it back. But add a newline if it's not
    					 * there already, since postmaster error messages may
    					 * not have one.
    					 */
    					if (conn->errorMessage.len <= 0 ||
    						conn->errorMessage.data[conn->errorMessage.len - 1] != '\n')
    						appendPQExpBufferChar(&conn->errorMessage, '\n');
    					PQclear(res);
    					goto error_return;
    				}
    
    				/* We can release the address list now. */
    				freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
    				conn->addrlist = NULL;
    				conn->addr_cur = NULL;
    
    				/* Fire up post-connection housekeeping if needed */
    				if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
    				{
    					conn->status = CONNECTION_SETENV;
    					conn->setenv_state = SETENV_STATE_OPTION_SEND;
    					conn->next_eo = EnvironmentOptions;
    					return PGRES_POLLING_WRITING;
    				}
    
    				/* Otherwise, we are open for business! */
    				conn->status = CONNECTION_OK;
    				return PGRES_POLLING_OK;
    			}
    
    		case CONNECTION_SETENV:
    
    			/*
    			 * Do post-connection housekeeping (only needed in protocol
    			 * 2.0).
    			 *
    			 * We pretend that the connection is OK for the duration of these
    			 * queries.
    			 */
    			conn->status = CONNECTION_OK;
    
    			switch (pqSetenvPoll(conn))
    			{
    				case PGRES_POLLING_OK:	/* Success */
    					break;
    
    				case PGRES_POLLING_READING:		/* Still going */
    					conn->status = CONNECTION_SETENV;
    					return PGRES_POLLING_READING;
    
    				case PGRES_POLLING_WRITING:		/* Still going */
    					conn->status = CONNECTION_SETENV;
    					return PGRES_POLLING_WRITING;
    
    				default:
    					goto error_return;
    			}
    
    			/* We are open for business! */
    			conn->status = CONNECTION_OK;
    			return PGRES_POLLING_OK;
    
    		default:
    			printfPQExpBuffer(&conn->errorMessage,
    							  libpq_gettext(
    										  "invalid connection state %c, "
    							 "probably indicative of memory corruption\n"
    											),
    							  conn->status);
    			goto error_return;
    	}
    
    	/* Unreachable */
    
    error_return:
    
    	/*
    	 * We used to close the socket at this point, but that makes it
    	 * awkward for those above us if they wish to remove this socket from
    	 * their own records (an fd_set for example).  We'll just have this
    	 * socket closed when PQfinish is called (which is compulsory even
    	 * after an error, since the connection structure must be freed).
    	 */
    	conn->status = CONNECTION_BAD;
    	return PGRES_POLLING_FAILED;
    }
    
    
    /*
     * makeEmptyPGconn
     *	 - create a PGconn data structure with (as yet) no interesting data
     */
    static PGconn *
    makeEmptyPGconn(void)
    {
    	PGconn	   *conn;
    
    #ifdef WIN32
    	/* needed to use the static libpq under windows as well */
    	WSADATA		wsaData;
    
    	if (WSAStartup(MAKEWORD(1, 1), &wsaData))
    		return NULL;
    	WSASetLastError(0);
    #endif
    
    	conn = (PGconn *) malloc(sizeof(PGconn));
    	if (conn == NULL)
    		return conn;
    
    	/* Zero all pointers and booleans */
    	MemSet(conn, 0, sizeof(PGconn));
    
    	conn->noticeHooks.noticeRec = defaultNoticeReceiver;
    	conn->noticeHooks.noticeProc = defaultNoticeProcessor;
    	conn->status = CONNECTION_BAD;
    	conn->asyncStatus = PGASYNC_IDLE;
    	conn->xactStatus = PQTRANS_IDLE;
    	conn->setenv_state = SETENV_STATE_IDLE;
    	conn->client_encoding = PG_SQL_ASCII;
    	conn->verbosity = PQERRORS_DEFAULT;
    	conn->notifyList = DLNewList();
    	conn->sock = -1;
    #ifdef USE_SSL
    	conn->allow_ssl_try = true;
    	conn->wait_ssl_try = false;
    #endif
    
    	/*
    	 * We try to send at least 8K at a time, which is the usual size of
    	 * pipe buffers on Unix systems.  That way, when we are sending a
    	 * large amount of data, we avoid incurring extra kernel context swaps
    	 * for partial bufferloads.  The output buffer is initially made 16K
    	 * in size, and we try to dump it after accumulating 8K.
    	 *
    	 * With the same goal of minimizing context swaps, the input buffer will
    	 * be enlarged anytime it has less than 8K free, so we initially
    	 * allocate twice that.
    	 */
    	conn->inBufSize = 16 * 1024;
    	conn->inBuffer = (char *) malloc(conn->inBufSize);
    	conn->outBufSize = 16 * 1024;
    	conn->outBuffer = (char *) malloc(conn->outBufSize);
    	conn->nonblocking = FALSE;
    	initPQExpBuffer(&conn->errorMessage);
    	initPQExpBuffer(&conn->workBuffer);
    
    	if (conn->inBuffer == NULL ||
    		conn->outBuffer == NULL ||
    		conn->errorMessage.data == NULL ||
    		conn->workBuffer.data == NULL)
    	{
    		/* out of memory already :-( */
    		freePGconn(conn);
    		conn = NULL;
    	}
    
    	return conn;
    }
    
    /*
     * freePGconn
     *	 - free the PGconn data structure
     */
    static void
    freePGconn(PGconn *conn)
    {
    	pgParameterStatus *pstatus;
    
    	if (!conn)
    		return;
    
    	pqClearAsyncResult(conn);	/* deallocate result and curTuple */
    	if (conn->sock >= 0)
    	{
    		pqsecure_close(conn);
    		closesocket(conn->sock);
    	}
    	if (conn->pghost)
    		free(conn->pghost);
    	if (conn->pghostaddr)
    		free(conn->pghostaddr);
    	if (conn->pgport)
    		free(conn->pgport);
    	if (conn->pgunixsocket)
    		free(conn->pgunixsocket);
    	if (conn->pgtty)
    		free(conn->pgtty);
    	if (conn->connect_timeout)
    		free(conn->connect_timeout);
    	if (conn->pgoptions)
    		free(conn->pgoptions);
    	if (conn->dbName)
    		free(conn->dbName);
    	if (conn->pguser)
    		free(conn->pguser);
    	if (conn->pgpass)
    		free(conn->pgpass);
    	if (conn->sslmode)
    		free(conn->sslmode);
    	/* Note that conn->Pfdebug is not ours to close or free */
    	if (conn->notifyList)
    		DLFreeList(conn->notifyList);
    	freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
    	pstatus = conn->pstatus;
    	while (pstatus != NULL)
    	{
    		pgParameterStatus *prev = pstatus;
    
    		pstatus = pstatus->next;
    		free(prev);
    	}
    	if (conn->lobjfuncs)
    		free(conn->lobjfuncs);
    	if (conn->inBuffer)
    		free(conn->inBuffer);
    	if (conn->outBuffer)
    		free(conn->outBuffer);
    	termPQExpBuffer(&conn->errorMessage);
    	termPQExpBuffer(&conn->workBuffer);
    	free(conn);
    }
    
    /*
     * closePGconn
     *	 - properly close a connection to the backend
     */
    static void
    closePGconn(PGconn *conn)
    {
    	/*
    	 * Note that the protocol doesn't allow us to send Terminate messages
    	 * during the startup phase.
    	 */
    	if (conn->sock >= 0 && conn->status == CONNECTION_OK)
    	{
    		/*
    		 * Try to send "close connection" message to backend. Ignore any
    		 * error.
    		 */
    		pqPutMsgStart('X', false, conn);
    		pqPutMsgEnd(conn);
    		pqFlush(conn);
    	}
    
    	/*
    	 * must reset the blocking status so a possible reconnect will work
    	 * don't call PQsetnonblocking() because it will fail if it's unable
    	 * to flush the connection.
    	 */
    	conn->nonblocking = FALSE;
    
    	/*
    	 * Close the connection, reset all transient state, flush I/O buffers.
    	 */
    	if (conn->sock >= 0)
    	{
    		pqsecure_close(conn);
    		closesocket(conn->sock);
    	}
    	conn->sock = -1;
    	conn->status = CONNECTION_BAD;		/* Well, not really _bad_ - just
    										 * absent */
    	conn->asyncStatus = PGASYNC_IDLE;
    	pqClearAsyncResult(conn);	/* deallocate result and curTuple */
    	if (conn->lobjfuncs)
    		free(conn->lobjfuncs);
    	conn->lobjfuncs = NULL;
    	conn->inStart = conn->inCursor = conn->inEnd = 0;
    	conn->outCount = 0;
    }
    
    /*
       PQfinish:
    	  properly close a connection to the backend
    	  also frees the PGconn data structure so it shouldn't be re-used
    	  after this
    */
    void
    PQfinish(PGconn *conn)
    {
    	if (conn)
    	{
    		closePGconn(conn);
    		freePGconn(conn);
    	}
    }
    
    /* PQreset :
       resets the connection to the backend
       closes the existing connection and makes a new one
    */
    void
    PQreset(PGconn *conn)
    {
    	if (conn)
    	{
    		closePGconn(conn);
    
    		if (connectDBStart(conn))
    			(void) connectDBComplete(conn);
    	}
    }
    
    
    /* PQresetStart :
       resets the connection to the backend
       closes the existing connection and makes a new one
       Returns 1 on success, 0 on failure.
    */
    int
    PQresetStart(PGconn *conn)
    {
    	if (conn)
    	{
    		closePGconn(conn);
    
    		return connectDBStart(conn);
    	}
    
    	return 0;
    }
    
    
    /* PQresetPoll :
       resets the connection to the backend
       closes the existing connection and makes a new one
    */
    
    PostgresPollingStatusType
    PQresetPoll(PGconn *conn)
    {
    	if (conn)
    		return PQconnectPoll(conn);
    
    	return PGRES_POLLING_FAILED;
    }
    
    
    /*
     * PQrequestCancel: attempt to request cancellation of the current operation.
     *
     * The return value is TRUE if the cancel request was successfully
     * dispatched, FALSE if not (in which case conn->errorMessage is set).
     * Note: successful dispatch is no guarantee that there will be any effect at
     * the backend.  The application must read the operation result as usual.
     *
     * XXX it was a bad idea to have the error message returned in
     * conn->errorMessage, since it could overwrite a message already there.
     * Would be better to return it in a char array passed by the caller.
     *
     * CAUTION: we want this routine to be safely callable from a signal handler
     * (for example, an application might want to call it in a SIGINT handler).
     * This means we cannot use any C library routine that might be non-reentrant.
     * malloc/free are often non-reentrant, and anything that might call them is
     * just as dangerous.  We avoid sprintf here for that reason.  Building up
     * error messages with strcpy/strcat is tedious but should be quite safe.
     * We also save/restore errno in case the signal handler support doesn't.
     *
     * NOTE: this routine must not generate any error message longer than
     * INITIAL_EXPBUFFER_SIZE (currently 256), since we dare not try to
     * expand conn->errorMessage!
     */
    
    int
    PQrequestCancel(PGconn *conn)
    {
    	int			save_errno = SOCK_ERRNO;
    	int			tmpsock = -1;
    	char		sebuf[256];
    	struct
    	{
    		uint32		packetlen;
    		CancelRequestPacket cp;
    	}			crp;
    
    	/* Check we have an open connection */
    	if (!conn)
    		return FALSE;
    
    	if (conn->sock < 0)
    	{
    		strcpy(conn->errorMessage.data,
    			   "PQrequestCancel() -- connection is not open\n");
    		conn->errorMessage.len = strlen(conn->errorMessage.data);
    #ifdef WIN32
    		WSASetLastError(save_errno);
    #else
    		errno = save_errno;
    #endif
    		return FALSE;
    	}
    
    	/*
    	 * We need to open a temporary connection to the postmaster. Use the
    	 * information saved by connectDB to do this with only kernel calls.
    	 */
    	if ((tmpsock = socket(conn->raddr.addr.ss_family, SOCK_STREAM, 0)) < 0)
    	{
    		strcpy(conn->errorMessage.data,
    			   "PQrequestCancel() -- socket() failed: ");
    		goto cancel_errReturn;
    	}
    retry3:
    	if (connect(tmpsock, (struct sockaddr *) & conn->raddr.addr,
    				conn->raddr.salen) < 0)
    	{
    		if (SOCK_ERRNO == EINTR)
    			/* Interrupted system call - we'll just try again */
    			goto retry3;
    		strcpy(conn->errorMessage.data,
    			   "PQrequestCancel() -- connect() failed: ");
    		goto cancel_errReturn;
    	}
    
    	/*
    	 * We needn't set nonblocking I/O or NODELAY options here.
    	 */
    
    	/* Create and send the cancel request packet. */
    
    	crp.packetlen = htonl((uint32) sizeof(crp));
    	crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE);
    	crp.cp.backendPID = htonl(conn->be_pid);
    	crp.cp.cancelAuthCode = htonl(conn->be_key);
    
    retry4:
    	if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
    	{
    		if (SOCK_ERRNO == EINTR)
    			/* Interrupted system call - we'll just try again */
    			goto retry4;
    		strcpy(conn->errorMessage.data,
    			   "PQrequestCancel() -- send() failed: ");
    		goto cancel_errReturn;
    	}
    
    	/*
    	 * Wait for the postmaster to close the connection, which indicates that
    	 * it's processed the request.  Without this delay, we might issue another
    	 * command only to find that our cancel zaps that command instead of the
    	 * one we thought we were canceling.  Note we don't actually expect this
    	 * read to obtain any data, we are just waiting for EOF to be signaled.
    	 */
    retry5:
    	if (recv(tmpsock, (char *) &crp, 1, 0) < 0)
    	{
    		if (SOCK_ERRNO == EINTR)
    			/* Interrupted system call - we'll just try again */
    			goto retry5;
    		/* we ignore other error conditions */
    	}
    
    	/* All done */
    	closesocket(tmpsock);
    #ifdef WIN32
    	WSASetLastError(save_errno);
    #else
    	errno = save_errno;
    #endif
    
    	return TRUE;
    
    cancel_errReturn:
    	strcat(conn->errorMessage.data, SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
    	strcat(conn->errorMessage.data, "\n");
    	conn->errorMessage.len = strlen(conn->errorMessage.data);
    	if (tmpsock >= 0)
    	{
    		closesocket(tmpsock);
    #ifdef WIN32
    		WSASetLastError(save_errno);
    #else
    		errno = save_errno;
    #endif
    	}
    	return FALSE;
    }
    
    
    /*
     * pqPacketSend() -- convenience routine to send a message to server.
     *
     * pack_type: the single-byte message type code.  (Pass zero for startup
     * packets, which have no message type code.)
     *
     * buf, buf_len: contents of message.  The given length includes only what
     * is in buf; the message type and message length fields are added here.
     *
     * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
     * SIDE_EFFECTS: may block.
     *
     * Note: all messages sent with this routine have a length word, whether
     * it's protocol 2.0 or 3.0.
     */
    int
    pqPacketSend(PGconn *conn, char pack_type,
    			 const void *buf, size_t buf_len)
    {
    	/* Start the message. */
    	if (pqPutMsgStart(pack_type, true, conn))
    		return STATUS_ERROR;
    
    	/* Send the message body. */
    	if (pqPutnchar(buf, buf_len, conn))
    		return STATUS_ERROR;
    
    	/* Finish the message. */
    	if (pqPutMsgEnd(conn))
    		return STATUS_ERROR;
    
    	/* Flush to ensure backend gets it. */
    	if (pqFlush(conn))
    		return STATUS_ERROR;
    
    	return STATUS_OK;
    }
    
    
    
    #define MAXBUFSIZE 256
    
    static int
    parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
    {
    	char	   *service = conninfo_getval(options, "service");
    	char		serviceFile[MAXPGPATH];
    	bool		group_found = false;
    	int			linenr = 0,
    				i;
    
    	/*
    	 * We have to special-case the environment variable PGSERVICE here,
    	 * since this is and should be called before inserting environment
    	 * defaults for other connection options.
    	 */
    	if (service == NULL)
    		service = getenv("PGSERVICE");
    
    	/*
    	 *	This could be used by any application so we can't use the binary
    	 *	location to find our config files.
    	 */	
    	snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
    			 getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);
    
    	if (service != NULL)
    	{
    		FILE	   *f;
    		char		buf[MAXBUFSIZE],
    				   *line;
    
    		f = fopen(serviceFile, "r");
    		if (f == NULL)
    		{
    			printfPQExpBuffer(errorMessage, "ERROR: Service file '%s' not found\n",
    							  serviceFile);
    			return 1;
    		}
    
    		while ((line = fgets(buf, MAXBUFSIZE - 1, f)) != NULL)
    		{
    			linenr++;
    
    			if (strlen(line) >= MAXBUFSIZE - 2)
    			{
    				fclose(f);
    				printfPQExpBuffer(errorMessage,
    						"ERROR: line %d too long in service file '%s'\n",
    								  linenr,
    								  serviceFile);
    				return 2;
    			}
    
    			/* ignore EOL at end of line */
    			if (strlen(line) && line[strlen(line) - 1] == '\n')
    				line[strlen(line) - 1] = 0;
    
    			/* ignore leading blanks */
    			while (*line && isspace((unsigned char) line[0]))
    				line++;
    
    			/* ignore comments and empty lines */
    			if (strlen(line) == 0 || line[0] == '#')
    				continue;
    
    			/* Check for right groupname */
    			if (line[0] == '[')
    			{
    				if (group_found)
    				{
    					/* group info already read */
    					fclose(f);
    					return 0;
    				}
    
    				if (strncmp(line + 1, service, strlen(service)) == 0 &&
    					line[strlen(service) + 1] == ']')
    					group_found = true;
    				else
    					group_found = false;
    			}
    			else
    			{
    				if (group_found)
    				{
    					/*
    					 * Finally, we are in the right group and can parse
    					 * the line
    					 */
    					char	   *key,
    							   *val;
    					bool		found_keyword;
    
    					key = line;
    					val = strchr(line, '=');
    					if (val == NULL)
    					{
    						printfPQExpBuffer(errorMessage,
    										  "ERROR: syntax error in service file '%s', line %d\n",
    										  serviceFile,
    										  linenr);
    						fclose(f);
    						return 3;
    					}
    					*val++ = '\0';
    
    					/*
    					 * Set the parameter --- but don't override any
    					 * previous explicit setting.
    					 */
    					found_keyword = false;
    					for (i = 0; options[i].keyword; i++)
    					{
    						if (strcmp(options[i].keyword, key) == 0)
    						{
    							if (options[i].val == NULL)
    								options[i].val = strdup(val);
    							found_keyword = true;
    							break;
    						}
    					}
    
    					if (!found_keyword)
    					{
    						printfPQExpBuffer(errorMessage,
    										  "ERROR: syntax error in service file '%s', line %d\n",
    										  serviceFile,
    										  linenr);
    						fclose(f);
    						return 3;
    					}
    				}
    			}
    		}
    
    		fclose(f);
    	}
    
    	return 0;
    }
    
    
    /*
     * Conninfo parser routine
     *
     * If successful, a malloc'd PQconninfoOption array is returned.
     * If not successful, NULL is returned and an error message is
     * left in errorMessage.
     */
    static PQconninfoOption *
    conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
    {
    	char	   *pname;
    	char	   *pval;
    	char	   *buf;
    	char	   *tmp;
    	char	   *cp;
    	char	   *cp2;
    	PQconninfoOption *options;
    	PQconninfoOption *option;
    	char		errortmp[PQERRORMSG_LENGTH];
    
    	/* Make a working copy of PQconninfoOptions */
    	options = malloc(sizeof(PQconninfoOptions));
    	if (options == NULL)
    	{
    		printfPQExpBuffer(errorMessage,
    						  libpq_gettext("out of memory\n"));
    		return NULL;
    	}
    	memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
    
    	/* Need a modifiable copy of the input string */
    	if ((buf = strdup(conninfo)) == NULL)
    	{
    		printfPQExpBuffer(errorMessage,
    						  libpq_gettext("out of memory\n"));
    		PQconninfoFree(options);
    		return NULL;
    	}
    	cp = buf;
    
    	while (*cp)
    	{
    		/* Skip blanks before the parameter name */
    		if (isspace((unsigned char) *cp))
    		{
    			cp++;
    			continue;
    		}
    
    		/* Get the parameter name */
    		pname = cp;
    		while (*cp)
    		{
    			if (*cp == '=')
    				break;
    			if (isspace((unsigned char) *cp))
    			{
    				*cp++ = '\0';
    				while (*cp)
    				{
    					if (!isspace((unsigned char) *cp))
    						break;
    					cp++;
    				}
    				break;
    			}
    			cp++;
    		}
    
    		/* Check that there is a following '=' */
    		if (*cp != '=')
    		{
    			printfPQExpBuffer(errorMessage,
    							  libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"),
    							  pname);
    			PQconninfoFree(options);
    			free(buf);
    			return NULL;
    		}
    		*cp++ = '\0';
    
    		/* Skip blanks after the '=' */
    		while (*cp)
    		{
    			if (!isspace((unsigned char) *cp))
    				break;
    			cp++;
    		}
    
    		/* Get the parameter value */
    		pval = cp;
    
    		if (*cp != '\'')
    		{
    			cp2 = pval;
    			while (*cp)
    			{
    				if (isspace((unsigned char) *cp))
    				{
    					*cp++ = '\0';
    					break;
    				}
    				if (*cp == '\\')
    				{
    					cp++;
    					if (*cp != '\0')
    						*cp2++ = *cp++;
    				}
    				else
    					*cp2++ = *cp++;
    			}
    			*cp2 = '\0';
    		}
    		else
    		{
    			cp2 = pval;
    			cp++;
    			for (;;)
    			{
    				if (*cp == '\0')
    				{
    					printfPQExpBuffer(errorMessage,
    									  libpq_gettext("unterminated quoted string in connection info string\n"));
    					PQconninfoFree(options);
    					free(buf);
    					return NULL;
    				}
    				if (*cp == '\\')
    				{
    					cp++;
    					if (*cp != '\0')
    						*cp2++ = *cp++;
    					continue;
    				}
    				if (*cp == '\'')
    				{
    					*cp2 = '\0';
    					cp++;
    					break;
    				}
    				*cp2++ = *cp++;
    			}
    		}
    
    		/*
    		 * Now we have the name and the value. Search for the param
    		 * record.
    		 */
    		for (option = options; option->keyword != NULL; option++)
    		{
    			if (strcmp(option->keyword, pname) == 0)
    				break;
    		}
    		if (option->keyword == NULL)
    		{
    			printfPQExpBuffer(errorMessage,
    					 libpq_gettext("invalid connection option \"%s\"\n"),
    							  pname);
    			PQconninfoFree(options);
    			free(buf);
    			return NULL;
    		}
    
    		/*
    		 * Store the value
    		 */
    		if (option->val)
    			free(option->val);
    		option->val = strdup(pval);
    
    	}
    
    	/* Done with the modifiable input string */
    	free(buf);
    
    	/*
    	 * If there's a service spec, use it to obtain any
    	 * not-explicitly-given parameters.
    	 */
    	if (parseServiceInfo(options, errorMessage))
    	{
    		PQconninfoFree(options);
    		return NULL;
    	}
    
    	/*
    	 * Get the fallback resources for parameters not specified in the
    	 * conninfo string nor the service.
    	 */
    	for (option = options; option->keyword != NULL; option++)
    	{
    		if (option->val != NULL)
    			continue;			/* Value was in conninfo or service */
    
    		/*
    		 * Try to get the environment variable fallback
    		 */
    		if (option->envvar != NULL)
    		{
    			if ((tmp = getenv(option->envvar)) != NULL)
    			{
    				option->val = strdup(tmp);
    				continue;
    			}
    		}
    
    		/*
    		 * No environment variable specified or this one isn't set - try
    		 * compiled in
    		 */
    		if (option->compiled != NULL)
    		{
    			option->val = strdup(option->compiled);
    			continue;
    		}
    
    		/*
    		 * Special handling for user
    		 */
    		if (strcmp(option->keyword, "user") == 0)
    		{
    			option->val = fe_getauthname(errortmp);
    			/* note any error message is thrown away */
    			continue;
    		}
    	}
    
    	return options;
    }
    
    
    static char *
    conninfo_getval(PQconninfoOption *connOptions,
    				const char *keyword)
    {
    	PQconninfoOption *option;
    
    	for (option = connOptions; option->keyword != NULL; option++)
    	{
    		if (strcmp(option->keyword, keyword) == 0)
    			return option->val;
    	}
    
    	return NULL;
    }
    
    
    void
    PQconninfoFree(PQconninfoOption *connOptions)
    {
    	PQconninfoOption *option;
    
    	if (connOptions == NULL)
    		return;
    
    	for (option = connOptions; option->keyword != NULL; option++)
    	{
    		if (option->val != NULL)
    			free(option->val);
    	}
    	free(connOptions);
    }
    
    
    /* =========== accessor functions for PGconn ========= */
    char *
    PQdb(const PGconn *conn)
    {
    	if (!conn)
    		return NULL;
    	return conn->dbName;
    }
    
    char *
    PQuser(const PGconn *conn)
    {
    	if (!conn)
    		return NULL;
    	return conn->pguser;
    }
    
    char *
    PQpass(const PGconn *conn)
    {
    	if (!conn)
    		return NULL;
    	return conn->pgpass;
    }
    
    char *
    PQhost(const PGconn *conn)
    {
    	if (!conn)
    		return NULL;
    	return conn->pghost ? conn->pghost : conn->pgunixsocket;
    }
    
    char *
    PQport(const PGconn *conn)
    {
    	if (!conn)
    		return NULL;
    	return conn->pgport;
    }
    
    char *
    PQtty(const PGconn *conn)
    {
    	if (!conn)
    		return NULL;
    	return conn->pgtty;
    }
    
    char *
    PQoptions(const PGconn *conn)
    {
    	if (!conn)
    		return NULL;
    	return conn->pgoptions;
    }
    
    ConnStatusType
    PQstatus(const PGconn *conn)
    {
    	if (!conn)
    		return CONNECTION_BAD;
    	return conn->status;
    }
    
    PGTransactionStatusType
    PQtransactionStatus(const PGconn *conn)
    {
    	if (!conn || conn->status != CONNECTION_OK)
    		return PQTRANS_UNKNOWN;
    	if (conn->asyncStatus != PGASYNC_IDLE)
    		return PQTRANS_ACTIVE;
    	return conn->xactStatus;
    }
    
    const char *
    PQparameterStatus(const PGconn *conn, const char *paramName)
    {
    	const pgParameterStatus *pstatus;
    
    	if (!conn || !paramName)
    		return NULL;
    	for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next)
    	{
    		if (strcmp(pstatus->name, paramName) == 0)
    			return pstatus->value;
    	}
    	return NULL;
    }
    
    int
    PQprotocolVersion(const PGconn *conn)
    {
    	if (!conn)
    		return 0;
    	if (conn->status == CONNECTION_BAD)
    		return 0;
    	return PG_PROTOCOL_MAJOR(conn->pversion);
    }
    
    char *
    PQerrorMessage(const PGconn *conn)
    {
    	if (!conn)
    		return libpq_gettext("connection pointer is NULL\n");
    
    	return conn->errorMessage.data;
    }
    
    int
    PQsocket(const PGconn *conn)
    {
    	if (!conn)
    		return -1;
    	return conn->sock;
    }
    
    int
    PQbackendPID(const PGconn *conn)
    {
    	if (!conn || conn->status != CONNECTION_OK)
    		return 0;
    	return conn->be_pid;
    }
    
    int
    PQclientEncoding(const PGconn *conn)
    {
    	if (!conn || conn->status != CONNECTION_OK)
    		return -1;
    	return conn->client_encoding;
    }
    
    int
    PQsetClientEncoding(PGconn *conn, const char *encoding)
    {
    	char		qbuf[128];
    	static const char query[] = "set client_encoding to '%s'";
    	PGresult   *res;
    	int			status;
    
    	if (!conn || conn->status != CONNECTION_OK)
    		return -1;
    
    	if (!encoding)
    		return -1;
    
    	/* check query buffer overflow */
    	if (sizeof(qbuf) < (sizeof(query) + strlen(encoding)))
    		return -1;
    
    	/* ok, now send a query */
    	sprintf(qbuf, query, encoding);
    	res = PQexec(conn, qbuf);
    
    	if (res == NULL)
    		return -1;
    	if (res->resultStatus != PGRES_COMMAND_OK)
    		status = -1;
    	else
    	{
    		/* change libpq internal encoding */
    		conn->client_encoding = pg_char_to_encoding(encoding);
    		status = 0;				/* everything is ok */
    	}
    	PQclear(res);
    	return (status);
    }
    
    PGVerbosity
    PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity)
    {
    	PGVerbosity old;
    
    	if (!conn)
    		return PQERRORS_DEFAULT;
    	old = conn->verbosity;
    	conn->verbosity = verbosity;
    	return old;
    }
    
    void
    PQtrace(PGconn *conn, FILE *debug_port)
    {
    	if (conn == NULL)
    		return;
    	PQuntrace(conn);
    	conn->Pfdebug = debug_port;
    }
    
    void
    PQuntrace(PGconn *conn)
    {
    	if (conn == NULL)
    		return;
    	if (conn->Pfdebug)
    	{
    		fflush(conn->Pfdebug);
    		conn->Pfdebug = NULL;
    	}
    }
    
    PQnoticeReceiver
    PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg)
    {
    	PQnoticeReceiver old;
    
    	if (conn == NULL)
    		return NULL;
    
    	old = conn->noticeHooks.noticeRec;
    	if (proc)
    	{
    		conn->noticeHooks.noticeRec = proc;
    		conn->noticeHooks.noticeRecArg = arg;
    	}
    	return old;
    }
    
    PQnoticeProcessor
    PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
    {
    	PQnoticeProcessor old;
    
    	if (conn == NULL)
    		return NULL;
    
    	old = conn->noticeHooks.noticeProc;
    	if (proc)
    	{
    		conn->noticeHooks.noticeProc = proc;
    		conn->noticeHooks.noticeProcArg = arg;
    	}
    	return old;
    }
    
    /*
     * The default notice message receiver just gets the standard notice text
     * and sends it to the notice processor.  This two-level setup exists
     * mostly for backwards compatibility; perhaps we should deprecate use of
     * PQsetNoticeProcessor?
     */
    static void
    defaultNoticeReceiver(void *arg, const PGresult *res)
    {
    	(void) arg;					/* not used */
    	if (res->noticeHooks.noticeProc != NULL)
    		(*res->noticeHooks.noticeProc) (res->noticeHooks.noticeProcArg,
    										PQresultErrorMessage(res));
    }
    
    /*
     * The default notice message processor just prints the
     * message on stderr.  Applications can override this if they
     * want the messages to go elsewhere (a window, for example).
     * Note that simply discarding notices is probably a bad idea.
     */
    static void
    defaultNoticeProcessor(void *arg, const char *message)
    {
    	(void) arg;					/* not used */
    	/* Note: we expect the supplied string to end with a newline already. */
    	fprintf(stderr, "%s", message);
    }
    
    /*
     * returns a pointer to the next token or NULL if the current
     * token doesn't match
     */
    static char *
    pwdfMatchesString(char *buf, char *token)
    {
    	char	   *tbuf,
    			   *ttok;
    	bool		bslash = false;
    
    	if (buf == NULL || token == NULL)
    		return NULL;
    	tbuf = buf;
    	ttok = token;
    	if (*tbuf == '*')
    		return tbuf + 2;
    	while (*tbuf != 0)
    	{
    		if (*tbuf == '\\' && !bslash)
    		{
    			tbuf++;
    			bslash = true;
    		}
    		if (*tbuf == ':' && *ttok == 0 && !bslash)
    			return tbuf + 1;
    		bslash = false;
    		if (*ttok == 0)
    			return NULL;
    		if (*tbuf == *ttok)
    		{
    			tbuf++;
    			ttok++;
    		}
    		else
    			return NULL;
    	}
    	return NULL;
    }
    
    /* Get a password from the password file. Return value is malloc'd. */
    static char *
    PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
    {
    	FILE	   *fp;
    	char	   *pgpassfile;
    	char	   *home;
    	struct stat stat_buf;
    
    #define LINELEN NAMEDATALEN*5
    	char		buf[LINELEN];
    
    	if (dbname == NULL || strlen(dbname) == 0)
    		return NULL;
    
    	if (username == NULL || strlen(username) == 0)
    		return NULL;
    
    	if (hostname == NULL)
    		hostname = DefaultHost;
    
    	if (port == NULL)
    		port = DEF_PGPORT_STR;
    
    	/* Look for it in the home dir */
    	home = getenv("HOME");
    	if (!home)
    		return NULL;
    
    	pgpassfile = malloc(strlen(home) + 1 + strlen(PGPASSFILE) + 1);
    	if (!pgpassfile)
    	{
    		fprintf(stderr, libpq_gettext("out of memory\n"));
    		return NULL;
    	}
    
    	sprintf(pgpassfile, "%s/%s", home, PGPASSFILE);
    
    	/* If password file cannot be opened, ignore it. */
    	if (stat(pgpassfile, &stat_buf) == -1)
    	{
    		free(pgpassfile);
    		return NULL;
    	}
    
    #ifndef WIN32
    	/* If password file is insecure, alert the user and ignore it. */
    	if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
    	{
    		fprintf(stderr,
    				libpq_gettext("WARNING: Password file %s has world or group read access; permission should be u=rw (0600)\n"),
    				pgpassfile);
    		free(pgpassfile);
    		return NULL;
    	}
    #endif
    
    	fp = fopen(pgpassfile, "r");
    	free(pgpassfile);
    	if (fp == NULL)
    		return NULL;
    
    	while (!feof(fp))
    	{
    		char	   *t = buf,
    				   *ret;
    		int			len;
    
    		fgets(buf, LINELEN - 1, fp);
    
    		len = strlen(buf);
    		if (len == 0)
    			continue;
    
    		/* Remove trailing newline */
    		if (buf[len - 1] == '\n')
    			buf[len - 1] = 0;
    
    		if ((t = pwdfMatchesString(t, hostname)) == NULL ||
    			(t = pwdfMatchesString(t, port)) == NULL ||
    			(t = pwdfMatchesString(t, dbname)) == NULL ||
    			(t = pwdfMatchesString(t, username)) == NULL)
    			continue;
    		ret = strdup(t);
    		fclose(fp);
    		return ret;
    	}
    
    	fclose(fp);
    	return NULL;
    
    #undef LINELEN
    }
    
    /*
     * To keep the API consistent, the locking stubs are always provided, even
     * if they are not required.
     */
    
    void
    PQinitSSL(int do_init)
    {
    #ifdef USE_SSL
    	pq_initssllib = do_init;
    #endif
    }
    
    static pgthreadlock_t default_threadlock;
    static void
    default_threadlock(int acquire)
    {
    #ifdef ENABLE_THREAD_SAFETY
    	static pthread_mutex_t singlethread_lock = PTHREAD_MUTEX_INITIALIZER;
    	if (acquire)
    		pthread_mutex_lock(&singlethread_lock);
    	else
    		pthread_mutex_unlock(&singlethread_lock);
    #endif
    }
    
    pgthreadlock_t *g_threadlock = default_threadlock;
    
    pgthreadlock_t *
    PQregisterThreadLock(pgthreadlock_t *newhandler)
    {
    	pgthreadlock_t *prev;
    
    	prev = g_threadlock;
    	if (newhandler)
    		g_threadlock = newhandler;
    	else
    		g_threadlock = default_threadlock;
    	return prev;
    }