diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 1d13e8b0b4..a698ab1958 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,4 +1,4 @@ - + <application>libpq</application> - C Library @@ -56,7 +56,8 @@ one time. (One reason to do that is to access more than one database.) Each connection is represented by a PGconnPGconn object, which - is obtained from the function PQconnectdb or + is obtained from the function PQconnectdb, + PQconnectdbParams, or PQsetdbLogin. Note that these functions will always return a non-null object pointer, unless perhaps there is too little memory even to allocate the PGconn object. @@ -91,35 +92,33 @@ - PQconnectdbPQconnectdb + PQconnectdbParamsPQconnectdbParams Makes a new connection to the database server. - PGconn *PQconnectdb(const char *conninfo); + PGconn *PQconnectdbParams(const char **keywords, const char **values); This function opens a new database connection using the parameters taken - from the string conninfo. Unlike PQsetdbLogin below, - the parameter set can be extended without changing the function signature, - so use of this function (or its nonblocking analogues PQconnectStart - and PQconnectPoll) is preferred for new application programming. + from two NULL-terminated arrays. The first, + keywords, is defined as an array of strings, each one + being a key word. The second, values, gives the value + for each key word. Unlike PQsetdbLogin below, the parameter + set can be extended without changing the function signature, so use of + this function (or its nonblocking analogs PQconnectStartParams + and PQconnectPoll) is preferred for new application + programming. - The passed string - can be empty to use all default parameters, or it can contain one or more - parameter settings separated by whitespace. - Each parameter setting is in the form keyword = value. - Spaces around the equal sign are optional. - To write an empty value or a value containing - spaces, surround it with single quotes, e.g., - keyword = 'a value'. - Single quotes and backslashes within the value must be escaped with a - backslash, i.e., \' and \\. + The passed arrays can be empty to use all default parameters, or can + contain one or more parameter settings. They should be matched in length. + Processing will stop with the last non-NULL element + of the keywords array. @@ -477,6 +476,39 @@ + + PQconnectdbPQconnectdb + + + Makes a new connection to the database server. + + + PGconn *PQconnectdb(const char *conninfo); + + + + + This function opens a new database connection using the parameters taken + from the string conninfo. + + + + The passed string can be empty to use all default parameters, or it can + contain one or more parameter settings separated by whitespace. + Each parameter setting is in the form keyword = value. + Spaces around the equal sign are optional. To write an empty value, + or a value containing spaces, surround it with single quotes, e.g., + keyword = 'a value'. Single quotes and backslashes + within the value must be escaped with a backslash, i.e., + \' and \\. + + + + The currently recognized parameter key words are the same as above. + + + + PQsetdbLoginPQsetdbLogin @@ -532,6 +564,7 @@ PGconn *PQsetdb(char *pghost, + PQconnectStartParamsPQconnectStartParams PQconnectStartPQconnectStart PQconnectPollPQconnectPoll @@ -539,6 +572,10 @@ PGconn *PQsetdb(char *pghost, nonblocking connection Make a connection to the database server in a nonblocking manner. + + PGconn *PQconnectStartParams(const char **keywords, const char **values); + + PGconn *PQconnectStart(const char *conninfo); @@ -549,29 +586,37 @@ PGconn *PQsetdb(char *pghost, - These two functions are used to open a connection to a database server such + These three functions are used to open a connection to a database server such that your application's thread of execution is not blocked on remote I/O - whilst doing so. - The point of this approach is that the waits for I/O to complete can occur - in the application's main loop, rather than down inside - PQconnectdb, and so the application can manage this - operation in parallel with other activities. + whilst doing so. The point of this approach is that the waits for I/O to + complete can occur in the application's main loop, rather than down inside + PQconnectdbParams or PQconnectdb, and so the + application can manage this operation in parallel with other activities. - The database connection is made using the parameters taken from the string - conninfo, passed to PQconnectStart. This string is in - the same format as described above for PQconnectdb. + With PQconnectStartParams, the database connection is made + using the parameters taken from the keywords and + values arrays, as described above for + PQconnectdbParams. + - Neither PQconnectStart nor PQconnectPoll will block, so long as a number of + With PQconnectStart, the database connection is made + using the parameters taken from the string conninfo as + described above for PQconnectdb. + + + + Neither PQconnectStartParams nor PQconnectStart + nor PQconnectPoll will block, so long as a number of restrictions are met: The hostaddr and host parameters are used appropriately to ensure that name and reverse name queries are not made. See the documentation of - these parameters under PQconnectdb above for details. + these parameters under PQconnectdbParams above for details. @@ -591,6 +636,11 @@ PGconn *PQsetdb(char *pghost, + + Note: use of PQconnectStartParams is analogous to + PQconnectStart shown below. + + To begin a nonblocking connection request, call conn = PQconnectStart("connection_info_string"). If conn is null, then libpq has been unable to allocate a new PGconn @@ -883,7 +933,8 @@ PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg); parameters previously used. This can be useful for error recovery if a working connection is lost. They differ from PQreset (above) in that they act in a nonblocking manner. These functions suffer from the same - restrictions as PQconnectStart and PQconnectPoll. + restrictions as PQconnectStartParams, PQconnectStart + and PQconnectPoll. @@ -1096,9 +1147,9 @@ PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg); - See the entry for PQconnectStart and PQconnectPoll with regards - to other status codes - that might be seen. + See the entry for PQconnectStartParams, PQconnectStart + and PQconnectPoll with regards to other status codes that + might be seen. diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 6ffe593786..b29c84fdae 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 a7a1b94e11..bdaa5857b7 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 4d7be078c0..048c438527 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,22 +294,59 @@ 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 * @@ -339,6 +380,78 @@ PQconnectdb(const char *conninfo) return conn; } +/* + * PQconnectStartParams + * + * Begins the establishment of a connection to a postgres backend through the + * postmaster using connection information in a struct. + * + * 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 + * 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 * +PQconnectStartParams(const char **keywords, const char **values) +{ + PGconn *conn; + PQconninfoOption *connOptions; + + /* + * Allocate memory for the conn structure + */ + conn = makeEmptyPGconn(); + if (conn == NULL) + return NULL; + + /* + * Parse the conninfo arrays + */ + 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 + */ + 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; +} + /* * PQconnectStart * @@ -394,34 +507,11 @@ PQconnectStart(const char *conninfo) 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) +static void +fillPGconn(PGconn *conn, PQconninfoOption *connOptions) { - PQconninfoOption *connOptions; char *tmp; - /* - * 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 * @@ -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 537bd231e8..5f59da0f75 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,