From c0a225ba3f77b9b972e4225858cecac6b723deab Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Tue, 2 Feb 2021 12:30:53 -0800
Subject: [PATCH v37 1/4] Moving code from src/bin/scripts to fe_utils

To make code useable from contrib/pg_amcheck, moving
scripts_parallel.[ch] and handle_help_version_opts() into
fe_utils.

Moving supporting code from src/bin/scripts/common into fe_utils.

Updating applications in src/bin/scripts with the new location.
---
 src/bin/scripts/Makefile                      |   6 +-
 src/bin/scripts/clusterdb.c                   |   2 +
 src/bin/scripts/common.c                      | 318 +-----------------
 src/bin/scripts/common.h                      |  49 +--
 src/bin/scripts/createdb.c                    |   1 +
 src/bin/scripts/createuser.c                  |   1 +
 src/bin/scripts/dropdb.c                      |   1 +
 src/bin/scripts/dropuser.c                    |   1 +
 src/bin/scripts/nls.mk                        |   2 +-
 src/bin/scripts/pg_isready.c                  |   1 +
 src/bin/scripts/reindexdb.c                   |   4 +-
 src/bin/scripts/vacuumdb.c                    |   4 +-
 src/fe_utils/Makefile                         |   4 +
 src/fe_utils/connect_utils.c                  | 181 ++++++++++
 src/fe_utils/option_utils.c                   |  38 +++
 .../parallel_slot.c}                          |  63 +++-
 src/fe_utils/query_utils.c                    |  92 +++++
 src/include/fe_utils/connect_utils.h          |  48 +++
 src/include/fe_utils/option_utils.h           |  23 ++
 .../fe_utils/parallel_slot.h}                 |  13 +-
 src/include/fe_utils/query_utils.h            |  26 ++
 src/tools/msvc/Mkvcbuild.pm                   |   4 +-
 src/tools/pgindent/typedefs.list              |   1 +
 23 files changed, 498 insertions(+), 385 deletions(-)
 create mode 100644 src/fe_utils/connect_utils.c
 create mode 100644 src/fe_utils/option_utils.c
 rename src/{bin/scripts/scripts_parallel.c => fe_utils/parallel_slot.c} (80%)
 create mode 100644 src/fe_utils/query_utils.c
 create mode 100644 src/include/fe_utils/connect_utils.h
 create mode 100644 src/include/fe_utils/option_utils.h
 rename src/{bin/scripts/scripts_parallel.h => include/fe_utils/parallel_slot.h} (82%)
 create mode 100644 src/include/fe_utils/query_utils.h

diff --git a/src/bin/scripts/Makefile b/src/bin/scripts/Makefile
index a02e4e430c..b8d7cf2f2d 100644
--- a/src/bin/scripts/Makefile
+++ b/src/bin/scripts/Makefile
@@ -28,8 +28,8 @@ createuser: createuser.o common.o $(WIN32RES) | submake-libpq submake-libpgport
 dropdb: dropdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
 dropuser: dropuser.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
 clusterdb: clusterdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
-vacuumdb: vacuumdb.o common.o scripts_parallel.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
-reindexdb: reindexdb.o common.o scripts_parallel.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
+vacuumdb: vacuumdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
+reindexdb: reindexdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
 pg_isready: pg_isready.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
 
 install: all installdirs
@@ -50,7 +50,7 @@ uninstall:
 
 clean distclean maintainer-clean:
 	rm -f $(addsuffix $(X), $(PROGRAMS)) $(addsuffix .o, $(PROGRAMS))
-	rm -f common.o scripts_parallel.o $(WIN32RES)
+	rm -f common.o $(WIN32RES)
 	rm -rf tmp_check
 
 check:
diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c
index 7d25bb31d4..fc771eed77 100644
--- a/src/bin/scripts/clusterdb.c
+++ b/src/bin/scripts/clusterdb.c
@@ -13,6 +13,8 @@
 #include "common.h"
 #include "common/logging.h"
 #include "fe_utils/cancel.h"
+#include "fe_utils/option_utils.h"
+#include "fe_utils/query_utils.h"
 #include "fe_utils/simple_list.h"
 #include "fe_utils/string_utils.h"
 
diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c
index 21ef297e6e..c86c19eae2 100644
--- a/src/bin/scripts/common.c
+++ b/src/bin/scripts/common.c
@@ -22,325 +22,9 @@
 #include "common/logging.h"
 #include "common/string.h"
 #include "fe_utils/cancel.h"
+#include "fe_utils/query_utils.h"
 #include "fe_utils/string_utils.h"
 
-#define ERRCODE_UNDEFINED_TABLE  "42P01"
-
-/*
- * Provide strictly harmonized handling of --help and --version
- * options.
- */
-void
-handle_help_version_opts(int argc, char *argv[],
-						 const char *fixed_progname, help_handler hlp)
-{
-	if (argc > 1)
-	{
-		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
-		{
-			hlp(get_progname(argv[0]));
-			exit(0);
-		}
-		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
-		{
-			printf("%s (PostgreSQL) " PG_VERSION "\n", fixed_progname);
-			exit(0);
-		}
-	}
-}
-
-
-/*
- * 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 database+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_log_error("could not connect to database %s: out of memory",
-						 cparams->dbname);
-			exit(1);
-		}
-
-		/*
-		 * No luck?  Trying asking (again) for a password.
-		 */
-		if (PQstatus(conn) == CONNECTION_BAD &&
-			PQconnectionNeedsPassword(conn) &&
-			cparams->prompt_password != TRI_NO)
-		{
-			PQfinish(conn);
-			if (password)
-				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_log_error("%s", PQerrorMessage(conn));
-		exit(1);
-	}
-
-	/* 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);
-}
-
-/*
- * Run a query, return the results, exit program on failure.
- */
-PGresult *
-executeQuery(PGconn *conn, const char *query, bool echo)
-{
-	PGresult   *res;
-
-	if (echo)
-		printf("%s\n", query);
-
-	res = PQexec(conn, query);
-	if (!res ||
-		PQresultStatus(res) != PGRES_TUPLES_OK)
-	{
-		pg_log_error("query failed: %s", PQerrorMessage(conn));
-		pg_log_info("query was: %s", query);
-		PQfinish(conn);
-		exit(1);
-	}
-
-	return res;
-}
-
-
-/*
- * As above for a SQL command (which returns nothing).
- */
-void
-executeCommand(PGconn *conn, const char *query, bool echo)
-{
-	PGresult   *res;
-
-	if (echo)
-		printf("%s\n", query);
-
-	res = PQexec(conn, query);
-	if (!res ||
-		PQresultStatus(res) != PGRES_COMMAND_OK)
-	{
-		pg_log_error("query failed: %s", PQerrorMessage(conn));
-		pg_log_info("query was: %s", query);
-		PQfinish(conn);
-		exit(1);
-	}
-
-	PQclear(res);
-}
-
-
-/*
- * As above for a SQL maintenance command (returns command success).
- * Command is executed with a cancel handler set, so Ctrl-C can
- * interrupt it.
- */
-bool
-executeMaintenanceCommand(PGconn *conn, const char *query, bool echo)
-{
-	PGresult   *res;
-	bool		r;
-
-	if (echo)
-		printf("%s\n", query);
-
-	SetCancelConn(conn);
-	res = PQexec(conn, query);
-	ResetCancelConn();
-
-	r = (res && PQresultStatus(res) == PGRES_COMMAND_OK);
-
-	if (res)
-		PQclear(res);
-
-	return r;
-}
-
-/*
- * Consume all the results generated for the given connection until
- * nothing remains.  If at least one error is encountered, return false.
- * Note that this will block if the connection is busy.
- */
-bool
-consumeQueryResult(PGconn *conn)
-{
-	bool		ok = true;
-	PGresult   *result;
-
-	SetCancelConn(conn);
-	while ((result = PQgetResult(conn)) != NULL)
-	{
-		if (!processQueryResult(conn, result))
-			ok = false;
-	}
-	ResetCancelConn();
-	return ok;
-}
-
-/*
- * Process (and delete) a query result.  Returns true if there's no error,
- * false otherwise -- but errors about trying to work on a missing relation
- * are reported and subsequently ignored.
- */
-bool
-processQueryResult(PGconn *conn, PGresult *result)
-{
-	/*
-	 * If it's an error, report it.  Errors about a missing table are harmless
-	 * so we continue processing; but die for other errors.
-	 */
-	if (PQresultStatus(result) != PGRES_COMMAND_OK)
-	{
-		char	   *sqlState = PQresultErrorField(result, PG_DIAG_SQLSTATE);
-
-		pg_log_error("processing of database \"%s\" failed: %s",
-					 PQdb(conn), PQerrorMessage(conn));
-
-		if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) != 0)
-		{
-			PQclear(result);
-			return false;
-		}
-	}
-
-	PQclear(result);
-	return true;
-}
-
-
 /*
  * Split TABLE[(COLUMNS)] into TABLE and [(COLUMNS)] portions.  When you
  * finish using them, pg_free(*table).  *columns is a pointer into "spec",
diff --git a/src/bin/scripts/common.h b/src/bin/scripts/common.h
index 5630975712..ddd8f35274 100644
--- a/src/bin/scripts/common.h
+++ b/src/bin/scripts/common.h
@@ -10,58 +10,11 @@
 #define COMMON_H
 
 #include "common/username.h"
+#include "fe_utils/connect_utils.h"
 #include "getopt_long.h"		/* pgrminclude ignore */
 #include "libpq-fe.h"
 #include "pqexpbuffer.h"		/* pgrminclude ignore */
 
