1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-19 13:42:17 +03:00
Files
postgres/src/fe_utils/connect_utils.c
Nathan Bossart 6d982e3b3b Harmonize password reuse in vacuumdb, clusterdb, and reindexdb.
Commits 83dec5a712 and ff402ae11b taught vacuumdb to reuse
passwords instead of prompting repeatedly.  However, the docs still
warn about repeated prompts, and this improvement was not applied
to clusterdb and reindexdb.  This commit allows clusterdb and
reindexdb to reuse passwords just like vacuumdb does, and it
expunges the aforementioned warnings from the docs.

Reviewed-by: Gurjeet Singh, Zhang Mingli
Discussion: https://postgr.es/m/20230628045741.GA1813397%40nathanxps13
2023-07-28 10:07:44 -07:00

177 lines
4.5 KiB
C

/*-------------------------------------------------------------------------
*
* Facilities for frontend code to connect to and disconnect from databases.
*
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/fe_utils/connect_utils.c
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include "common/connect.h"
#include "common/logging.h"
#include "common/string.h"
#include "fe_utils/connect_utils.h"
#include "fe_utils/query_utils.h"
/*
* Make a database connection with the given parameters.
*
* An interactive password prompt is automatically issued if needed and
* allowed by cparams->prompt_password.
*
* If allow_password_reuse is true, we will try to re-use any password
* given during previous calls to this routine. (Callers should not pass
* allow_password_reuse=true unless reconnecting to the same host+port+user
* as before, else we might create password exposure hazards.)
*/
PGconn *
connectDatabase(const ConnParams *cparams, const char *progname,
bool echo, bool fail_ok, bool allow_password_reuse)
{
PGconn *conn;
bool new_pass;
static char *password = NULL;
/* Callers must supply at least dbname; other params can be NULL */
Assert(cparams->dbname);
if (!allow_password_reuse && password)
{
free(password);
password = NULL;
}
if (cparams->prompt_password == TRI_YES && password == NULL)
password = simple_prompt("Password: ", false);
/*
* Start the connection. Loop until we have a password if requested by
* backend.
*/
do
{
const char *keywords[8];
const char *values[8];
int i = 0;
/*
* If dbname is a connstring, its entries can override the other
* values obtained from cparams; but in turn, override_dbname can
* override the dbname component of it.
*/
keywords[i] = "host";
values[i++] = cparams->pghost;
keywords[i] = "port";
values[i++] = cparams->pgport;
keywords[i] = "user";
values[i++] = cparams->pguser;
keywords[i] = "password";
values[i++] = password;
keywords[i] = "dbname";
values[i++] = cparams->dbname;
if (cparams->override_dbname)
{
keywords[i] = "dbname";
values[i++] = cparams->override_dbname;
}
keywords[i] = "fallback_application_name";
values[i++] = progname;
keywords[i] = NULL;
values[i++] = NULL;
Assert(i <= lengthof(keywords));
new_pass = false;
conn = PQconnectdbParams(keywords, values, true);
if (!conn)
pg_fatal("could not connect to database %s: out of memory",
cparams->dbname);
/*
* No luck? Trying asking (again) for a password.
*/
if (PQstatus(conn) == CONNECTION_BAD &&
PQconnectionNeedsPassword(conn) &&
cparams->prompt_password != TRI_NO)
{
PQfinish(conn);
free(password);
password = simple_prompt("Password: ", false);
new_pass = true;
}
} while (new_pass);
/* check to see that the backend connection was successfully made */
if (PQstatus(conn) == CONNECTION_BAD)
{
if (fail_ok)
{
PQfinish(conn);
return NULL;
}
pg_fatal("%s", PQerrorMessage(conn));
}
/* Start strict; callers may override this. */
PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo));
return conn;
}
/*
* Try to connect to the appropriate maintenance database.
*
* This differs from connectDatabase only in that it has a rule for
* inserting a default "dbname" if none was given (which is why cparams
* is not const). Note that cparams->dbname should typically come from
* a --maintenance-db command line parameter.
*/
PGconn *
connectMaintenanceDatabase(ConnParams *cparams,
const char *progname, bool echo)
{
PGconn *conn;
/* If a maintenance database name was specified, just connect to it. */
if (cparams->dbname)
return connectDatabase(cparams, progname, echo, false, false);
/* Otherwise, try postgres first and then template1. */
cparams->dbname = "postgres";
conn = connectDatabase(cparams, progname, echo, true, false);
if (!conn)
{
cparams->dbname = "template1";
conn = connectDatabase(cparams, progname, echo, false, false);
}
return conn;
}
/*
* Disconnect the given connection, canceling any statement if one is active.
*/
void
disconnectDatabase(PGconn *conn)
{
char errbuf[256];
Assert(conn != NULL);
if (PQtransactionStatus(conn) == PQTRANS_ACTIVE)
{
PGcancel *cancel;
if ((cancel = PQgetCancel(conn)))
{
(void) PQcancel(cancel, errbuf, sizeof(errbuf));
PQfreeCancel(cancel);
}
}
PQfinish(conn);
}