diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/psql/startup.c | 23 | ||||
-rw-r--r-- | src/interfaces/libpq/exports.txt | 4 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 320 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-fe.h | 4 |
4 files changed, 316 insertions, 35 deletions
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 6ffe593786a..b29c84fdaec 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2010, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.158 2010/01/02 16:57:59 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.159 2010/01/28 06:28:26 joe Exp $ */ #include "postgres_fe.h" @@ -90,6 +90,8 @@ main(int argc, char *argv[]) char *password = NULL; char *password_prompt = NULL; bool new_pass; + const char *keywords[] = {"host","port","dbname","user", + "password","application_name",NULL}; set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("psql")); @@ -171,11 +173,20 @@ main(int argc, char *argv[]) /* loop until we have a password if requested by backend */ do { - new_pass = false; - pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL, - options.action == ACT_LIST_DB && options.dbname == NULL ? - "postgres" : options.dbname, - options.username, password); + const char *values[] = { + options.host, + options.port, + (options.action == ACT_LIST_DB && + options.dbname == NULL) ? "postgres" : options.dbname, + options.username, + password, + pset.progname, + NULL + }; + + new_pass = false; + + pset.db = PQconnectdbParams(keywords, values); if (PQstatus(pset.db) == CONNECTION_BAD && PQconnectionNeedsPassword(pset.db) && diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt index a7a1b94e111..bdaa5857b7f 100644 --- a/src/interfaces/libpq/exports.txt +++ b/src/interfaces/libpq/exports.txt @@ -1,4 +1,4 @@ -# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.24 2010/01/21 14:58:53 rhaas Exp $ +# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.25 2010/01/28 06:28:26 joe Exp $ # Functions to be exported by libpq DLLs PQconnectdb 1 PQsetdbLogin 2 @@ -155,3 +155,5 @@ PQconninfoParse 152 PQinitOpenSSL 153 PQescapeLiteral 154 PQescapeIdentifier 155 +PQconnectdbParams 156 +PQconnectStartParams 157 diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 4d7be078c07..048c438527b 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.384 2010/01/20 21:15:21 petere Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.385 2010/01/28 06:28:26 joe Exp $ * *------------------------------------------------------------------------- */ @@ -262,10 +262,14 @@ static bool connectOptions2(PGconn *conn); static int connectDBStart(PGconn *conn); static int connectDBComplete(PGconn *conn); static PGconn *makeEmptyPGconn(void); +static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions); static void freePGconn(PGconn *conn); static void closePGconn(PGconn *conn); static PQconninfoOption *conninfo_parse(const char *conninfo, PQExpBuffer errorMessage, bool use_defaults); +static PQconninfoOption *conninfo_array_parse(const char **keywords, + const char **values, PQExpBuffer errorMessage, + bool use_defaults); static char *conninfo_getval(PQconninfoOption *connOptions, const char *keyword); static void defaultNoticeReceiver(void *arg, const PGresult *res); @@ -290,23 +294,60 @@ pgthreadlock_t pg_g_threadlock = default_threadlock; /* * Connecting to a Database * - * There are now four different ways a user of this API can connect to the + * There are now six 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. + * function PQconnectdb or PQconnectdbParams. The former accepts a string + * of option = value pairs which must be parsed; the latter takes two NULL + * terminated arrays instead. * * To connect in an asynchronous (non-blocking) manner, use the functions - * PQconnectStart, and PQconnectPoll. + * PQconnectStart or PQconnectStartParams (which differ in the same way as + * PQconnectdb and PQconnectdbParams) and PQconnectPoll. * * Internally, the static functions connectDBStart, connectDBComplete * are part of the connection procedure. */ /* + * PQconnectdbParams + * + * establishes a connection to a postgres backend through the postmaster + * using connection information in two arrays. + * + * The keywords array is defined as + * + * const char *params[] = {"option1", "option2", NULL} + * + * The values array is defined as + * + * const char *values[] = {"value1", "value2", NULL} + * + * 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 * +PQconnectdbParams(const char **keywords, const char **values) +{ + PGconn *conn = PQconnectStartParams(keywords, values); + + if (conn && conn->status != CONNECTION_BAD) + (void) connectDBComplete(conn); + + return conn; + +} + +/* * PQconnectdb * * establishes a connection to a postgres backend through the postmaster @@ -340,12 +381,12 @@ PQconnectdb(const char *conninfo) } /* - * PQconnectStart + * PQconnectStartParams * * Begins the establishment of a connection to a postgres backend through the - * postmaster using connection information in a string. + * postmaster using connection information in a struct. * - * See comment for PQconnectdb for the definition of the string format. + * See comment for PQconnectdbParams 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 @@ -359,9 +400,10 @@ PQconnectdb(const char *conninfo) * See PQconnectPoll for more info. */ PGconn * -PQconnectStart(const char *conninfo) +PQconnectStartParams(const char **keywords, const char **values) { - PGconn *conn; + PGconn *conn; + PQconninfoOption *connOptions; /* * Allocate memory for the conn structure @@ -371,10 +413,26 @@ PQconnectStart(const char *conninfo) return NULL; /* - * Parse the conninfo string + * Parse the conninfo arrays */ - if (!connectOptions1(conn, conninfo)) - return conn; + connOptions = conninfo_array_parse(keywords, values, + &conn->errorMessage, true); + if (connOptions == NULL) + { + conn->status = CONNECTION_BAD; + /* errorMessage is already set */ + return false; + } + + /* + * Move option values into conn structure + */ + fillPGconn(conn, connOptions); + + /* + * Free the option info - all is in conn now + */ + PQconninfoFree(connOptions); /* * Compute derived options @@ -395,33 +453,65 @@ PQconnectStart(const char *conninfo) } /* - * connectOptions1 + * PQconnectStart * - * 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.) + * Begins the establishment of a connection to a postgres backend through the + * postmaster using connection information in a string. * - * Returns true if OK, false if trouble (in which case errorMessage is set - * and so is conn->status). + * 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. */ -static bool -connectOptions1(PGconn *conn, const char *conninfo) +PGconn * +PQconnectStart(const char *conninfo) { - PQconninfoOption *connOptions; - char *tmp; + PGconn *conn; + + /* + * Allocate memory for the conn structure + */ + conn = makeEmptyPGconn(); + if (conn == NULL) + return NULL; /* * Parse the conninfo string */ - connOptions = conninfo_parse(conninfo, &conn->errorMessage, true); - if (connOptions == NULL) + 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; - /* errorMessage is already set */ - return false; } + return conn; +} + +static void +fillPGconn(PGconn *conn, PQconninfoOption *connOptions) +{ + char *tmp; + /* * Move option values into conn structure * @@ -482,6 +572,39 @@ connectOptions1(PGconn *conn, const char *conninfo) #endif tmp = conninfo_getval(connOptions, "replication"); conn->replication = tmp ? strdup(tmp) : NULL; +} + +/* + * 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; + + /* + * Parse the conninfo string + */ + connOptions = conninfo_parse(conninfo, &conn->errorMessage, true); + if (connOptions == NULL) + { + conn->status = CONNECTION_BAD; + /* errorMessage is already set */ + return false; + } + + /* + * Move option values into conn structure + */ + fillPGconn(conn, connOptions); /* * Free the option info - all is in conn now @@ -3598,6 +3721,149 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage, return options; } +/* + * Conninfo array 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. + * Defaults are supplied (from a service file, environment variables, etc) + * for unspecified options, but only if use_defaults is TRUE. + */ +static PQconninfoOption * +conninfo_array_parse(const char **keywords, const char **values, + PQExpBuffer errorMessage, bool use_defaults) +{ + char *tmp; + PQconninfoOption *options; + PQconninfoOption *option; + int i = 0; + + /* 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)); + + /* Parse the keywords/values arrays */ + while(keywords[i]) + { + const char *pname = keywords[i]; + const char *pvalue = values[i]; + + if (pvalue != NULL) + { + /* Search for the param record */ + for (option = options; option->keyword != NULL; option++) + { + if (strcmp(option->keyword, pname) == 0) + break; + } + + /* Check for invalid connection option */ + if (option->keyword == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid connection option \"%s\"\n"), + pname); + PQconninfoFree(options); + return NULL; + } + + /* + * Store the value + */ + if (option->val) + free(option->val); + option->val = strdup(pvalue); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } + } + ++i; + } + + /* + * Stop here if caller doesn't want defaults filled in. + */ + if (!use_defaults) + return options; + + /* + * 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); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } + continue; + } + } + + /* + * No environment variable specified or this one isn't set - try + * compiled in + */ + if (option->compiled != NULL) + { + option->val = strdup(option->compiled); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } + continue; + } + + /* + * Special handling for user + */ + if (strcmp(option->keyword, "user") == 0) + { + option->val = pg_fe_getauthname(errorMessage); + continue; + } + } + + return options; +} static char * conninfo_getval(PQconninfoOption *connOptions, diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 537bd231e8f..5f59da0f753 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.149 2010/01/21 14:58:53 rhaas Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.150 2010/01/28 06:28:26 joe Exp $ * *------------------------------------------------------------------------- */ @@ -226,10 +226,12 @@ typedef struct pgresAttDesc /* make a new client connection to the backend */ /* Asynchronous (non-blocking) */ extern PGconn *PQconnectStart(const char *conninfo); +extern PGconn *PQconnectStartParams(const char **keywords, const char **values); extern PostgresPollingStatusType PQconnectPoll(PGconn *conn); /* Synchronous (blocking) */ extern PGconn *PQconnectdb(const char *conninfo); +extern PGconn *PQconnectdbParams(const char **keywords, const char **values); extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, const char *pgtty, const char *dbName, |