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
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,
signature.asc
Description: OpenPGP digital signature