Re: Patch: libpq new connect function (PQconnectParams)
I'm reviewing the patch posted here:
http://archives.postgresql.org/pgsql-hackers/2010-01/msg01579.php
for this commitfest item:
https://commitfest.postgresql.org/action/patch_view?id=259
Patch attached - a few minor changes:
-------------------------------------
1) Updated to apply cleanly against cvs tip
2) Improved comments
3) Moved much of what was in PQconnectStartParams() to a new
conninfo_array_parse() to be more consistent with existing code
Questions/comments:
-------------------
a) Do we want an analog to PQconninfoParse(), e.g.
PQconninfoParseParams()? If not, it isn't worth keeping use_defaults
as an argument to conninfo_array_parse().
b) I refrained from further consolidation even though there is room.
For example, I considered leaving only the real parsing code in
conninfo_parse(), and having it return keywords and values arrays.
If we did that, the rest of the code could be modified to accept
keywords and values instead of conninfo, and therefore shared. I was
concerned about the probably small performance hit to the existing
code path. Thoughts?
c) Obviously I liked the "two-arrays approach" better -- any objections
to that?
Thanks,
Joe
Attachments:
PQconnectParams-r2.patchtext/x-patch; name=PQconnectParams-r2.patchDownload
Index: src/bin/psql/startup.c
===================================================================
RCS file: /opt/src/cvs/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.158
diff -c -r1.158 startup.c
*** src/bin/psql/startup.c 2 Jan 2010 16:57:59 -0000 1.158
--- src/bin/psql/startup.c 25 Jan 2010 18:09:55 -0000
***************
*** 168,181 ****
if (pset.getPassword == TRI_YES)
password = simple_prompt(password_prompt, 100, false);
/* 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);
if (PQstatus(pset.db) == CONNECTION_BAD &&
PQconnectionNeedsPassword(pset.db) &&
--- 168,200 ----
if (pset.getPassword == TRI_YES)
password = simple_prompt(password_prompt, 100, false);
+ const char *keywords[] = {
+ "host",
+ "port",
+ "dbname",
+ "user",
+ "password",
+ "application_name",
+ NULL
+ };
+
/* loop until we have a password if requested by backend */
do
{
! 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) &&
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /opt/src/cvs/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.24
diff -c -r1.24 exports.txt
*** src/interfaces/libpq/exports.txt 21 Jan 2010 14:58:53 -0000 1.24
--- src/interfaces/libpq/exports.txt 25 Jan 2010 18:09:55 -0000
***************
*** 155,157 ****
--- 155,159 ----
PQinitOpenSSL 153
PQescapeLiteral 154
PQescapeIdentifier 155
+ PQconnectdbParams 156
+ PQconnectStartParams 157
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /opt/src/cvs/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.384
diff -c -r1.384 fe-connect.c
*** src/interfaces/libpq/fe-connect.c 20 Jan 2010 21:15:21 -0000 1.384
--- src/interfaces/libpq/fe-connect.c 25 Jan 2010 22:39:45 -0000
***************
*** 262,271 ****
--- 262,275 ----
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,312 ****
/*
* 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
--- 294,353 ----
/*
* Connecting to a Database
*
! * 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 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 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,351 ****
}
/*
! * 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
--- 381,392 ----
}
/*
! * 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
***************
*** 359,367 ****
* See PQconnectPoll for more info.
*/
PGconn *
! PQconnectStart(const char *conninfo)
{
! PGconn *conn;
/*
* Allocate memory for the conn structure
--- 400,409 ----
* See PQconnectPoll for more info.
*/
PGconn *
! PQconnectStartParams(const char **keywords, const char **values)
{
! PGconn *conn;
! PQconninfoOption *connOptions;
/*
* Allocate memory for the conn structure
***************
*** 371,380 ****
return NULL;
/*
! * Parse the conninfo string
*/
! if (!connectOptions1(conn, conninfo))
! return conn;
/*
* Compute derived options
--- 413,438 ----
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
***************
*** 395,427 ****
}
/*
! * 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, true);
! if (connOptions == NULL)
{
conn->status = CONNECTION_BAD;
- /* errorMessage is already set */
- return false;
}
/*
* Move option values into conn structure
*
--- 453,517 ----
}
/*
! * 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;
+ }
+
+ static void
+ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
+ {
+ char *tmp;
+
/*
* Move option values into conn structure
*
***************
*** 482,487 ****
--- 572,610 ----
#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,3603 ****
--- 3721,3869 ----
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,
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /opt/src/cvs/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.149
diff -c -r1.149 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h 21 Jan 2010 14:58:53 -0000 1.149
--- src/interfaces/libpq/libpq-fe.h 25 Jan 2010 18:09:55 -0000
***************
*** 226,235 ****
--- 226,237 ----
/* 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,
Le 26/01/2010 00:04, Joe Conway a �crit :
I'm reviewing the patch posted here:
http://archives.postgresql.org/pgsql-hackers/2010-01/msg01579.php
for this commitfest item:
https://commitfest.postgresql.org/action/patch_view?id=259
First, thanks for reviewing my patch.
Patch attached - a few minor changes:
-------------------------------------
1) Updated to apply cleanly against cvs tip
Sorry about this. I already updated it twice. I didn't think a new
update was needed.
2) Improved comments
Sure.
3) Moved much of what was in PQconnectStartParams() to a new
conninfo_array_parse() to be more consistent with existing code
You're right. It also makes the code more readable and understandable. I
should have done that.
Questions/comments:
-------------------
a) Do we want an analog to PQconninfoParse(), e.g.
PQconninfoParseParams()? If not, it isn't worth keeping use_defaults
as an argument to conninfo_array_parse().
No, I don't think so. I can't find a use case for it.
b) I refrained from further consolidation even though there is room.
For example, I considered leaving only the real parsing code in
conninfo_parse(), and having it return keywords and values arrays.
If we did that, the rest of the code could be modified to accept
keywords and values instead of conninfo, and therefore shared. I was
concerned about the probably small performance hit to the existing
code path. Thoughts?
c) Obviously I liked the "two-arrays approach" better -- any objections
to that?
No objection. I prefer the other one, but it's just not that important.
I didn't put any documentation before knowing which one will be choosen.
So we still need to work on the manual.
Thanks again.
--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com
On 01/25/2010 03:21 PM, Guillaume Lelarge wrote:
I didn't put any documentation before knowing which one will be choosen.
So we still need to work on the manual.
Please send the documentation as a separate patch. Once I have that I
will commit the posted patch, barring any objections in the meantime.
Joe
Le 26/01/2010 19:43, Joe Conway a �crit :
On 01/25/2010 03:21 PM, Guillaume Lelarge wrote:
I didn't put any documentation before knowing which one will be choosen.
So we still need to work on the manual.Please send the documentation as a separate patch. Once I have that I
will commit the posted patch, barring any objections in the meantime.
You'll find it attached with this mail. Please read it carefully, my
written english is not that good.
Thanks.
--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com
Attachments:
PQconnectdbParams_docs.patchtext/x-patch; name=PQconnectdbParams_docs.patchDownload
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.295
diff -c -p -c -r1.295 libpq.sgml
*** doc/src/sgml/libpq.sgml 21 Jan 2010 14:58:52 -0000 1.295
--- doc/src/sgml/libpq.sgml 26 Jan 2010 22:52:52 -0000
***************
*** 56,62 ****
one time. (One reason to do that is to access more than one
database.) Each connection is represented by a
<structname>PGconn</><indexterm><primary>PGconn</></> object, which
! is obtained from the function <function>PQconnectdb</> or
<function>PQsetdbLogin</>. Note that these functions will always
return a non-null object pointer, unless perhaps there is too
little memory even to allocate the <structname>PGconn</> object.
--- 56,63 ----
one time. (One reason to do that is to access more than one
database.) Each connection is represented by a
<structname>PGconn</><indexterm><primary>PGconn</></> object, which
! is obtained from the function <function>PQconnectdb</>,
! <function>PQconnectdbParams</> or
<function>PQsetdbLogin</>. Note that these functions will always
return a non-null object pointer, unless perhaps there is too
little memory even to allocate the <structname>PGconn</> object.
***************
*** 91,125 ****
<variablelist>
<varlistentry>
! <term><function>PQconnectdb</function><indexterm><primary>PQconnectdb</></></term>
<listitem>
<para>
Makes a new connection to the database server.
<synopsis>
! PGconn *PQconnectdb(const char *conninfo);
</synopsis>
</para>
<para>
This function opens a new database connection using the parameters taken
! from the string <literal>conninfo</literal>. Unlike <function>PQsetdbLogin</> below,
the parameter set can be extended without changing the function signature,
! so use of this function (or its nonblocking analogues <function>PQconnectStart</>
and <function>PQconnectPoll</function>) is preferred for new application programming.
</para>
<para>
! 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 <literal>keyword = value</literal>.
! Spaces around the equal sign are optional.
! To write an empty value or a value containing
! spaces, surround it with single quotes, e.g.,
! <literal>keyword = 'a value'</literal>.
! Single quotes and backslashes within the value must be escaped with a
! backslash, i.e., <literal>\'</literal> and <literal>\\</literal>.
</para>
<para>
--- 92,121 ----
<variablelist>
<varlistentry>
! <term><function>PQconnectdbParams</function><indexterm><primary>PQconnectdbParams</></></term>
<listitem>
<para>
Makes a new connection to the database server.
<synopsis>
! PGconn *PQconnectdbParams(const char **keywords, const char **values);
</synopsis>
</para>
<para>
This function opens a new database connection using the parameters taken
! from two arrays. The first one, <literal>keywords</literal>, is defined
! as an array of strings, each one being a keyword. The second one,
! <literal>values</literal>, gives the value for each keyword. Unlike
! <function>PQsetdbLogin</> below,
the parameter set can be extended without changing the function signature,
! so use of this function (or its nonblocking analogues <function>PQconnectStartParams</>
and <function>PQconnectPoll</function>) is preferred for new application programming.
</para>
<para>
! The passed arrays can be empty to use all default parameters, or it can
! contain one or more parameter settings.
</para>
<para>
***************
*** 478,483 ****
--- 474,518 ----
</varlistentry>
<varlistentry>
+ <term><function>PQconnectdb</function><indexterm><primary>PQconnectdb</></></term>
+ <listitem>
+ <para>
+ Makes a new connection to the database server.
+
+ <synopsis>
+ PGconn *PQconnectdb(const char *conninfo);
+ </synopsis>
+ </para>
+
+ <para>
+ This function opens a new database connection using the parameters taken
+ from the string <literal>conninfo</literal>.
+ </para>
+
+ <para>
+ 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 <literal>keyword = value</literal>.
+ Spaces around the equal sign are optional.
+ To write an empty value or a value containing
+ spaces, surround it with single quotes, e.g.,
+ <literal>keyword = 'a value'</literal>.
+ Single quotes and backslashes within the value must be escaped with a
+ backslash, i.e., <literal>\'</literal> and <literal>\\</literal>.
+ </para>
+
+ <para>
+ The currently recognized parameter key words are available above.
+ If any parameter is unspecified, then the corresponding
+ environment variable (see <xref linkend="libpq-envars">)
+ is checked. If the environment variable is not set either,
+ then the indicated built-in defaults are used.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><function>PQsetdbLogin</function><indexterm><primary>PQsetdbLogin</></></term>
<listitem>
<para>
*************** PGconn *PQsetdb(char *pghost,
*** 532,537 ****
--- 567,573 ----
</varlistentry>
<varlistentry>
+ <term><function>PQconnectStartParams</function><indexterm><primary>PQconnectStartParams</></></term>
<term><function>PQconnectStart</function><indexterm><primary>PQconnectStart</></></term>
<term><function>PQconnectPoll</function><indexterm><primary>PQconnectPoll</></></term>
<listitem>
*************** PGconn *PQsetdb(char *pghost,
*** 540,545 ****
--- 576,585 ----
Make a connection to the database server in a nonblocking manner.
<synopsis>
+ PGconn *PQconnectStartParams(const char **keywords, const char **values);
+ </synopsis>
+
+ <synopsis>
PGconn *PQconnectStart(const char *conninfo);
</synopsis>
*************** PGconn *PQsetdb(char *pghost,
*** 549,577 ****
</para>
<para>
! These two 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
! <function>PQconnectdb</>, and so the application can manage this
! operation in parallel with other activities.
</para>
<para>
The database connection is made using the parameters taken from the string
! <literal>conninfo</literal>, passed to <function>PQconnectStart</function>. This string is in
! the same format as described above for <function>PQconnectdb</function>.
</para>
<para>
! Neither <function>PQconnectStart</function> nor <function>PQconnectPoll</function> will block, so long as a number of
restrictions are met:
<itemizedlist>
<listitem>
<para>
The <literal>hostaddr</> and <literal>host</> parameters are used appropriately to ensure that
name and reverse name queries are not made. See the documentation of
! these parameters under <function>PQconnectdb</function> above for details.
</para>
</listitem>
--- 589,626 ----
</para>
<para>
! 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
! <function>PQconnectdbParams</> or <function>PQconnectdb</>, and so the
! application can manage this operation in parallel with other activities.
</para>
<para>
The database connection is made using the parameters taken from the string
! <literal>conninfo</literal>, passed to <function>PQconnectStart</function>.
! This string is in the same format as described above for
! <function>PQconnectdb</function>.
</para>
+
+ <para>
+ The database connection can also be made using the parameters taken from
+ the <literal>keywords</literal> and <literal>values</literal> arrays,
+ this time passed to <function>PQconnectStartParams</function>.
+ </para>
+
<para>
! Neither <function>PQconnectStartParams</function> nor <function>PQconnectStart</function>
! nor <function>PQconnectPoll</function> will block, so long as a number of
restrictions are met:
<itemizedlist>
<listitem>
<para>
The <literal>hostaddr</> and <literal>host</> parameters are used appropriately to ensure that
name and reverse name queries are not made. See the documentation of
! these parameters under <function>PQconnectdbParams</function> above for details.
</para>
</listitem>
*************** PQconninfoOption *PQconninfoParse(const
*** 883,889 ****
parameters previously used. This can be useful for error recovery if a
working connection is lost. They differ from <function>PQreset</function> (above) in that they
act in a nonblocking manner. These functions suffer from the same
! restrictions as <function>PQconnectStart</> and <function>PQconnectPoll</>.
</para>
<para>
--- 932,939 ----
parameters previously used. This can be useful for error recovery if a
working connection is lost. They differ from <function>PQreset</function> (above) in that they
act in a nonblocking manner. These functions suffer from the same
! restrictions as <function>PQconnectStartParams</>, <function>PQconnectStart</>
! and <function>PQconnectPoll</>.
</para>
<para>
*************** PQconninfoOption *PQconninfoParse(const
*** 1096,1104 ****
</para>
<para>
! See the entry for <function>PQconnectStart</> and <function>PQconnectPoll</> with regards
! to other status codes
! that might be seen.
</para>
</listitem>
</varlistentry>
--- 1146,1154 ----
</para>
<para>
! See the entry for <function>PQconnectStartParams</>, <function>PQconnectStart</>
! and <function>PQconnectPoll</> with regards to other status codes that
! might be seen.
</para>
</listitem>
</varlistentry>
On 01/26/2010 02:55 PM, Guillaume Lelarge wrote:
Le 26/01/2010 19:43, Joe Conway a écrit :
On 01/25/2010 03:21 PM, Guillaume Lelarge wrote:
I didn't put any documentation before knowing which one will be choosen.
So we still need to work on the manual.Please send the documentation as a separate patch. Once I have that I
will commit the posted patch, barring any objections in the meantime.You'll find it attached with this mail. Please read it carefully, my
written english is not that good.
Final committed patch attached.
One last code correction -- in psql/startup.c the original patch defines
the keywords array in the body of the code, rather than at the top of
the block.
Minor improvements ( hopefully ;-)) to the documentation as well.
Joe
Attachments:
PQconnectParams-final.patchtext/x-patch; name=PQconnectParams-final.patchDownload
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.295
diff -c -r1.295 libpq.sgml
*** doc/src/sgml/libpq.sgml 21 Jan 2010 14:58:52 -0000 1.295
--- doc/src/sgml/libpq.sgml 28 Jan 2010 06:26:29 -0000
***************
*** 56,62 ****
one time. (One reason to do that is to access more than one
database.) Each connection is represented by a
<structname>PGconn</><indexterm><primary>PGconn</></> object, which
! is obtained from the function <function>PQconnectdb</> or
<function>PQsetdbLogin</>. Note that these functions will always
return a non-null object pointer, unless perhaps there is too
little memory even to allocate the <structname>PGconn</> object.
--- 56,63 ----
one time. (One reason to do that is to access more than one
database.) Each connection is represented by a
<structname>PGconn</><indexterm><primary>PGconn</></> object, which
! is obtained from the function <function>PQconnectdb</>,
! <function>PQconnectdbParams</>, or
<function>PQsetdbLogin</>. Note that these functions will always
return a non-null object pointer, unless perhaps there is too
little memory even to allocate the <structname>PGconn</> object.
***************
*** 91,125 ****
<variablelist>
<varlistentry>
! <term><function>PQconnectdb</function><indexterm><primary>PQconnectdb</></></term>
<listitem>
<para>
Makes a new connection to the database server.
<synopsis>
! PGconn *PQconnectdb(const char *conninfo);
</synopsis>
</para>
<para>
This function opens a new database connection using the parameters taken
! from the string <literal>conninfo</literal>. Unlike <function>PQsetdbLogin</> below,
! the parameter set can be extended without changing the function signature,
! so use of this function (or its nonblocking analogues <function>PQconnectStart</>
! and <function>PQconnectPoll</function>) is preferred for new application programming.
</para>
<para>
! 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 <literal>keyword = value</literal>.
! Spaces around the equal sign are optional.
! To write an empty value or a value containing
! spaces, surround it with single quotes, e.g.,
! <literal>keyword = 'a value'</literal>.
! Single quotes and backslashes within the value must be escaped with a
! backslash, i.e., <literal>\'</literal> and <literal>\\</literal>.
</para>
<para>
--- 92,124 ----
<variablelist>
<varlistentry>
! <term><function>PQconnectdbParams</function><indexterm><primary>PQconnectdbParams</></></term>
<listitem>
<para>
Makes a new connection to the database server.
<synopsis>
! PGconn *PQconnectdbParams(const char **keywords, const char **values);
</synopsis>
</para>
<para>
This function opens a new database connection using the parameters taken
! from two <symbol>NULL</symbol>-terminated arrays. The first,
! <literal>keywords</literal>, is defined as an array of strings, each one
! being a key word. The second, <literal>values</literal>, gives the value
! for each key word. Unlike <function>PQsetdbLogin</> below, the parameter
! set can be extended without changing the function signature, so use of
! this function (or its nonblocking analogs <function>PQconnectStartParams</>
! and <function>PQconnectPoll</function>) is preferred for new application
! programming.
</para>
<para>
! 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-<symbol>NULL</symbol> element
! of the <literal>keywords</literal> array.
</para>
<para>
***************
*** 478,483 ****
--- 477,515 ----
</varlistentry>
<varlistentry>
+ <term><function>PQconnectdb</function><indexterm><primary>PQconnectdb</></></term>
+ <listitem>
+ <para>
+ Makes a new connection to the database server.
+
+ <synopsis>
+ PGconn *PQconnectdb(const char *conninfo);
+ </synopsis>
+ </para>
+
+ <para>
+ This function opens a new database connection using the parameters taken
+ from the string <literal>conninfo</literal>.
+ </para>
+
+ <para>
+ 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 <literal>keyword = value</literal>.
+ Spaces around the equal sign are optional. To write an empty value,
+ or a value containing spaces, surround it with single quotes, e.g.,
+ <literal>keyword = 'a value'</literal>. Single quotes and backslashes
+ within the value must be escaped with a backslash, i.e.,
+ <literal>\'</literal> and <literal>\\</literal>.
+ </para>
+
+ <para>
+ The currently recognized parameter key words are the same as above.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><function>PQsetdbLogin</function><indexterm><primary>PQsetdbLogin</></></term>
<listitem>
<para>
***************
*** 532,537 ****
--- 564,570 ----
</varlistentry>
<varlistentry>
+ <term><function>PQconnectStartParams</function><indexterm><primary>PQconnectStartParams</></></term>
<term><function>PQconnectStart</function><indexterm><primary>PQconnectStart</></></term>
<term><function>PQconnectPoll</function><indexterm><primary>PQconnectPoll</></></term>
<listitem>
***************
*** 540,545 ****
--- 573,582 ----
Make a connection to the database server in a nonblocking manner.
<synopsis>
+ PGconn *PQconnectStartParams(const char **keywords, const char **values);
+ </synopsis>
+
+ <synopsis>
PGconn *PQconnectStart(const char *conninfo);
</synopsis>
***************
*** 549,577 ****
</para>
<para>
! These two 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
! <function>PQconnectdb</>, and so the application can manage this
! operation in parallel with other activities.
</para>
<para>
! The database connection is made using the parameters taken from the string
! <literal>conninfo</literal>, passed to <function>PQconnectStart</function>. This string is in
! the same format as described above for <function>PQconnectdb</function>.
</para>
<para>
! Neither <function>PQconnectStart</function> nor <function>PQconnectPoll</function> will block, so long as a number of
restrictions are met:
<itemizedlist>
<listitem>
<para>
The <literal>hostaddr</> and <literal>host</> parameters are used appropriately to ensure that
name and reverse name queries are not made. See the documentation of
! these parameters under <function>PQconnectdb</function> above for details.
</para>
</listitem>
--- 586,622 ----
</para>
<para>
! 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
! <function>PQconnectdbParams</> or <function>PQconnectdb</>, and so the
! application can manage this operation in parallel with other activities.
</para>
<para>
! With <function>PQconnectStartParams</function>, the database connection is made
! using the parameters taken from the <literal>keywords</literal> and
! <literal>values</literal> arrays, as described above for
! <function>PQconnectdbParams</function>.
</para>
+
<para>
! With <function>PQconnectStart</function>, the database connection is made
! using the parameters taken from the string <literal>conninfo</literal> as
! described above for <function>PQconnectdb</function>.
! </para>
!
! <para>
! Neither <function>PQconnectStartParams</function> nor <function>PQconnectStart</function>
! nor <function>PQconnectPoll</function> will block, so long as a number of
restrictions are met:
<itemizedlist>
<listitem>
<para>
The <literal>hostaddr</> and <literal>host</> parameters are used appropriately to ensure that
name and reverse name queries are not made. See the documentation of
! these parameters under <function>PQconnectdbParams</function> above for details.
</para>
</listitem>
***************
*** 592,597 ****
--- 637,647 ----
</para>
<para>
+ Note: use of <function>PQconnectStartParams</> is analogous to
+ <function>PQconnectStart</> shown below.
+ </para>
+
+ <para>
To begin a nonblocking connection request, call <literal>conn = PQconnectStart("<replaceable>connection_info_string</>")</literal>.
If <varname>conn</varname> is null, then <application>libpq</> has been unable to allocate a new <structname>PGconn</>
structure. Otherwise, a valid <structname>PGconn</> pointer is returned (though not yet
***************
*** 883,889 ****
parameters previously used. This can be useful for error recovery if a
working connection is lost. They differ from <function>PQreset</function> (above) in that they
act in a nonblocking manner. These functions suffer from the same
! restrictions as <function>PQconnectStart</> and <function>PQconnectPoll</>.
</para>
<para>
--- 933,940 ----
parameters previously used. This can be useful for error recovery if a
working connection is lost. They differ from <function>PQreset</function> (above) in that they
act in a nonblocking manner. These functions suffer from the same
! restrictions as <function>PQconnectStartParams</>, <function>PQconnectStart</>
! and <function>PQconnectPoll</>.
</para>
<para>
***************
*** 1096,1104 ****
</para>
<para>
! See the entry for <function>PQconnectStart</> and <function>PQconnectPoll</> with regards
! to other status codes
! that might be seen.
</para>
</listitem>
</varlistentry>
--- 1147,1155 ----
</para>
<para>
! See the entry for <function>PQconnectStartParams</>, <function>PQconnectStart</>
! and <function>PQconnectPoll</> with regards to other status codes that
! might be seen.
</para>
</listitem>
</varlistentry>
Index: src/bin/psql/startup.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.158
diff -c -r1.158 startup.c
*** src/bin/psql/startup.c 2 Jan 2010 16:57:59 -0000 1.158
--- src/bin/psql/startup.c 28 Jan 2010 06:26:29 -0000
***************
*** 90,95 ****
--- 90,97 ----
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,181 ****
/* 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);
if (PQstatus(pset.db) == CONNECTION_BAD &&
PQconnectionNeedsPassword(pset.db) &&
--- 173,192 ----
/* loop until we have a password if requested by backend */
do
{
! 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) &&
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.24
diff -c -r1.24 exports.txt
*** src/interfaces/libpq/exports.txt 21 Jan 2010 14:58:53 -0000 1.24
--- src/interfaces/libpq/exports.txt 28 Jan 2010 06:26:29 -0000
***************
*** 155,157 ****
--- 155,159 ----
PQinitOpenSSL 153
PQescapeLiteral 154
PQescapeIdentifier 155
+ PQconnectdbParams 156
+ PQconnectStartParams 157
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.384
diff -c -r1.384 fe-connect.c
*** src/interfaces/libpq/fe-connect.c 20 Jan 2010 21:15:21 -0000 1.384
--- src/interfaces/libpq/fe-connect.c 28 Jan 2010 06:26:29 -0000
***************
*** 262,271 ****
--- 262,275 ----
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,312 ****
/*
* 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
--- 294,353 ----
/*
* Connecting to a Database
*
! * 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 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 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,351 ****
}
/*
! * 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
--- 381,392 ----
}
/*
! * 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
***************
*** 359,367 ****
* See PQconnectPoll for more info.
*/
PGconn *
! PQconnectStart(const char *conninfo)
{
! PGconn *conn;
/*
* Allocate memory for the conn structure
--- 400,409 ----
* See PQconnectPoll for more info.
*/
PGconn *
! PQconnectStartParams(const char **keywords, const char **values)
{
! PGconn *conn;
! PQconninfoOption *connOptions;
/*
* Allocate memory for the conn structure
***************
*** 371,380 ****
return NULL;
/*
! * Parse the conninfo string
*/
! if (!connectOptions1(conn, conninfo))
! return conn;
/*
* Compute derived options
--- 413,438 ----
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
***************
*** 395,427 ****
}
/*
! * 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, true);
! if (connOptions == NULL)
{
conn->status = CONNECTION_BAD;
- /* errorMessage is already set */
- return false;
}
/*
* Move option values into conn structure
*
--- 453,517 ----
}
/*
! * 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;
+ }
+
+ static void
+ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
+ {
+ char *tmp;
+
/*
* Move option values into conn structure
*
***************
*** 482,487 ****
--- 572,610 ----
#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,3603 ****
--- 3721,3869 ----
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,
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.149
diff -c -r1.149 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h 21 Jan 2010 14:58:53 -0000 1.149
--- src/interfaces/libpq/libpq-fe.h 28 Jan 2010 06:26:29 -0000
***************
*** 226,235 ****
--- 226,237 ----
/* 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,
Le 28/01/2010 07:32, Joe Conway a �crit :
On 01/26/2010 02:55 PM, Guillaume Lelarge wrote:
Le 26/01/2010 19:43, Joe Conway a �crit :
On 01/25/2010 03:21 PM, Guillaume Lelarge wrote:
I didn't put any documentation before knowing which one will be choosen.
So we still need to work on the manual.Please send the documentation as a separate patch. Once I have that I
will commit the posted patch, barring any objections in the meantime.You'll find it attached with this mail. Please read it carefully, my
written english is not that good.Final committed patch attached.
One last code correction -- in psql/startup.c the original patch defines
the keywords array in the body of the code, rather than at the top of
the block.Minor improvements ( hopefully ;-)) to the documentation as well.
Thanks a lot.
--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com