-enum trivalue
-{
-	TRI_DEFAULT,
-	TRI_NO,
-	TRI_YES
-};
-
-/* Parameters needed by connectDatabase/connectMaintenanceDatabase */
-typedef struct _connParams
-{
-	/* These fields record the actual command line parameters */
-	const char *dbname;			/* this may be a connstring! */
-	const char *pghost;
-	const char *pgport;
-	const char *pguser;
-	enum trivalue prompt_password;
-	/* If not NULL, this overrides the dbname obtained from command line */
-	/* (but *only* the DB name, not anything else in the connstring) */
-	const char *override_dbname;
-} ConnParams;
-
-typedef void (*help_handler) (const char *progname);
-
-extern void handle_help_version_opts(int argc, char *argv[],
-									 const char *fixed_progname,
-									 help_handler hlp);
-
-extern PGconn *connectDatabase(const ConnParams *cparams,
-							   const char *progname,
-							   bool echo, bool fail_ok,
-							   bool allow_password_reuse);
-
-extern PGconn *connectMaintenanceDatabase(ConnParams *cparams,
-										  const char *progname, bool echo);
-
-extern void disconnectDatabase(PGconn *conn);
-
-extern PGresult *executeQuery(PGconn *conn, const char *query, bool echo);
-
-extern void executeCommand(PGconn *conn, const char *query, bool echo);
-
-extern bool executeMaintenanceCommand(PGconn *conn, const char *query,
-									  bool echo);
-
-extern bool consumeQueryResult(PGconn *conn);
-
-extern bool processQueryResult(PGconn *conn, PGresult *result);
-
 extern void splitTableColumnsSpec(const char *spec, int encoding,
 								  char **table, const char **columns);
 
diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c
index abf21d4942..041454f075 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -13,6 +13,7 @@
 
 #include "common.h"
 #include "common/logging.h"
+#include "fe_utils/option_utils.h"
 #include "fe_utils/string_utils.h"
 
 
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index 47b0e28bc6..ef7e0e549f 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -14,6 +14,7 @@
 #include "common.h"
 #include "common/logging.h"
 #include "common/string.h"
+#include "fe_utils/option_utils.h"
 #include "fe_utils/simple_list.h"
 #include "fe_utils/string_utils.h"
 
diff --git a/src/bin/scripts/dropdb.c b/src/bin/scripts/dropdb.c
index ba0dcdecb9..b154ed1bb6 100644
--- a/src/bin/scripts/dropdb.c
+++ b/src/bin/scripts/dropdb.c
@@ -13,6 +13,7 @@
 #include "postgres_fe.h"
 #include "common.h"
 #include "common/logging.h"
