diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index ed88ac001a..21195e0e72 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -7116,6 +7116,45 @@ char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
+
+ PQchangePasswordPQchangePassword
+
+
+
+ Changes a PostgreSQL password.
+
+PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd);
+
+ This function uses PQencryptPasswordConn
+ to build and execute the command ALTER USER ... PASSWORD
+ '...', thereby changing the user's password. It exists for
+ the same reason as PQencryptPasswordConn, but
+ is more convenient as it both builds and runs the command for you.
+ is passed a
+ NULL for the algorithm argument, hence encryption is
+ done according to the server's
+ setting.
+
+
+
+ The user and passwd arguments
+ are the SQL name of the target user, and the new cleartext password.
+
+
+
+ Returns a PGresult pointer representing
+ the result of the ALTER USER command, or
+ a null pointer if the routine failed before issuing any command.
+ The function should be called
+ to check the return value for any errors (including the value of a null
+ pointer, in which case it will return
+ PGRES_FATAL_ERROR). Use
+ to get more information about
+ such errors.
+
+
+
+
PQencryptPasswordPQencryptPassword
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9103bc3465..eb216b7c09 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -2158,29 +2158,15 @@ exec_command_password(PsqlScanState scan_state, bool active_branch)
}
else
{
- char *encrypted_password;
+ PGresult *res = PQchangePassword(pset.db, user, pw1);
- encrypted_password = PQencryptPasswordConn(pset.db, pw1, user, NULL);
-
- if (!encrypted_password)
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
pg_log_info("%s", PQerrorMessage(pset.db));
success = false;
}
- else
- {
- PGresult *res;
- printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD ",
- fmtId(user));
- appendStringLiteralConn(&buf, encrypted_password, pset.db);
- res = PSQLexec(buf.data);
- if (!res)
- success = false;
- else
- PQclear(res);
- PQfreemem(encrypted_password);
- }
+ PQclear(res);
}
free(user);
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 850734ac96..28b861fd93 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -191,3 +191,4 @@ PQclosePrepared 188
PQclosePortal 189
PQsendClosePrepared 190
PQsendClosePortal 191
+PQchangePassword 192
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index da5ccd324d..1a8e4f6fbf 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -1372,3 +1372,84 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
return crypt_pwd;
}
+
+/*
+ * PQchangePassword -- exported routine to change a password
+ *
+ * This is intended to be used by client applications that wish to
+ * change the password for a user. The password is not sent in
+ * cleartext because it is encrypted on the client side. This is
+ * good because it ensures the cleartext password is never known by
+ * the server, and therefore won't end up in logs, pg_stat displays,
+ * etc. The password encryption is performed by PQencryptPasswordConn(),
+ * which is passed a NULL for the algorithm argument. Hence encryption
+ * is done according to the server's password_encryption
+ * setting. We export the function so that clients won't be dependent
+ * on the implementation specific details with respect to how the
+ * server changes passwords.
+ *
+ * Arguments are a connection object, the SQL name of the target user,
+ * and the cleartext password.
+ *
+ * Return value is the PGresult of the executed ALTER USER statement
+ * or NULL if we never get there. The caller is responsible to PQclear()
+ * the returned PGresult.
+ *
+ * PQresultStatus() should be called to check the return value for errors,
+ * and PQerrorMessage() used to get more information about such errors.
+ */
+PGresult *
+PQchangePassword(PGconn *conn, const char *user, const char *passwd)
+{
+ char *encrypted_password = PQencryptPasswordConn(conn, passwd,
+ user, NULL);
+
+ if (!encrypted_password)
+ {
+ /* PQencryptPasswordConn() already registered the error */
+ return NULL;
+ }
+ else
+ {
+ char *fmtpw = PQescapeLiteral(conn, encrypted_password,
+ strlen(encrypted_password));
+
+ /* no longer needed, so clean up now */
+ PQfreemem(encrypted_password);
+
+ if (!fmtpw)
+ {
+ /* PQescapeLiteral() already registered the error */
+ return NULL;
+ }
+ else
+ {
+ char *fmtuser = PQescapeIdentifier(conn, user, strlen(user));
+
+ if (!fmtuser)
+ {
+ /* PQescapeIdentifier() already registered the error */
+ PQfreemem(fmtpw);
+ return NULL;
+ }
+ else
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD %s",
+ fmtuser, fmtpw);
+
+ res = PQexec(conn, buf.data);
+
+ /* clean up */
+ termPQExpBuffer(&buf);
+ PQfreemem(fmtuser);
+ PQfreemem(fmtpw);
+
+ return res;
+ }
+ }
+ }
+}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 12eb72c1fe..f0ec660cb6 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -659,6 +659,7 @@ extern int PQenv2encoding(void);
extern char *PQencryptPassword(const char *passwd, const char *user);
extern char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm);
+extern PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd);
/* === in encnames.c === */