Skip to content

Commit 94929f1

Browse files
committed
Clean up some unpleasant behaviors in psql's \connect command.
The check for whether to complain about not having an old connection to get parameters from was seriously out of date: it had not been rethought when we invented connstrings, nor when we invented the -reuse-previous option. Replace it with a check that throws an error if reuse-previous is active and we lack an old connection to reuse. While that doesn't move the goalposts very far in terms of easing reconnection after a server crash, at least it's consistent. If the user specifies a connstring plus additional parameters (which is invalid per the documentation), the extra parameters were silently ignored. That seems like it could be really confusing, so let's throw a syntax error instead. Teach the connstring code path to re-use the old connection's password in the same cases as the old-style-syntax code path would, ie if we are reusing parameters and the values of username, host/hostaddr, and port are not being changed. Document this behavior, too, since it was unmentioned before. Also simplify the implementation a bit, giving rise to two new and useful properties: if there's a "password=xxx" in the connstring, we'll use it not ignore it, and by default (i.e., except with --no-password) we will prompt for a password if the re-used password or connstring password doesn't work. The previous code just failed if the re-used password didn't work. Given the paucity of field complaints about these issues, I don't think that they rise to the level of back-patchable bug fixes, and in any case they might represent undesirable behavior changes in minor releases. So no back-patch. Discussion: https://postgr.es/m/235210.1603321144@sss.pgh.pa.us
1 parent 866e24d commit 94929f1

File tree

2 files changed

+54
-39
lines changed

2 files changed

+54
-39
lines changed

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,8 @@ testdb=>
920920
is changed from its previous value using the positional syntax,
921921
any <replaceable>hostaddr</replaceable> setting present in the
922922
existing connection's parameters is dropped.
923+
Also, any password used for the existing connection will be re-used
924+
only if the user, host, and port settings are not changed.
923925
When the command neither specifies nor reuses a particular parameter,
924926
the <application>libpq</application> default is used.
925927
</para>

src/bin/psql/command.c

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3014,11 +3014,10 @@ param_is_newly_set(const char *old_val, const char *new_val)
30143014
/*
30153015
* do_connect -- handler for \connect
30163016
*
3017-
* Connects to a database with given parameters. Absent an established
3018-
* connection, all parameters are required. Given -reuse-previous=off or a
3019-
* connection string without -reuse-previous=on, NULL values will pass through
3020-
* to PQconnectdbParams(), so the libpq defaults will be used. Otherwise, NULL
3021-
* values will be replaced with the ones in the current connection.
3017+
* Connects to a database with given parameters. If we are told to re-use
3018+
* parameters, parameters from the previous connection are used where the
3019+
* command's own options do not supply a value. Otherwise, libpq defaults
3020+
* are used.
30223021
*
30233022
* In interactive mode, if connection fails with the given parameters,
30243023
* the old connection will be kept.
@@ -3038,20 +3037,16 @@ do_connect(enum trivalue reuse_previous_specification,
30383037
bool has_connection_string;
30393038
bool reuse_previous;
30403039

3041-
if (!o_conn && (!dbname || !user || !host || !port))
3040+
has_connection_string = dbname ?
3041+
recognized_connection_string(dbname) : false;
3042+
3043+
/* Complain if we have additional arguments after a connection string. */
3044+
if (has_connection_string && (user || host || port))
30423045
{
3043-
/*
3044-
* We don't know the supplied connection parameters and don't want to
3045-
* connect to the wrong database by using defaults, so require all
3046-
* parameters to be specified.
3047-
*/
3048-
pg_log_error("All connection parameters must be supplied because no "
3049-
"database connection exists");
3046+
pg_log_error("Do not give user, host, or port separately when using a connection string");
30503047
return false;
30513048
}
30523049