+#include "fe_utils/option_utils.h"
 #include "fe_utils/string_utils.h"
 
 
diff --git a/src/bin/scripts/dropuser.c b/src/bin/scripts/dropuser.c
index ff5b455ae5..61b8557bc7 100644
--- a/src/bin/scripts/dropuser.c
+++ b/src/bin/scripts/dropuser.c
@@ -14,6 +14,7 @@
 #include "common.h"
 #include "common/logging.h"
 #include "common/string.h"
+#include "fe_utils/option_utils.h"
 #include "fe_utils/string_utils.h"
 
 
diff --git a/src/bin/scripts/nls.mk b/src/bin/scripts/nls.mk
index 5d5dd11b7b..7fc716092e 100644
--- a/src/bin/scripts/nls.mk
+++ b/src/bin/scripts/nls.mk
@@ -7,7 +7,7 @@ GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) \
                    clusterdb.c vacuumdb.c reindexdb.c \
                    pg_isready.c \
                    common.c \
-                   scripts_parallel.c \
+                   ../../fe_utils/parallel_slot.c \
                    ../../fe_utils/cancel.c ../../fe_utils/print.c \
                    ../../common/fe_memutils.c ../../common/username.c
 GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) simple_prompt yesno_prompt
diff --git a/src/bin/scripts/pg_isready.c b/src/bin/scripts/pg_isready.c
index ceb8a09b4c..fc6f7b0a93 100644
--- a/src/bin/scripts/pg_isready.c
+++ b/src/bin/scripts/pg_isready.c
@@ -12,6 +12,7 @@
 #include "postgres_fe.h"
 #include "common.h"
 #include "common/logging.h"
+#include "fe_utils/option_utils.h"
 
 #define DEFAULT_CONNECT_TIMEOUT "3"
 
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index dece8200fa..7781fb1151 100644
--- a/src/bin/scripts/reindexdb.c
+++ b/src/bin/scripts/reindexdb.c
@@ -16,9 +16,11 @@
 #include "common/connect.h"
 #include "common/logging.h"
 #include "fe_utils/cancel.h"
+#include "fe_utils/option_utils.h"
+#include "fe_utils/parallel_slot.h"
+#include "fe_utils/query_utils.h"
 #include "fe_utils/simple_list.h"
 #include "fe_utils/string_utils.h"
