aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/libpq/fe-connect.c')
-rw-r--r--src/interfaces/libpq/fe-connect.c320
1 files changed, 293 insertions, 27 deletions
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,