diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 308e3900c0..cf85de1ca7 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -799,7 +799,7 @@ testdb=> - \c or \connect [ dbname [ username ] [ host ] [ port ] ] | conninfo + \c or \connect [ -reuse-previous=on|off ] [ dbname [ username ] [ host ] [ port ] | conninfo ] Establishes a new connection to a PostgreSQL @@ -809,16 +809,19 @@ testdb=> - When using positional parameters, if any of - dbname, + Where the command omits database name, user, host, or port, the new + connection can reuse values from the previous connection. By default, + values from the previous connection are reused except when processing + a conninfo string. Passing a first argument + of -reuse-previous=on + or -reuse-previous=off overrides that default. + When the command neither specifies nor reuses a particular parameter, + the libpq default is used. Specifying any + of dbname, username, host or - port are omitted or - specified as -, the value of that parameter from - the previous connection is used; if there is no previous connection, - the libpq default for the parameter's value - is used. When using conninfo strings, no values from the - previous connection are used for the new connection. + port + as - is equivalent to omitting that parameter. diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 07d3e6998e..6a885674d0 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -56,7 +56,8 @@ static backslashResult exec_command(const char *cmd, PQExpBuffer query_buf); static bool do_edit(const char *filename_arg, PQExpBuffer query_buf, int lineno, bool *edited); -static bool do_connect(char *dbname, char *user, char *host, char *port); +static bool do_connect(enum trivalue reuse_previous_specification, + char *dbname, char *user, char *host, char *port); static bool do_shell(const char *command); static bool do_watch(PQExpBuffer query_buf, long sleep); static bool lookup_function_oid(const char *desc, Oid *foid); @@ -217,12 +218,9 @@ exec_command(const char *cmd, /* * \c or \connect -- connect to database using the specified parameters. * - * \c dbname user host port + * \c [-reuse-previous=BOOL] dbname user host port * - * If any of these parameters are omitted or specified as '-', the current - * value of the parameter will be used instead. If the parameter has no - * current value, the default value for that parameter will be used. Some - * examples: + * Specifying a parameter as '-' is equivalent to omitting it. Examples: * * \c - - hst Connect to current database on current port of host * "hst" as current user. \c - usr - prt Connect to current database on @@ -231,17 +229,31 @@ exec_command(const char *cmd, */ else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0) { + static const char prefix[] = "-reuse-previous="; char *opt1, *opt2, *opt3, *opt4; + enum trivalue reuse_previous; opt1 = read_connect_arg(scan_state); + if (opt1 != NULL && strncmp(opt1, prefix, sizeof(prefix) - 1) == 0) + { + reuse_previous = + ParseVariableBool(opt1 + sizeof(prefix) - 1, prefix) ? + TRI_YES : TRI_NO; + + free(opt1); + opt1 = read_connect_arg(scan_state); + } + else + reuse_previous = TRI_DEFAULT; + opt2 = read_connect_arg(scan_state); opt3 = read_connect_arg(scan_state); opt4 = read_connect_arg(scan_state); - success = do_connect(opt1, opt2, opt3, opt4); + success = do_connect(reuse_previous, opt1, opt2, opt3, opt4); free(opt1); free(opt2); @@ -1599,22 +1611,25 @@ param_is_newly_set(const char *old_val, const char *new_val) /* * do_connect -- handler for \connect * - * Connects to a database with given parameters. If there exists an - * established connection, NULL values will be replaced with the ones - * in the current connection. Otherwise NULL will be passed for that - * parameter to PQconnectdbParams(), so the libpq defaults will be used. + * Connects to a database with given parameters. Absent an established + * connection, all parameters are required. Given -reuse-previous=off or a + * connection string without -reuse-previous=on, NULL values will pass through + * to PQconnectdbParams(), so the libpq defaults will be used. Otherwise, NULL + * values will be replaced with the ones in the current connection. * * In interactive mode, if connection fails with the given parameters, * the old connection will be kept. */ static bool -do_connect(char *dbname, char *user, char *host, char *port) +do_connect(enum trivalue reuse_previous_specification, + char *dbname, char *user, char *host, char *port) { PGconn *o_conn = pset.db, *n_conn; char *password = NULL; bool keep_password; bool has_connection_string; + bool reuse_previous; if (!o_conn && (!dbname || !user || !host || !port)) { @@ -1628,16 +1643,35 @@ do_connect(char *dbname, char *user, char *host, char *port) return false; } - /* grab values from the old connection, unless supplied by caller */ - if (!user) - user = PQuser(o_conn); - if (!host) - host = PQhost(o_conn); - if (!port) - port = PQport(o_conn); + has_connection_string = dbname ? + recognized_connection_string(dbname) : false; + switch (reuse_previous_specification) + { + case TRI_YES: + reuse_previous = true; + break; + case TRI_NO: + reuse_previous = false; + break; + default: + reuse_previous = !has_connection_string; + break; + } + /* Silently ignore arguments subsequent to a connection string. */ + if (has_connection_string) + { + user = NULL; + host = NULL; + port = NULL; + } - has_connection_string = - dbname ? recognized_connection_string(dbname) : false; + /* grab missing values from the old connection */ + if (!user && reuse_previous) + user = PQuser(o_conn); + if (!host && reuse_previous) + host = PQhost(o_conn); + if (!port && reuse_previous) + port = PQport(o_conn); /* * Any change in the parameters read above makes us discard the password. @@ -1655,10 +1689,10 @@ do_connect(char *dbname, char *user, char *host, char *port) (port && PQport(o_conn) && strcmp(port, PQport(o_conn)) == 0); /* - * Grab dbname from old connection unless supplied by caller. No password - * discard if this changes: passwords aren't (usually) database-specific. + * Grab missing dbname from old connection. No password discard if this + * changes: passwords aren't (usually) database-specific. */ - if (!dbname) + if (!dbname && reuse_previous) dbname = PQdb(o_conn); /* @@ -1689,20 +1723,27 @@ do_connect(char *dbname, char *user, char *host, char *port) #define PARAMS_ARRAY_SIZE 8 const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); - int paramnum = 0; + int paramnum = -1; - keywords[0] = "dbname"; - values[0] = dbname; + keywords[++paramnum] = "host"; + values[paramnum] = host; + keywords[++paramnum] = "port"; + values[paramnum] = port; + keywords[++paramnum] = "user"; + values[paramnum] = user; - if (!has_connection_string) - { - keywords[++paramnum] = "host"; - values[paramnum] = host; - keywords[++paramnum] = "port"; - values[paramnum] = port; - keywords[++paramnum] = "user"; - values[paramnum] = user; - } + /* + * Position in the array matters when the dbname is a connection + * string, because settings in a connection string override earlier + * array entries only. Thus, user= in the connection string always + * takes effect, but client_encoding= often will not. + * + * If you change this code, also change the initial-connection code in + * main(). For no good reason, a connection string password= takes + * precedence in main() but not here. + */ + keywords[++paramnum] = "dbname"; + values[paramnum] = dbname; keywords[++paramnum] = "password"; values[paramnum] = password; keywords[++paramnum] = "fallback_application_name"; diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 8b1777e243..0c090d15c2 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -215,7 +215,7 @@ main(int argc, char *argv[]) values[2] = options.username; keywords[3] = "password"; values[3] = password; - keywords[4] = "dbname"; + keywords[4] = "dbname"; /* see do_connect() */ values[4] = (options.action == ACT_LIST_DB && options.dbname == NULL) ? "postgres" : options.dbname;