3053-
has_connection_string = dbname ?
3054-
recognized_connection_string(dbname) : false;
30553050
switch (reuse_previous_specification)
30563051
{
30573052
case TRI_YES:
@@ -3065,16 +3060,14 @@ do_connect(enum trivalue reuse_previous_specification,
30653060
break;
30663061
}
30673062

3068-
/* If the old connection does not exist, there is nothing to reuse. */
3069-
if (!o_conn)
3070-
reuse_previous = false;
3071-
3072-
/* Silently ignore arguments subsequent to a connection string. */
3073-
if (has_connection_string)
3063+
/*
3064+
* If we are to re-use parameters, there'd better be an old connection to
3065+
* get them from.
3066+
*/
3067+
if (reuse_previous && !o_conn)
30743068
{
3075-
user = NULL;
3076-
host = NULL;
3077-
port = NULL;
3069+
pg_log_error("No database connection exists to re-use parameters from");
3070+
return false;
30783071
}
30793072

30803073
/*
@@ -3103,6 +3096,7 @@ do_connect(enum trivalue reuse_previous_specification,
31033096
{
31043097
PQconninfoOption *ci;
31053098
PQconninfoOption *replci;
3099+
bool have_password = false;
31063100

31073101
for (ci = cinfo, replci = replcinfo;
31083102
ci->keyword && replci->keyword;
@@ -3121,6 +3115,26 @@ do_connect(enum trivalue reuse_previous_specification,
31213115

31223116
replci->val = ci->val;
31233117
ci->val = swap;
3118+
3119+
/*
3120+
* Check whether connstring provides options affecting
3121+
* password re-use. While any change in user, host,
3122+
* hostaddr, or port causes us to ignore the old
3123+
* connection's password, we don't force that for
3124+
* dbname, since passwords aren't database-specific.
3125+
*/
3126+
if (replci->val == NULL ||
3127+
strcmp(ci->val, replci->val) != 0)
3128+
{
3129+
if (strcmp(replci->keyword, "user") == 0 ||
3130+
strcmp(replci->keyword, "host") == 0 ||
3131+
strcmp(replci->keyword, "hostaddr") == 0 ||
3132+
strcmp(replci->keyword, "port") == 0)
3133+
keep_password = false;
3134+
}
3135+
/* Also note whether connstring contains a password. */
3136+
if (strcmp(replci->keyword, "password") == 0)
3137+
have_password = true;
31243138
}
31253139
}
31263140
Assert(ci->keyword == NULL && replci->keyword == NULL);
@@ -3130,8 +3144,13 @@ do_connect(enum trivalue reuse_previous_specification,
31303144

31313145
PQconninfoFree(replcinfo);
31323146

3133-
/* We never re-use a password with a conninfo string. */
3134-
keep_password = false;
3147+
/*
3148+
* If the connstring contains a password, tell the loop below
3149+
* that we may use it, regardless of other settings (i.e.,
3150+
* cinfo's password is no longer an "old" password).
3151+
*/
3152+
if (have_password)
3153+
keep_password = true;
31353154

31363155
/* Don't let code below try to inject dbname into params. */
31373156
dbname = NULL;
@@ -3219,14 +3238,6 @@ do_connect(enum trivalue reuse_previous_specification,
32193238
*/
32203239
password = prompt_for_password(has_connection_string ? NULL : user);
32213240
}
3222-
else if (o_conn && keep_password)
3223-
{
3224-
password = PQpass(o_conn);
3225-
if (password && *password)
3226-
password = pg_strdup(password);
3227-
else
3228-
password = NULL;
3229-
}
32303241

32313242
/* Loop till we have a connection or fail, which we might've already */
32323243
while (success)
@@ -3238,12 +3249,12 @@ do_connect(enum trivalue reuse_previous_specification,
32383249

32393250
/*
32403251
* Copy non-default settings into the PQconnectdbParams parameter
3241-
* arrays; but override any values specified old-style, as well as the
3242-
* password and a couple of fields we want to set forcibly.
3252+
* arrays; but inject any values specified old-style, as well as any
3253+
* interactively-obtained password, and a couple of fields we want to
3254+
* set forcibly.
32433255
*
32443256
* If you change this code, see also the initial-connection code in
3245-
* main(). For no good reason, a connection string password= takes
3246-
* precedence in main() but not here.
3257+
* main().
32473258
*/
32483259
for (ci = cinfo; ci->keyword; ci++)
32493260
{
@@ -3262,7 +3273,9 @@ do_connect(enum trivalue reuse_previous_specification,
32623273
}
32633274
else if (port && strcmp(ci->keyword, "port") == 0)
32643275
values[paramnum++] = port;
3265-
else if (strcmp(ci->keyword, "password") == 0)
3276+
/* If !keep_password, we unconditionally drop old password */
3277+
else if ((password || !keep_password) &&
3278+
strcmp(ci->keyword, "password") == 0)
32663279
values[paramnum++] = password;
32673280
else if (strcmp(ci->keyword, "fallback_application_name") == 0)
32683281
values[paramnum++] = pset.progname;

0 commit comments

Comments
 (0)