-#include "scripts_parallel.h"
 
 typedef enum ReindexType
 {
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 8246327770..ed320817bc 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -18,9 +18,11 @@
 #include "common/connect.h"
 #include "common/logging.h"
 #include "fe_utils/cancel.h"
+#include "fe_utils/option_utils.h"
+#include "fe_utils/parallel_slot.h"
+#include "fe_utils/query_utils.h"
 #include "fe_utils/simple_list.h"
 #include "fe_utils/string_utils.h"
-#include "scripts_parallel.h"
 
 
 /* vacuum options controlled by user flags */
diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile
index 10d6838cf9..456c441a33 100644
--- a/src/fe_utils/Makefile
+++ b/src/fe_utils/Makefile
@@ -23,9 +23,13 @@ OBJS = \
 	archive.o \
 	cancel.o \
 	conditional.o \
+	connect_utils.o \
 	mbprint.o \
+	option_utils.o \
+	parallel_slot.o \
 	print.o \
 	psqlscan.o \
+	query_utils.o \
 	recovery_gen.o \
 	simple_list.o \
 	string_utils.o
diff --git a/src/fe_utils/connect_utils.c b/src/fe_utils/connect_utils.c
new file mode 100644
index 0000000000..96bb798316
--- /dev/null
+++ b/src/fe_utils/connect_utils.c
@@ -0,0 +1,181 @@
+/*-------------------------------------------------------------------------
+ *
+ * Facilities for frontend code to connect to and disconnect from databases.
+ *
+ * Portions Copyright (c) 1996-2021, 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 database+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_log_error("could not connect to database %s: out of memory",
+						 cparams->dbname);
+			exit(1);
+		}
+
+		/*
+		 * No luck?  Trying asking (again) for a password.
+		 */
+		if (PQstatus(conn) == CONNECTION_BAD &&
+			PQconnectionNeedsPassword(conn) &&
+			cparams->prompt_password != TRI_NO)
+		{
+			PQfinish(conn);
+			if (password)
+				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_log_error("%s", PQerrorMessage(conn));
+		exit(1);
+	}
+
+	/* 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);
+}
diff --git a/src/fe_utils/option_utils.c b/src/fe_utils/option_utils.c
new file mode 100644
index 0000000000..e19a495dba
--- /dev/null
+++ b/src/fe_utils/option_utils.c
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * Command line option processing facilities for frontend code
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/fe_utils/option_utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "fe_utils/option_utils.h"
+
+/*
+ * Provide strictly harmonized handling of --help and --version
+ * options.
+ */
+void
+handle_help_version_opts(int argc, char *argv[],
+						 const char *fixed_progname, help_handler hlp)
+{
+	if (argc > 1)
+	{
+		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
+		{
+			hlp(get_progname(argv[0]));
+			exit(0);
+		}
+		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
+		{
+			printf("%s (PostgreSQL) " PG_VERSION "\n", fixed_progname);
+			exit(0);
+		}
+	}
+}
diff --git a/src/bin/scripts/scripts_parallel.c b/src/fe_utils/parallel_slot.c
similarity index 80%
rename from src/bin/scripts/scripts_parallel.c
rename to src/fe_utils/parallel_slot.c
index 1f863a1bb4..3987a4702b 100644
--- a/src/bin/scripts/scripts_parallel.c
+++ b/src/fe_utils/parallel_slot.c
@@ -1,13 +1,13 @@
 /*-------------------------------------------------------------------------
  *
- *	scripts_parallel.c
- *		Parallel support for bin/scripts/
+ *	parallel_slot.c
+ *		Parallel support for front-end parallel database connections
  *
  *
  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * src/bin/scripts/scripts_parallel.c
+ * src/fe_utils/parallel_slot.c
  *
  *-------------------------------------------------------------------------
  */
@@ -22,13 +22,15 @@
 #include <sys/select.h>
 #endif
 
-#include "common.h"
 #include "common/logging.h"
 #include "fe_utils/cancel.h"
-#include "scripts_parallel.h"
+#include "fe_utils/parallel_slot.h"
+
+#define ERRCODE_UNDEFINED_TABLE  "42P01"
 
 static void init_slot(ParallelSlot *slot, PGconn *conn);
 static int	select_loop(int maxFd, fd_set *workerset);
+static bool processQueryResult(PGconn *conn, PGresult *result);
 
 static void
 init_slot(ParallelSlot *slot, PGconn *conn)
@@ -38,6 +40,57 @@ init_slot(ParallelSlot *slot, PGconn *conn)
 	slot->isFree = true;
 }
 
