mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-05 22:39:21 +02:00
vacuumdb: don't prompt for passwords over and over
Having the script prompt for passwords over and over was a preexisting
problem when it processed multiple databases or when it processed
multiple analyze stages, but the parallel mode introduced in commit
a179232047
made it worse.
Fix the annoyance by keeping a copy of the password used by the first
connection that requires one. Since users can (currently) only have a
single password, there's no need for more complex arrangements (such as
remembering one password per database).
Per bug #13741 reported by Eric Brown. Patch authored and
cross-reviewed by Haribabu Kommi and Michael Paquier, slightly tweaked
by Álvaro Herrera.
Discussion: http://www.postgresql.org/message-id/20151027193919.931.54948@wrigleys.postgresql.org
Backpatch to 9.5, where parallel vacuumdb was introduced.
This commit is contained in:
parent
747854f010
commit
5094da99b9
@ -203,7 +203,7 @@ cluster_one_database(const char *dbname, bool verbose, const char *table,
|
||||
appendPQExpBuffer(&sql, " %s", table);
|
||||
appendPQExpBufferChar(&sql, ';');
|
||||
|
||||
conn = connectDatabase(dbname, host, port, username, prompt_password,
|
||||
conn = connectDatabase(dbname, host, port, username, NULL, prompt_password,
|
||||
progname, false);
|
||||
if (!executeMaintenanceCommand(conn, sql.data, echo))
|
||||
{
|
||||
|
@ -52,19 +52,24 @@ handle_help_version_opts(int argc, char *argv[],
|
||||
|
||||
|
||||
/*
|
||||
* Make a database connection with the given parameters. An
|
||||
* interactive password prompt is automatically issued if required.
|
||||
* Make a database connection with the given parameters.
|
||||
*
|
||||
* A password can be given, but if not (or if user forces us to) we prompt
|
||||
* interactively for one, unless caller prohibited us from doing so.
|
||||
*/
|
||||
PGconn *
|
||||
connectDatabase(const char *dbname, const char *pghost, const char *pgport,
|
||||
const char *pguser, enum trivalue prompt_password,
|
||||
const char *progname, bool fail_ok)
|
||||
const char *pguser, const char *pgpassword,
|
||||
enum trivalue prompt_password, const char *progname,
|
||||
bool fail_ok)
|
||||
{
|
||||
PGconn *conn;
|
||||
char *password = NULL;
|
||||
char *password;
|
||||
bool new_pass;
|
||||
|
||||
if (prompt_password == TRI_YES)
|
||||
password = pgpassword ? strdup(pgpassword) : NULL;
|
||||
|
||||
if (prompt_password == TRI_YES && !pgpassword)
|
||||
password = simple_prompt("Password: ", 100, false);
|
||||
|
||||
/*
|
||||
@ -95,22 +100,26 @@ connectDatabase(const char *dbname, const char *pghost, const char *pgport,
|
||||
new_pass = false;
|
||||
conn = PQconnectdbParams(keywords, values, true);
|
||||
|
||||
free(keywords);
|
||||
free(values);
|
||||
|
||||
if (!conn)
|
||||
{
|
||||
fprintf(stderr, _("%s: could not connect to database %s\n"),
|
||||
fprintf(stderr, _("%s: could not connect to database %s: out of memory\n"),
|
||||
progname, dbname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pg_free(keywords);
|
||||
pg_free(values);
|
||||
|
||||
/*
|
||||
* No luck? Trying asking (again) for a password.
|
||||
*/
|
||||
if (PQstatus(conn) == CONNECTION_BAD &&
|
||||
PQconnectionNeedsPassword(conn) &&
|
||||
password == NULL &&
|
||||
prompt_password != TRI_NO)
|
||||
{
|
||||
PQfinish(conn);
|
||||
if (password)
|
||||
free(password);
|
||||
password = simple_prompt("Password: ", 100, false);
|
||||
new_pass = true;
|
||||
}
|
||||
@ -148,14 +157,14 @@ connectMaintenanceDatabase(const char *maintenance_db, const char *pghost,
|
||||
|
||||
/* If a maintenance database name was specified, just connect to it. */
|
||||
if (maintenance_db)
|
||||
return connectDatabase(maintenance_db, pghost, pgport, pguser,
|
||||
return connectDatabase(maintenance_db, pghost, pgport, pguser, NULL,
|
||||
prompt_password, progname, false);
|
||||
|
||||
/* Otherwise, try postgres first and then template1. */
|
||||
conn = connectDatabase("postgres", pghost, pgport, pguser, prompt_password,
|
||||
progname, true);
|
||||
conn = connectDatabase("postgres", pghost, pgport, pguser, NULL,
|
||||
prompt_password, progname, true);
|
||||
if (!conn)
|
||||
conn = connectDatabase("template1", pghost, pgport, pguser,
|
||||
conn = connectDatabase("template1", pghost, pgport, pguser, NULL,
|
||||
prompt_password, progname, false);
|
||||
|
||||
return conn;
|
||||
|
@ -31,8 +31,8 @@ extern void handle_help_version_opts(int argc, char *argv[],
|
||||
|
||||
extern PGconn *connectDatabase(const char *dbname, const char *pghost,
|
||||
const char *pgport, const char *pguser,
|
||||
enum trivalue prompt_password, const char *progname,
|
||||
bool fail_ok);
|
||||
const char *pgpassword, enum trivalue prompt_password,
|
||||
const char *progname, bool fail_ok);
|
||||
|
||||
extern PGconn *connectMaintenanceDatabase(const char *maintenance_db,
|
||||
const char *pghost, const char *pgport, const char *pguser,
|
||||
|
@ -140,8 +140,8 @@ main(int argc, char *argv[])
|
||||
printQueryOpt popt;
|
||||
static const bool translate_columns[] = {false, true};
|
||||
|
||||
conn = connectDatabase(dbname, host, port, username, prompt_password,
|
||||
progname, false);
|
||||
conn = connectDatabase(dbname, host, port, username, NULL,
|
||||
prompt_password, progname, false);
|
||||
|
||||
printfPQExpBuffer(&sql, "SELECT lanname as \"%s\", "
|
||||
"(CASE WHEN lanpltrusted THEN '%s' ELSE '%s' END) as \"%s\" "
|
||||
@ -180,8 +180,8 @@ main(int argc, char *argv[])
|
||||
if (*p >= 'A' && *p <= 'Z')
|
||||
*p += ('a' - 'A');
|
||||
|
||||
conn = connectDatabase(dbname, host, port, username, prompt_password,
|
||||
progname, false);
|
||||
conn = connectDatabase(dbname, host, port, username, NULL,
|
||||
prompt_password, progname, false);
|
||||
|
||||
/*
|
||||
* Make sure the language isn't already installed
|
||||
|
@ -250,8 +250,8 @@ main(int argc, char *argv[])
|
||||
if (login == 0)
|
||||
login = TRI_YES;
|
||||
|
||||
conn = connectDatabase("postgres", host, port, username, prompt_password,
|
||||
progname, false);
|
||||
conn = connectDatabase("postgres", host, port, username, NULL,
|
||||
prompt_password, progname, false);
|
||||
|
||||
initPQExpBuffer(&sql);
|
||||
|
||||
|
@ -139,8 +139,8 @@ main(int argc, char *argv[])
|
||||
printQueryOpt popt;
|
||||
static const bool translate_columns[] = {false, true};
|
||||
|
||||
conn = connectDatabase(dbname, host, port, username, prompt_password,
|
||||
progname, false);
|
||||
conn = connectDatabase(dbname, host, port, username, NULL,
|
||||
prompt_password, progname, false);
|
||||
|
||||
printfPQExpBuffer(&sql, "SELECT lanname as \"%s\", "
|
||||
"(CASE WHEN lanpltrusted THEN '%s' ELSE '%s' END) as \"%s\" "
|
||||
@ -181,8 +181,8 @@ main(int argc, char *argv[])
|
||||
if (*p >= 'A' && *p <= 'Z')
|
||||
*p += ('a' - 'A');
|
||||
|
||||
conn = connectDatabase(dbname, host, port, username, prompt_password,
|
||||
progname, false);
|
||||
conn = connectDatabase(dbname, host, port, username, NULL,
|
||||
prompt_password, progname, false);
|
||||
|
||||
/*
|
||||
* Force schema search path to be just pg_catalog, so that we don't have
|
||||
|
@ -128,8 +128,8 @@ main(int argc, char *argv[])
|
||||
appendPQExpBuffer(&sql, "DROP ROLE %s%s;",
|
||||
(if_exists ? "IF EXISTS " : ""), fmtId(dropuser));
|
||||
|
||||
conn = connectDatabase("postgres", host, port, username, prompt_password,
|
||||
progname, false);
|
||||
conn = connectDatabase("postgres", host, port, username, NULL,
|
||||
prompt_password, progname, false);
|
||||
|
||||
if (echo)
|
||||
printf("%s\n", sql.data);
|
||||
|
@ -297,8 +297,8 @@ reindex_one_database(const char *name, const char *dbname, const char *type,
|
||||
appendPQExpBuffer(&sql, " DATABASE %s", fmtId(name));
|
||||
appendPQExpBufferChar(&sql, ';');
|
||||
|
||||
conn = connectDatabase(dbname, host, port, username, prompt_password,
|
||||
progname, false);
|
||||
conn = connectDatabase(dbname, host, port, username, NULL,
|
||||
prompt_password, progname, false);
|
||||
|
||||
if (!executeMaintenanceCommand(conn, sql.data, echo))
|
||||
{
|
||||
@ -372,8 +372,8 @@ reindex_system_catalogs(const char *dbname, const char *host, const char *port,
|
||||
|
||||
appendPQExpBuffer(&sql, " SYSTEM %s;", dbname);
|
||||
|
||||
conn = connectDatabase(dbname, host, port, username, prompt_password,
|
||||
progname, false);
|
||||
conn = connectDatabase(dbname, host, port, username, NULL,
|
||||
prompt_password, progname, false);
|
||||
if (!executeMaintenanceCommand(conn, sql.data, echo))
|
||||
{
|
||||
fprintf(stderr, _("%s: reindexing of system catalogs failed: %s"),
|
||||
|
@ -43,7 +43,8 @@ static void vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
|
||||
const char *host, const char *port,
|
||||
const char *username, enum trivalue prompt_password,
|
||||
int concurrentCons,
|
||||
const char *progname, bool echo, bool quiet);
|
||||
const char *progname, bool echo, bool quiet,
|
||||
char **password);
|
||||
|
||||
static void vacuum_all_databases(vacuumingOptions *vacopts,
|
||||
bool analyze_in_stages,
|
||||
@ -275,6 +276,8 @@ main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
char *password = NULL;
|
||||
|
||||
if (dbname == NULL)
|
||||
{
|
||||
if (getenv("PGDATABASE"))
|
||||
@ -296,7 +299,8 @@ main(int argc, char *argv[])
|
||||
&tables,
|
||||
host, port, username, prompt_password,
|
||||
concurrentCons,
|
||||
progname, echo, quiet);
|
||||
progname, echo, quiet,
|
||||
&password);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -305,7 +309,10 @@ main(int argc, char *argv[])
|
||||
&tables,
|
||||
host, port, username, prompt_password,
|
||||
concurrentCons,
|
||||
progname, echo, quiet);
|
||||
progname, echo, quiet,
|
||||
&password);
|
||||
|
||||
pg_free(password);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
@ -323,15 +330,21 @@ main(int argc, char *argv[])
|
||||
* If concurrentCons is > 1, multiple connections are used to vacuum tables
|
||||
* in parallel. In this case and if the table list is empty, we first obtain
|
||||
* a list of tables from the database.
|
||||
*
|
||||
* 'password' is both an input and output parameter. If one is not passed,
|
||||
* then whatever is used in a connection is returned so that caller can
|
||||
* reuse it in future connections.
|
||||
*/
|
||||
static void
|
||||
vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
|
||||
int stage,
|
||||
SimpleStringList *tables,
|
||||
const char *host, const char *port,
|
||||
const char *username, enum trivalue prompt_password,
|
||||
const char *username,
|
||||
enum trivalue prompt_password,
|
||||
int concurrentCons,
|
||||
const char *progname, bool echo, bool quiet)
|
||||
const char *progname, bool echo, bool quiet,
|
||||
char **password)
|
||||
{
|
||||
PQExpBufferData sql;
|
||||
PGconn *conn;
|
||||
@ -365,8 +378,15 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
conn = connectDatabase(dbname, host, port, username, prompt_password,
|
||||
progname, false);
|
||||
conn = connectDatabase(dbname, host, port, username, *password,
|
||||
prompt_password, progname, false);
|
||||
|
||||
/*
|
||||
* If no password was not specified by caller and the connection required
|
||||
* one, remember it; this suppresses further password prompts.
|
||||
*/
|
||||
if (PQconnectionUsedPassword(conn) && *password == NULL)
|
||||
*password = pg_strdup(PQpass(conn));
|
||||
|
||||
initPQExpBuffer(&sql);
|
||||
|
||||
@ -424,10 +444,20 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
|
||||
init_slot(slots, conn);
|
||||
if (parallel)
|
||||
{
|
||||
const char *pqpass;
|
||||
|
||||
/*
|
||||
* If a password was supplied for the initial connection, use it for
|
||||
* subsequent ones too. (Note that since we're connecting to the same
|
||||
* database with the same user, there's no need to update the stored
|
||||
* password any further.)
|
||||
*/
|
||||
pqpass = PQpass(conn);
|
||||
|
||||
for (i = 1; i < concurrentCons; i++)
|
||||
{
|
||||
conn = connectDatabase(dbname, host, port, username, prompt_password,
|
||||
progname, false);
|
||||
conn = connectDatabase(dbname, host, port, username, pqpass,
|
||||
prompt_password, progname, false);
|
||||
init_slot(slots + i, conn);
|
||||
}
|
||||
}
|
||||
@ -542,12 +572,23 @@ vacuum_all_databases(vacuumingOptions *vacopts,
|
||||
PGresult *result;
|
||||
int stage;
|
||||
int i;
|
||||
char *password = NULL;
|
||||
|
||||
conn = connectMaintenanceDatabase(maintenance_db, host, port,
|
||||
username, prompt_password, progname);
|
||||
|
||||
result = executeQuery(conn,
|
||||
"SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;",
|
||||
progname, echo);
|
||||
|
||||
/*
|
||||
* Remember the password for further connections. If no password was
|
||||
* required for the maintenance db connection, this gets updated for the
|
||||
* first connection that does.
|
||||
*/
|
||||
if (PQconnectionUsedPassword(conn))
|
||||
password = pg_strdup(PQpass(conn));
|
||||
|
||||
PQfinish(conn);
|
||||
|
||||
if (analyze_in_stages)
|
||||
@ -572,7 +613,8 @@ vacuum_all_databases(vacuumingOptions *vacopts,
|
||||
NULL,
|
||||
host, port, username, prompt_password,
|
||||
concurrentCons,
|
||||
progname, echo, quiet);
|
||||
progname, echo, quiet,
|
||||
&password);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -588,11 +630,13 @@ vacuum_all_databases(vacuumingOptions *vacopts,
|
||||
NULL,
|
||||
host, port, username, prompt_password,
|
||||
concurrentCons,
|
||||
progname, echo, quiet);
|
||||
progname, echo, quiet,
|
||||
&password);
|
||||
}
|
||||
}
|
||||
|
||||
PQclear(result);
|
||||
pg_free(password);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user