On 02/02/2010 06:40 PM, Tom Lane wrote: > The difference with PQconnectdbParams is that the dbname might not be > the first thing in the parameter array. I think that a straightforward > implementation would have the effect of the expanded dbname overriding > parameters given before it, but not those given after it. Consider > > keyword[0] = "port"; > values[0] = "5678"; > keyword[1] = "dbname"; > values[1] = "dbname = db user = foo port = 9999"; > keyword[2] = "user"; > values[2] = "uu"; > > What I'm imagining is that this would end up equivalent to > dbname = db user = uu port = 9999. That's probably reasonable, > and maybe even useful, as long as it's documented.
No doc changes yet, and I still have not corrected the earlier mentioned issue, > While I'm looking at this, there's another bogosity in the original > patch: it neglected the PQsetdbLogin call in psql/command.c, meaning > that doing \c would result in losing the application_name setting. but I wanted to get feedback before going further. This patch implements Tom's idea above. Note that I also rearranged the parameters for the call from psql so that dbname is last, therefore allowing a conninfo to override all other settings. The result looks like: keywords[0] = "host"; values[0] = options.host; keywords[1] = "port"; values[1] = options.port; keywords[2] = "user"; values[2] = options.username; keywords[3] = "password"; values[3] = password; keywords[4] = "application_name"; values[4] = pset.progname; keywords[5] = "dbname"; values[5] = (options.action == ACT_LIST_DB && options.dbname == NULL) ? "postgres" : options.dbname; keywords[6] = NULL; values[6] = NULL; Which produces: # psql -U postgres "dbname=regression user=fred application_name='joe\'s app'" psql (8.5devel) Type "help" for help. regression=> select backend_start, application_name from pg_stat_activity ; backend_start | application_name -------------------------------+------------------ 2010-02-02 21:44:55.969202-08 | joe's app (1 row) regression=> select current_user; current_user -------------- fred (1 row) Are there any of the psql parameters that we do not want to allow to be overridden by the conninfo string? Joe
Index: src/bin/psql/startup.c =================================================================== RCS file: /opt/src/cvs/pgsql/src/bin/psql/startup.c,v retrieving revision 1.159 diff -c -r1.159 startup.c *** src/bin/psql/startup.c 28 Jan 2010 06:28:26 -0000 1.159 --- src/bin/psql/startup.c 3 Feb 2010 05:41:54 -0000 *************** *** 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")); --- 90,95 ---- *************** *** 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) && --- 171,201 ---- /* loop until we have a password if requested by backend */ do { ! #define PARAMS_ARRAY_SIZE 7 ! const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); ! const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); ! ! keywords[0] = "host"; ! values[0] = options.host; ! keywords[1] = "port"; ! values[1] = options.port; ! keywords[2] = "user"; ! values[2] = options.username; ! keywords[3] = "password"; ! values[3] = password; ! keywords[4] = "application_name"; ! values[4] = pset.progname; ! keywords[5] = "dbname"; ! values[5] = (options.action == ACT_LIST_DB && ! options.dbname == NULL) ? ! "postgres" : options.dbname; ! keywords[6] = NULL; ! values[6] = NULL; ! ! new_pass = false; ! pset.db = PQconnectdbParams(keywords, values, true /* expand conninfo string in dbname if found */); ! free(keywords); ! free(values); if (PQstatus(pset.db) == CONNECTION_BAD && PQconnectionNeedsPassword(pset.db) && Index: src/interfaces/libpq/fe-connect.c =================================================================== RCS file: /opt/src/cvs/pgsql/src/interfaces/libpq/fe-connect.c,v retrieving revision 1.385 diff -c -r1.385 fe-connect.c *** src/interfaces/libpq/fe-connect.c 28 Jan 2010 06:28:26 -0000 1.385 --- src/interfaces/libpq/fe-connect.c 3 Feb 2010 05:29:54 -0000 *************** *** 269,275 **** 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); --- 269,275 ---- PQExpBuffer errorMessage, bool use_defaults); static PQconninfoOption *conninfo_array_parse(const char **keywords, const char **values, PQExpBuffer errorMessage, ! bool use_defaults, bool expand_dbname); static char *conninfo_getval(PQconninfoOption *connOptions, const char *keyword); static void defaultNoticeReceiver(void *arg, const PGresult *res); *************** *** 336,344 **** * call succeeded. */ PGconn * ! PQconnectdbParams(const char **keywords, const char **values) { ! PGconn *conn = PQconnectStartParams(keywords, values); if (conn && conn->status != CONNECTION_BAD) (void) connectDBComplete(conn); --- 336,346 ---- * call succeeded. */ PGconn * ! PQconnectdbParams(const char **keywords, ! const char **values, ! bool expand_dbname) { ! PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname); if (conn && conn->status != CONNECTION_BAD) (void) connectDBComplete(conn); *************** *** 400,406 **** * See PQconnectPoll for more info. */ PGconn * ! PQconnectStartParams(const char **keywords, const char **values) { PGconn *conn; PQconninfoOption *connOptions; --- 402,410 ---- * See PQconnectPoll for more info. */ PGconn * ! PQconnectStartParams(const char **keywords, ! const char **values, ! bool expand_dbname) { PGconn *conn; PQconninfoOption *connOptions; *************** *** 416,422 **** * Parse the conninfo arrays */ connOptions = conninfo_array_parse(keywords, values, ! &conn->errorMessage, true); if (connOptions == NULL) { conn->status = CONNECTION_BAD; --- 420,427 ---- * Parse the conninfo arrays */ connOptions = conninfo_array_parse(keywords, values, ! &conn->errorMessage, ! true, expand_dbname); if (connOptions == NULL) { conn->status = CONNECTION_BAD; *************** *** 3729,3744 **** * 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) --- 3734,3786 ---- * left in errorMessage. * Defaults are supplied (from a service file, environment variables, etc) * for unspecified options, but only if use_defaults is TRUE. + * + * If expand_dbname is TRUE, and the value passed for keyword "dbname" + * contains an "=", assume it is a conninfo string and process it, + * overriding any previously processed conflicting keywords. Subsequent + * keywords will take precedence, however. */ static PQconninfoOption * conninfo_array_parse(const char **keywords, const char **values, ! PQExpBuffer errorMessage, bool use_defaults, ! bool expand_dbname) { char *tmp; PQconninfoOption *options; + PQconninfoOption *str_options = NULL; PQconninfoOption *option; int i = 0; + /* + * If expand_dbname is true, check keyword "dbname" + * to see if val is actually a conninfo string + */ + while(expand_dbname && keywords[i]) + { + const char *pname = keywords[i]; + const char *pvalue = values[i]; + + /* first find "dbname" if any */ + if (strcmp(pname, "dbname") == 0) + { + /* next look for "=" in the value */ + if (pvalue && strchr(pvalue, '=')) + { + /* + * Must be a conninfo string, so parse it, but do not + * use defaults here -- those get picked up later. + * We only want to override for those parameters actually + * passed. + */ + str_options = conninfo_parse(pvalue, errorMessage, false); + if (str_options == NULL) + return NULL; + } + break; + } + ++i; + } + /* Make a working copy of PQconninfoOptions */ options = malloc(sizeof(PQconninfoOptions)); if (options == NULL) *************** *** 3749,3754 **** --- 3791,3797 ---- } memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions)); + i = 0; /* Parse the keywords/values arrays */ while(keywords[i]) { *************** *** 3774,3792 **** 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; } --- 3817,3867 ---- return NULL; } ! /* ! * If we are on the dbname parameter, and we have a parsed ! * conninfo string, copy those parameters across, overriding ! * any existing previous settings ! */ ! if (strcmp(pname, "dbname") == 0 && str_options) ! { ! PQconninfoOption *str_option; ! ! for (str_option = str_options; str_option->keyword != NULL; str_option++) ! { ! if (str_option->val != NULL) ! { ! int k; ! ! for (k = 0; options[k].keyword; k++) ! { ! if (strcmp(options[k].keyword, str_option->keyword) == 0) ! { ! if (options[k].val) ! free(options[k].val); ! options[k].val = strdup(str_option->val); ! break; ! } ! } ! } ! } ! PQconninfoFree(str_options); ! } ! else ! { ! /* ! * Store the value, overriding previous settings ! */ ! 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; } Index: src/interfaces/libpq/libpq-fe.h =================================================================== RCS file: /opt/src/cvs/pgsql/src/interfaces/libpq/libpq-fe.h,v retrieving revision 1.150 diff -c -r1.150 libpq-fe.h *** src/interfaces/libpq/libpq-fe.h 28 Jan 2010 06:28:26 -0000 1.150 --- src/interfaces/libpq/libpq-fe.h 3 Feb 2010 02:37:50 -0000 *************** *** 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, --- 226,239 ---- /* 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, bool expand_dbname); extern PostgresPollingStatusType PQconnectPoll(PGconn *conn); /* Synchronous (blocking) */ extern PGconn *PQconnectdb(const char *conninfo); ! extern PGconn *PQconnectdbParams(const char **keywords, ! const char **values, bool expand_dbname); extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, const char *pgtty, const char *dbName,
signature.asc
Description: OpenPGP digital signature