+/*
+ * Process (and delete) a query result.  Returns true if there's no error,
+ * false otherwise -- but errors about trying to work on a missing relation
+ * are reported and subsequently ignored.
+ */
+static bool
+processQueryResult(PGconn *conn, PGresult *result)
+{
+	/*
+	 * If it's an error, report it.  Errors about a missing table are harmless
+	 * so we continue processing; but die for other errors.
+	 */
+	if (PQresultStatus(result) != PGRES_COMMAND_OK)
+	{
+		char	   *sqlState = PQresultErrorField(result, PG_DIAG_SQLSTATE);
+
+		pg_log_error("processing of database \"%s\" failed: %s",
+					 PQdb(conn), PQerrorMessage(conn));
+
+		if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) != 0)
+		{
+			PQclear(result);
+			return false;
+		}
+	}
+
+	PQclear(result);
+	return true;
+}
+
+/*
+ * Consume all the results generated for the given connection until
+ * nothing remains.  If at least one error is encountered, return false.
+ * Note that this will block if the connection is busy.
+ */
+static bool
+consumeQueryResult(PGconn *conn)
+{
+	bool		ok = true;
+	PGresult   *result;
+
+	SetCancelConn(conn);
+	while ((result = PQgetResult(conn)) != NULL)
+	{
+		if (!processQueryResult(conn, result))
+			ok = false;
+	}
+	ResetCancelConn();
+	return ok;
+}
+
 /*
  * Wait until a file descriptor from the given set becomes readable.
  *
diff --git a/src/fe_utils/query_utils.c b/src/fe_utils/query_utils.c
new file mode 100644
index 0000000000..d5ffe56fd6
--- /dev/null
+++ b/src/fe_utils/query_utils.c
@@ -0,0 +1,92 @@
+/*-------------------------------------------------------------------------
+ *
+ * Facilities for frontend code to query a databases.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/fe_utils/query_utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include "common/logging.h"
+#include "fe_utils/cancel.h"
+#include "fe_utils/query_utils.h"
+
+/*
+ * Run a query, return the results, exit program on failure.
+ */
+PGresult *
+executeQuery(PGconn *conn, const char *query, bool echo)
+{
+	PGresult   *res;
+
+	if (echo)
+		printf("%s\n", query);
+
+	res = PQexec(conn, query);
+	if (!res ||
+		PQresultStatus(res) != PGRES_TUPLES_OK)
+	{
+		pg_log_error("query failed: %s", PQerrorMessage(conn));
+		pg_log_info("query was: %s", query);
+		PQfinish(conn);
+		exit(1);
+	}
+
+	return res;
+}
+
+
+/*
+ * As above for a SQL command (which returns nothing).
+ */
+void
+executeCommand(PGconn *conn, const char *query, bool echo)
+{
+	PGresult   *res;
+
+	if (echo)
+		printf("%s\n", query);
+
+	res = PQexec(conn, query);
+	if (!res ||
+		PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		pg_log_error("query failed: %s", PQerrorMessage(conn));
+		pg_log_info("query was: %s", query);
+		PQfinish(conn);
+		exit(1);
+	}
+
+	PQclear(res);
+}
+
+
+/*
+ * As above for a SQL maintenance command (returns command success).
+ * Command is executed with a cancel handler set, so Ctrl-C can
+ * interrupt it.
+ */
+bool
+executeMaintenanceCommand(PGconn *conn, const char *query, bool echo)
+{
+	PGresult   *res;
+	bool		r;
+
+	if (echo)
+		printf("%s\n", query);
+
+	SetCancelConn(conn);
+	res = PQexec(conn, query);
+	ResetCancelConn();
+
+	r = (res && PQresultStatus(res) == PGRES_COMMAND_OK);
+
+	if (res)
+		PQclear(res);
+
+	return r;
+}
diff --git a/src/include/fe_utils/connect_utils.h b/src/include/fe_utils/connect_utils.h
new file mode 100644
index 0000000000..5048940509
--- /dev/null
+++ b/src/include/fe_utils/connect_utils.h
@@ -0,0 +1,48 @@
+/*-------------------------------------------------------------------------
+ *
+ * Facilities for frontend code to connect to and disconnect from databases.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/fe_utils/connect_utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CONNECT_UTILS_H
+#define CONNECT_UTILS_H
+
+#include "libpq-fe.h"
+
+enum trivalue
+{
+	TRI_DEFAULT,
+	TRI_NO,
+	TRI_YES
+};
+
+/* Parameters needed by connectDatabase/connectMaintenanceDatabase */
+typedef struct _connParams
+{
+	/* These fields record the actual command line parameters */
+	const char *dbname;			/* this may be a connstring! */
+	const char *pghost;
+	const char *pgport;
+	const char *pguser;
+	enum trivalue prompt_password;
+	/* If not NULL, this overrides the dbname obtained from command line */
+	/* (but *only* the DB name, not anything else in the connstring) */
+	const char *override_dbname;
+} ConnParams;
+
+extern PGconn *connectDatabase(const ConnParams *cparams,
+							   const char *progname,
+							   bool echo, bool fail_ok,
+							   bool allow_password_reuse);
+
+extern PGconn *connectMaintenanceDatabase(ConnParams *cparams,
+										  const char *progname, bool echo);
+
+extern void disconnectDatabase(PGconn *conn);
+
+#endif							/* CONNECT_UTILS_H */
diff --git a/src/include/fe_utils/option_utils.h b/src/include/fe_utils/option_utils.h
new file mode 100644
index 0000000000..d653cb94e3
--- /dev/null
+++ b/src/include/fe_utils/option_utils.h
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * Command line option processing facilities for frontend code
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/fe_utils/option_utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef OPTION_UTILS_H
+#define OPTION_UTILS_H
+
+#include "postgres_fe.h"
+
+typedef void (*help_handler) (const char *progname);
+
+extern void handle_help_version_opts(int argc, char *argv[],
+									 const char *fixed_progname,
+									 help_handler hlp);
+
+#endif							/* OPTION_UTILS_H */
diff --git a/src/bin/scripts/scripts_parallel.h b/src/include/fe_utils/parallel_slot.h
similarity index 82%
rename from src/bin/scripts/scripts_parallel.h
rename to src/include/fe_utils/parallel_slot.h
index f62692510a..99eeb3328d 100644
--- a/src/bin/scripts/scripts_parallel.h
+++ b/src/include/fe_utils/parallel_slot.h
@@ -1,21 +1,20 @@
 /*-------------------------------------------------------------------------
  *
- *	scripts_parallel.h
+ *	parallel_slot.h
  *		Parallel support for bin/scripts/
  *
  *	Copyright (c) 2003-2021, PostgreSQL Global Development Group
  *
- *	src/bin/scripts/scripts_parallel.h
+ *	src/include/fe_utils/parallel_slot.h
  *
  *-------------------------------------------------------------------------
  */
