]> granicus.if.org Git - postgresql/commitdiff
vacuumdb: don't prompt for passwords over and over
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 12 Nov 2015 21:05:23 +0000 (18:05 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 12 Nov 2015 21:05:23 +0000 (18:05 -0300)
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.

src/bin/scripts/clusterdb.c
src/bin/scripts/common.c
src/bin/scripts/common.h
src/bin/scripts/createlang.c
src/bin/scripts/createuser.c
src/bin/scripts/droplang.c
src/bin/scripts/dropuser.c
src/bin/scripts/reindexdb.c
src/bin/scripts/vacuumdb.c

index 8c0e7cfab28ddb2e323f2465b2bc06bacea16fc3..2f15c82273f8c4fab4fc3090b4f331cfa20ace73 100644 (file)
@@ -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))
        {
index 0deadec09756ae3f15e3baee646380ed95972082..d26a4edbb68e6305aa86013c2edbe09f1a2ce474 100644 (file)
@@ -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;
index b5ce1ed7444012921ec175ea2dbde00e7aebd432..dafd58f42255c271bad23acd983207eeb5dbe720 100644 (file)
@@ -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,
index 228215c7387071e9c612a60f8e91fc31023d96e8..38cc20d9d4f160ed3897ee3807aa1e2007f6e638 100644 (file)
@@ -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
index c8bcf0d0b2e65c79dc6f6240f2859991bed690e3..9e7f84d9d543fe221c95add51dbc32a91af0ad10 100644 (file)
@@ -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);
 
index 746732825c0fe6f71ffbd796b2fba607350e99cb..502ec6475e8521012b0fbef92257b0b94dd71d04 100644 (file)
@@ -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
index 916056662b6e0a547a2eebae97b91f966d948e58..10d0691c6772656b5b209f05f3b5b6378de83e48 100644 (file)
@@ -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);
index decb7538db5bfed2ba9f0b18819673d5363c145c..fbf436c6d5973683cfa932cc21f7f3e4161ef8f8 100644 (file)
@@ -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"),
index 4ce27b78c54b13cadb6729f74af159ba0def2a5e..6c67263f3c6bfcd1bc86232bf968d1ba693db461 100644 (file)
@@ -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);
 }
 
 /*