-#ifndef SCRIPTS_PARALLEL_H
-#define SCRIPTS_PARALLEL_H
+#ifndef PARALLEL_SLOT_H
+#define PARALLEL_SLOT_H
 
-#include "common.h"
+#include "fe_utils/connect_utils.h"
 #include "libpq-fe.h"
 
-
 typedef struct ParallelSlot
 {
 	PGconn	   *connection;		/* One connection */
@@ -33,4 +32,4 @@ extern void ParallelSlotsTerminate(ParallelSlot *slots, int numslots);
 extern bool ParallelSlotsWaitCompletion(ParallelSlot *slots, int numslots);
 
 
-#endif							/* SCRIPTS_PARALLEL_H */
+#endif							/* PARALLEL_SLOT_H */
diff --git a/src/include/fe_utils/query_utils.h b/src/include/fe_utils/query_utils.h
new file mode 100644
index 0000000000..1099260193
--- /dev/null
+++ b/src/include/fe_utils/query_utils.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * Facilities for frontend code to query a databases.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/fe_utils/query_utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef QUERY_UTILS_H
+#define QUERY_UTILS_H
+
+#include "postgres_fe.h"
+
+#include "libpq-fe.h"
+
+extern PGresult *executeQuery(PGconn *conn, const char *query, bool echo);
+
+extern void executeCommand(PGconn *conn, const char *query, bool echo);
+
+extern bool executeMaintenanceCommand(PGconn *conn, const char *query,
+									  bool echo);
+
+#endif							/* QUERY_UTILS_H */
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 90328db04e..7be6e6c9e5 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -147,8 +147,8 @@ sub mkvcbuild
 	our @pgcommonbkndfiles = @pgcommonallfiles;
 
 	our @pgfeutilsfiles = qw(
-	  archive.c cancel.c conditional.c mbprint.c print.c psqlscan.l
-	  psqlscan.c simple_list.c string_utils.c recovery_gen.c);
+	  archive.c cancel.c conditional.c connect_utils.c mbprint.c option_utils.c parallel_slot.c print.c psqlscan.l
+	  psqlscan.c query_utils.c simple_list.c string_utils.c recovery_gen.c);
 
 	$libpgport = $solution->AddProject('libpgport', 'lib', 'misc');
 	$libpgport->AddDefine('FRONTEND');
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 1d540fe489..4d0d09a5dd 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -403,6 +403,7 @@ ConfigData
 ConfigVariable
 ConnCacheEntry
 ConnCacheKey
+ConnParams
 ConnStatusType
 ConnType
 ConnectionStateEnum
-- 
2.21.1 (Apple Git-122.3)

