Subject: Re: Non-text mode for pg_dumpall

From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 5 Mar 2025 06:42:02 +0530

On Wed, 5 Mar 2025 at 01:02, Álvaro Herrera <[email-masked]> wrote:
>
> A database name containing a newline breaks things for this patch:
>
> CREATE DATABASE "foo
> bar";
>
>
> $ pg_dumpall -Fc --file test
> shell command argument contains a newline or carriage return: "
dbname='foo
> bar'"
>
> --
> Álvaro Herrera               48°01'N 7°57'E  —
https://www.EnterpriseDB.com/

Hi Alvaro,
I also reported this issue on 29-01-2025. This breaks even without this
patch also.

error with pg_dumpall when db name have new line in double quote
<https://www.postgresql.org/message-id/flat/CAFC%2Bb6qwc%2Bwpt7_b2R6YhpDkrXeFvFd5NoLbTMMoxX9tfOHjpg%40mail.gmail.com#6423324382ff7a0537b2fe6231eb1e09>

-- 
Thanks and Regards
Mahendra Singh Thalor
EnterpriseDB: http://www.enterprisedb.com

---

From: Álvaro Herrera <[email-masked]>
Date: Wed, 5 Mar 2025 16:12:14 +0100

Disclaimer: I didn't review these patches fully.

> I also reported this issue on 29-01-2025. This breaks even without this

Okay, we should probably fix that, but I think the new map.dat file your
patch adds is going to make the problem worse, because it doesn't look
like you handled that case in any particular way that would make it not
fail.  I think it would be good to avoid digging us up even deeper in
that hole.  More generally, the pg_upgrade tests contain some code to
try database names with almost all possible ascii characters (see
generate_db in pg_upgrade/t/002_pg_upgrade.pl); it would be good to
ensure that this new functionality also works correctly for that --
perhaps add an equivalent test to the pg_dumpall test suite.

Looking at 0001:

I'm not sure that the whole common_dumpall_restore.c thing is properly
structured.  First, the file name shouldn't presume which programs
exactly are going to use the funcionality there.  Second, it looks like
there's another PQconnectdbParams() in pg_backup_db.c and I don't
understand what the reason is for that one to be separate.  In my mind,
there should be a file maybe called connection.c or connectdb.c or
whatever that's in charge of establishing connection for all the
src/bin/pg_dump programs, for cleanliness sake.  (This is probably also
the place where to put an on_exit callback that cleans up any leftover
connections.)

Looking at 0002 I see it mentions looking at the EXIT_NICELY macro for
documentation.  No such macro exists.  But also I think the addition
(and use) of reset_exit_nicely_list() is not a good idea.  It seems to
assume that the only entries in that list are ones that can be cleared
and reinstated whenever.  This makes too much of an assumption about how
the program works.  It may work today, but it'll get in the way of any
other patch that wants to set up some different on-exit clean up.  In
other words, we shouldn't reset the on_exit list at all.  Also, this is
just a weird addition:

#define exit_nicely(code) exit(code)

You added "A" as an option to the getopt_long() call in pg_restore, but
no handling for it is added.

I think the --globals-only option to pg_restore should be a separate
commit.

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 5 Mar 2025 22:18:57 +0530

Thanks Alvaro for feedback and review.

> perhaps add an equivalent test to the pg_dumpall test suite.

In the attached patch, I tried to solve the problem of the map.dat
file. I will do more analysis based on dbnames in 002_pg_upgrade.pl
file.

> the place where to put an on_exit callback that cleans up any leftover

Okay. I will do these changes.

> other words, we shouldn't reset the on_exit list at all.  Also, this is

I will do more study for this case and will update here.

> #define exit_nicely(code) exit(code)

Okay. I will fix this.

> no handling for it is added.

Fixed.

> I think the --globals-only option to pg_restore should be a separate

Okay.

Here, I am attaching updated patches for review and testing.

---

From: jian he <[email-masked]>
Date: Mon, 10 Mar 2025 15:54:45 +0800

> file. I will do more analysis based on dbnames in 002_pg_upgrade.pl

hi.

/*
 * Append the given string to the shell command being built in the buffer,
 * with shell-style quoting as needed to create exactly one argument.
 *
 * Forbid LF or CR characters, which have scant practical use beyond designing
 * security breaches.  The Windows command shell is unusable as a conduit for
 * arguments containing LF or CR characters.  A future major release should
 * reject those characters in CREATE ROLE and CREATE DATABASE, because use
 * there eventually leads to errors here.
 *
 * appendShellString() simply prints an error and dies if LF or CR appears.
 * appendShellStringNoError() omits those characters from the result, and
 * returns false if there were any.
 */
void
appendShellString(PQExpBuffer buf, const char *str)

per above comments,
we need to disallow LF/CR in database name and role name when issuing
shell command.

rolename LF/CR issue already being handled in
src/bin/pg_dump/pg_dumpall.c: while(getopt_long) code:
            case 3:
                use_role = pg_strdup(optarg);
                appendPQExpBufferStr(pgdumpopts, " --role ");
                appendShellString(pgdumpopts, use_role);

we can fail earlier also for database names in dumpDatabases, right
after executeQuery.
Please check attached, which is based on *v20*.

in V21, src/bin/pg_dump/pg_dumpall.c:
+#include "common_dumpall_restore.h"
happened within v21-0001 and v21-0002, it is being included twice.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Tue, 11 Mar 2025 19:41:04 +0530

Thanks Alvaro and Jian for the review and feedback.

> perhaps add an equivalent test to the pg_dumpall test suite.

As Jian also pointed out, we should not allow \n\r in dbnames. I am
keeping dbanames as single line names only.

I am doing testing using the pg_upgrade/t/002_pg_upgrade.pl file to
check different-2 dbnames.

> the place where to put an on_exit callback that cleans up any leftover

I did some more refactoring and made a connectdb.c file.

> other words, we shouldn't reset the on_exit list at all.  Also, this is

Based on some discussions, I added handling for cleanup. for 1st
database, I am saving index of array and then I am using same index
for rest of the databases as we are closing archive file in
CloseArchive so we can use same index for next database.

Fixed.

Fixed.

> I think the --globals-only option to pg_restore should be a separate
I will make this in the next version.

---

From: Álvaro Herrera <[email-masked]>
Date: Tue, 11 Mar 2025 15:42:53 +0100

> > fail.

Ehm, did you get consensus on adding such a restriction?

-- 
Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Tue, 11 Mar 2025 21:11:07 +0530

Hi Alvaro,

In map.dat file, I tried to fix this issue by adding number of characters
in dbname but as per code comments, as of now, we are not supporting \n\r
in dbnames so i removed handling.
I will do some more study to fix this issue.

/*
> appendShellString(PQExpBuffer buf, const char *str)

Sorry, in the v22 patches, I missed to use the "git add connectdb.c" file.
(Thanks Andrew for reporting this offline)

---

From: Álvaro Herrera <[email-masked]>
Date: Tue, 11 Mar 2025 18:05:59 +0100

Hello,

> I will do some more study to fix this issue.

Yeah, I think this is saying that you should not consider the contents
of map.dat as a shell string.  After all, you're not going to _execute_
that file via the shell.

Maybe for map.dat you need to escape such characters somehow, so that
they don't appear as literal newlines/carriage returns.

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/

---

From: Dagfinn Ilmari Mannsåker <[email-masked]>
Date: Tue, 11 Mar 2025 17:52:17 +0000

> they don't appear as literal newlines/carriage returns.

I haven't looked at the code for this, but why are we inventing an
ad-hoc file format?  Why not use JSON, like we do for backup manifests?
Then storing arbitrary database names won't be a problem.

- ilmari

---

From: Andrew Dunstan <[email-masked]>
Date: Tue, 11 Mar 2025 16:16:26 -0400

> Then storing arbitrary database names won't be a problem.

I'm not sure everyone thinks that was a good idea for backup manifests 
(in fact I know some don't), and it seems somewhat like overkill for a 
simple map of oids to database names.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com

---

From: Álvaro Herrera <[email-masked]>
Date: Tue, 11 Mar 2025 22:03:09 +0100

On 2025-Mar-11, Andrew Dunstan wrote:

> I'm not sure everyone thinks that was a good idea for backup manifests (in
> fact I know some don't), and it seems somewhat like overkill for a simple
> map of oids to database names.

If such a simple system can be made to work for all possible valid
database names, then I agree with you.  But if it forces us to restrict
database names to not contain newlines or other funny chars that are so
far unrestricted, then I would take the other position.

-- 
Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/
"La victoria es para quien se atreve a estar solo"

---

From: Andrew Dunstan <[email-masked]>
Date: Tue, 11 Mar 2025 18:37:14 -0400

> far unrestricted, then I would take the other position.

Well, JSON is supposed to be UTF8. What should we do about database 
names that are not UTF8?

It's kinda tempting to say we should have the file consist of lines like:

     oid base64_encoded_name escaped_human_readable name

cheers

andrew

---

From: Isaac Morland <[email-masked]>
Date: Tue, 11 Mar 2025 19:14:30 -0400

On Tue, 11 Mar 2025 at 18:37, Andrew Dunstan <[email-masked]> wrote:

> names that are not UTF8?

How can you have a database name that isn't encodeable in UTF-8? At this
point I'm pretty sure Unicode has subsumed essentially every character ever
mentioned in a standards document.

---

From: jian he <[email-masked]>
Date: Wed, 12 Mar 2025 15:03:37 +0800

> they don't appear as literal newlines/carriage returns.

I am confused.
currently pg_dumpall plain format will abort when encountering dbname
containing newline.
the left dumped plain file does not contain all the cluster databases data.

if pg_dumpall non-text format aborts earlier,
it's aligned with pg_dumpall plain format?
it's also an improvement since aborts earlier, nothing will be dumped?

am i missing something?

---

From: Laurenz Albe <[email-masked]>
Date: Wed, 12 Mar 2025 08:24:27 +0100

On Tue, 2025-03-11 at 19:14 -0400, Isaac Morland wrote:
> How can you have a database name that isn't encodeable in UTF-8? At this point
> I'm pretty sure Unicode has subsumed essentially every character ever mentioned
> in a standards document.

There is a difference between "encodable" and "encoded".  You'd have to figure
out the actual encoding of the database name and convert that to UTF-8.

Yours,
Laurenz Albe

---

From: Andrew Dunstan <[email-masked]>
Date: Wed, 12 Mar 2025 11:48:45 -0400

> it's also an improvement since aborts earlier, nothing will be dumped?

I think we should fix that.

But for the current proposal, Álvaro and I were talking this morning, 
and we thought the simplest thing here would be to have the one line 
format and escape NL/CRs in the database name.

cheers

andrew

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 19 Mar 2025 12:11:09 +0530

> format and escape NL/CRs in the database name.
Okay. As per discussions, we will keep one line entry for each
database into map.file.

Thanks all for feedback and review.

Here, I am attaching updated patches for review and testing. These
patches can be applied on commit a6524105d20b.

---

From: Andrew Dunstan <[email-masked]>
Date: Thu, 27 Mar 2025 17:15:57 -0400

> patches can be applied on commit a6524105d20b.

I'm working through this patch set with a view to committing it. 
Attached is some cleanup which is where I got to today, although there 
is more to do. One thing I am wondering is why not put the 
SimpleDatabaseOidList stuff in fe_utils/simle_list.{c,h} ? That's where 
all the similar stuff belongs, and it feels strange to have this inline 
in pg_restore.c. (I also don't like the name much - SimpleOidStringList 
or maybe SimpleOidPlusStringList might be better).

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index a3dcc585ace..6aab1bfe831 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -434,13 +434,13 @@ main(int argc, char *argv[])
 	archDumpFormat = parseDumpFormat(formatName);
 
 	/*
-	 * If non-plain format is specified then we must provide the
-	 * file name to create one main directory.
+	 * If a non-plain format is specified, a file name is also required as the
+	 * path to the main directory.
 	 */
 	if (archDumpFormat != archNull &&
 			(!filename || strcmp(filename, "") == 0))
 	{
-		pg_log_error("options -F/--format=d|c|t requires option -f/--file with non-empty string");
+		pg_log_error("option -F/--format=d|c|t requires option -f/--file");
 		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit_nicely(1);
 	}
@@ -513,14 +513,14 @@ main(int argc, char *argv[])
 	 */
 	if (archDumpFormat != archNull)
 	{
-		char	toc_path[MAXPGPATH];
+		char	global_path[MAXPGPATH];
 
 		/* Create new directory or accept the empty existing directory. */
 		create_or_open_dir(filename);
 
-		snprintf(toc_path, MAXPGPATH, "%s/global.dat", filename);
+		snprintf(global_path, MAXPGPATH, "%s/global.dat", filename);
 
-		OPF = fopen(toc_path, PG_BINARY_W);
+		OPF = fopen(global_path, PG_BINARY_W);
 		if (!OPF)
 			pg_fatal("could not open global.dat file: %s", strerror(errno));
 	}
@@ -1680,7 +1680,7 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 		}
 
 		/*
-		 * If this is non-plain dump format, then append dboid and dbname to
+		 * If this is not a plain format dump, then append dboid and dbname to
 		 * the map.dat file.
 		 */
 		if (archDumpFormat != archNull)
@@ -1688,7 +1688,7 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 			snprintf(dbfilepath, MAXPGPATH, "\"%s\"/\"%s\"", db_subdir, oid);
 
 			/* Put one line entry for dboid and dbname in map file. */
-			fprintf(map_file, "%s %s\n", oid, pg_strdup(dbname));
+			fprintf(map_file, "%s %s\n", oid, dbname);
 		}
 
 		pg_log_info("dumping database \"%s\"", dbname);
@@ -1734,17 +1734,17 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 
 		if (filename)
 		{
-			char	toc_path[MAXPGPATH];
+			char	global_path[MAXPGPATH];
 
 			if (archDumpFormat != archNull)
-				snprintf(toc_path, MAXPGPATH, "%s/global.dat", filename);
+				snprintf(global_path, MAXPGPATH, "%s/global.dat", filename);
 			else
-				snprintf(toc_path, MAXPGPATH, "%s", filename);
+				snprintf(global_path, MAXPGPATH, "%s", filename);
 
-			OPF = fopen(toc_path, PG_BINARY_A);
+			OPF = fopen(global_path, PG_BINARY_A);
 			if (!OPF)
 				pg_fatal("could not re-open the output file \"%s\": %m",
-						 toc_path);
+						 global_path);
 		}
 	}
 
@@ -1772,7 +1772,7 @@ runPgDump(const char *dbname, const char *create_opts, char *dbfile,
 	initPQExpBuffer(&cmd);
 
 	/*
-	 * If this is non-plain format dump, then append file name and dump
+	 * If this is not a plain format dump, then append file name and dump
 	 * format to the pg_dump command to get archive dump.
 	 */
 	if (archDumpFormat != archNull)
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index e4093427e2f..91602a2e37b 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -46,8 +46,6 @@
 #include <termios.h>
 #endif
 
-#include "common/connect.h"
-#include "compress_io.h"
 #include "common/string.h"
 #include "connectdb.h"
 #include "fe_utils/option_utils.h"
@@ -55,7 +53,6 @@
 #include "filter.h"
 #include "getopt_long.h"
 #include "parallel.h"
-#include "pg_backup_archiver.h"
 #include "pg_backup_utils.h"
 
 typedef struct SimpleDatabaseOidListCell
@@ -73,10 +70,10 @@ typedef struct SimpleDatabaseOidList
 
 static void usage(const char *progname);
 static void read_restore_filters(const char *filename, RestoreOptions *opts);
-static bool IsFileExistsInDirectory(const char *dir, const char *filename);
+static bool file_exists_in_directory(const char *dir, const char *filename);
 static int restoreOneDatabase(const char *inputFileSpec, RestoreOptions *opts,
 							  int numWorkers, bool append_data, int num);
-static int ReadOneStatement(StringInfo inBuf, FILE *pfile);
+static int read_one_statement(StringInfo inBuf, FILE *pfile);
 static int restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
 							   SimpleStringList db_exclude_patterns, RestoreOptions *opts, int numWorkers);
 static int process_global_sql_commands(PGconn *conn, const char *dumpdirpath,
@@ -89,7 +86,6 @@ static int get_dbname_oid_list_from_mfile(const char *dumpdirpath,
 										  SimpleDatabaseOidList *dbname_oid_list);
 static void simple_db_oid_list_append(SimpleDatabaseOidList *list, Oid db_oid,
 									  const char *dbname);
-static void simple_string_full_list_delete(SimpleStringList *list);
 static void simple_db_oid_full_list_delete(SimpleDatabaseOidList *list);
 static void simple_db_oid_list_delete(SimpleDatabaseOidList *list,
 									  SimpleDatabaseOidListCell *cell,
@@ -521,8 +517,8 @@ main(int argc, char **argv)
 	 * databases from map.dat(if exist) file list and skip restoring for
 	 * --exclude-database patterns.
 	 */
-	if (inputFileSpec != NULL && !IsFileExistsInDirectory(inputFileSpec, "toc.dat") &&
-			IsFileExistsInDirectory(inputFileSpec, "global.dat"))
+	if (inputFileSpec != NULL && !file_exists_in_directory(inputFileSpec, "toc.dat") &&
+			file_exists_in_directory(inputFileSpec, "global.dat"))
 	{
 		PGconn  *conn = NULL; /* Connection to restore global sql commands. */
 
@@ -578,7 +574,7 @@ main(int argc, char **argv)
 		}
 
 		/* Free db pattern list. */
-		simple_string_full_list_delete(&db_exclude_patterns);
+		simple_string_list_destroy(&db_exclude_patterns);
 	}
 	else /* process if global.dat file does not exist. */
 	{
@@ -847,12 +843,12 @@ read_restore_filters(const char *filename, RestoreOptions *opts)
 }
 
 /*
- * IsFileExistsInDirectory
+ * file_exists_in_directory
  *
  * Returns true if file exist in current directory.
  */
 static bool
-IsFileExistsInDirectory(const char *dir, const char *filename)
+file_exists_in_directory(const char *dir, const char *filename)
 {
 	struct stat			st;
 	char				buf[MAXPGPATH];
@@ -864,7 +860,7 @@ IsFileExistsInDirectory(const char *dir, const char *filename)
 }
 
 /*
- * ReadOneStatement
+ * read_one_statement
  *
  * This will start reading from passed file pointer using fgetc and read till
  * semicolon(sql statement terminator for global.dat file)
@@ -873,7 +869,7 @@ IsFileExistsInDirectory(const char *dir, const char *filename)
  */
 
 static int
-ReadOneStatement(StringInfo inBuf, FILE *pfile)
+read_one_statement(StringInfo inBuf, FILE *pfile)
 {
 	int			c; /* character read from getc() */
 	int			m;
@@ -1064,7 +1060,7 @@ get_dbname_oid_list_from_mfile(const char *dumpdirpath, SimpleDatabaseOidList *d
 	 * If there is only global.dat file in dump, then return from here as there
 	 * is no database to restore.
 	 */
-	if (!IsFileExistsInDirectory(pg_strdup(dumpdirpath), "map.dat"))
+	if (!file_exists_in_directory(dumpdirpath, "map.dat"))
 	{
 		pg_log_info("databases restoring is skipped as map.dat file is not present in \"%s\"", dumpdirpath);
 		return 0;
@@ -1281,7 +1277,7 @@ process_global_sql_commands(PGconn *conn, const char *dumpdirpath, const char *o
 	initStringInfo(&sqlstatement);
 
 	/* Process file till EOF and execute sql statements. */
-	while (ReadOneStatement(&sqlstatement, pfile) != EOF)
+	while (read_one_statement(&sqlstatement, pfile) != EOF)
 	{
 		pg_log_info("executing query: %s", sqlstatement.data);
 		result = PQexec(conn, sqlstatement.data);
@@ -1393,28 +1389,6 @@ simple_db_oid_full_list_delete(SimpleDatabaseOidList *list)
 	list->tail = NULL;
 }
 
-/*
- * simple_string_full_list_delete
- *
- * delete all cell from string list.
- */
-static void
-simple_string_full_list_delete(SimpleStringList *list)
-{
-	SimpleStringListCell	*cell = list->head;
-	SimpleStringListCell    *cellnext = NULL;
-
-	while (cell)
-	{
-		cellnext = cell->next;
-		pfree(cell);
-		cell = cellnext;
-	}
-
-	list->head = NULL;
-	list->tail = NULL;
-}
-
 /*
  * simple_db_oid_list_delete
  *

---

From: Andrew Dunstan <[email-masked]>
Date: Fri, 28 Mar 2025 18:20:42 -0400

On 2025-03-27 Th 5:15 PM, Andrew Dunstan wrote:
> SimpleDatabaseOidList stuff in fe_utils/simle_list.{c,h} ? That's 
> where all the similar stuff belongs, and it feels strange to have this 
> inline in pg_restore.c. (I also don't like the name much - 
> SimpleOidStringList or maybe SimpleOidPlusStringList might be better).
>
>
>

OK, I have done that, so here is the result. The first two are you 
original patches. patch 3 adds the new list type to fe-utils, and patch 
4 contains my cleanups and use of the new list type. Apart from some 
relatively minor cleanup, the one thing I would like to change is how 
dumps are named. If we are producing tar or custom format dumps, I think 
the file names should reflect that (oid.dmp and oid.tar rather than a 
bare oid as the filename), and pg_restore should look for those. I'm 
going to work on that tomorrow - I don't think it will be terribly 
difficult.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com
From 7105ed4a08b0c2b4d30e7b3eedb6c94882eb7421 Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 19 Mar 2025 01:18:46 +0530
Subject: [PATCH 1/4] move common code related to connection to new the file

ConnectDatabase is used by both pg_dumpall, pg_restore
and pg_dump so move common code to new file.

new file name: connectdb.c
---
 src/bin/pg_dump/Makefile             |   5 +-
 src/bin/pg_dump/connectdb.c          | 294 +++++++++++++++++++++++++++
 src/bin/pg_dump/connectdb.h          |  26 +++
 src/bin/pg_dump/meson.build          |   3 +
 src/bin/pg_dump/pg_backup.h          |   2 +-
 src/bin/pg_dump/pg_backup_archiver.c |   6 +-
 src/bin/pg_dump/pg_backup_db.c       |  75 +------
 src/bin/pg_dump/pg_dump.c            |   2 +-
 src/bin/pg_dump/pg_dumpall.c         | 278 +------------------------
 9 files changed, 350 insertions(+), 341 deletions(-)
 create mode 100644 src/bin/pg_dump/connectdb.c
 create mode 100644 src/bin/pg_dump/connectdb.h

diff --git a/src/bin/pg_dump/Makefile b/src/bin/pg_dump/Makefile
index 233ad15ca75..fa795883e9f 100644
--- a/src/bin/pg_dump/Makefile
+++ b/src/bin/pg_dump/Makefile
@@ -31,6 +31,7 @@ OBJS = \
 	compress_lz4.o \
 	compress_none.o \
 	compress_zstd.o \
+	connectdb.o \
 	dumputils.o \
 	filter.o \
 	parallel.o \
@@ -50,8 +51,8 @@ pg_dump: pg_dump.o common.o pg_dump_sort.o $(OBJS) | submake-libpq submake-libpg
 pg_restore: pg_restore.o $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils
 	$(CC) $(CFLAGS) pg_restore.o $(OBJS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
 
-pg_dumpall: pg_dumpall.o dumputils.o filter.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
-	$(CC) $(CFLAGS) pg_dumpall.o dumputils.o filter.o $(WIN32RES) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
+pg_dumpall: pg_dumpall.o $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils
+	$(CC) $(CFLAGS) pg_dumpall.o $(OBJS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
 
 install: all installdirs
 	$(INSTALL_PROGRAM) pg_dump$(X) '$(DESTDIR)$(bindir)'/pg_dump$(X)
diff --git a/src/bin/pg_dump/connectdb.c b/src/bin/pg_dump/connectdb.c
new file mode 100644
index 00000000000..3e1fbe98c25
--- /dev/null
+++ b/src/bin/pg_dump/connectdb.c
@@ -0,0 +1,294 @@
+/*-------------------------------------------------------------------------
+ *
+ * connectdb.c
+ *    This is a common file connection to the database.
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *    src/bin/pg_dump/connectdb.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "common/connect.h"
+#include "common/logging.h"
+#include "common/string.h"
+#include "connectdb.h"
+#include "dumputils.h"
+#include "fe_utils/string_utils.h"
+
+static char *constructConnStr(const char **keywords, const char **values);
+
+/*
+ * ConnectDatabase
+ *
+ * Make a database connection with the given parameters.  An
+ * interactive password prompt is automatically issued if required.
+ *
+ * If fail_on_error is false, we return NULL without printing any message
+ * on failure, but preserve any prompted password for the next try.
+ *
+ * On success, the 'connstr' is set to a connection string containing
+ * the options used and 'server_version' is set to version so that caller
+ * can use them.
+ */
+PGconn *
+ConnectDatabase(const char *dbname, const char *connection_string,
+				const char *pghost, const char *pgport, const char *pguser,
+				trivalue prompt_password, bool fail_on_error, const char *progname,
+				const char **connstr, int *server_version, char *password,
+				char *override_dbname)
+{
+	PGconn	   *conn;
+	bool		new_pass;
+	const char *remoteversion_str;
+	int			my_version;
+	const char **keywords = NULL;
+	const char **values = NULL;
+	PQconninfoOption *conn_opts = NULL;
+	int			server_version_temp;
+
+	if (prompt_password == TRI_YES && !password)
+		password = simple_prompt("Password: ", false);
+
+	/*
+	 * Start the connection.  Loop until we have a password if requested by
+	 * backend.
+	 */
+	do
+	{
+		int			argcount = 8;
+		PQconninfoOption *conn_opt;
+		char	   *err_msg = NULL;
+		int			i = 0;
+
+		free(keywords);
+		free(values);
+		PQconninfoFree(conn_opts);
+
+		/*
+		 * Merge the connection info inputs given in form of connection string
+		 * and other options.  Explicitly discard any dbname value in the
+		 * connection string; otherwise, PQconnectdbParams() would interpret
+		 * that value as being itself a connection string.
+		 */
+		if (connection_string)
+		{
+			conn_opts = PQconninfoParse(connection_string, &err_msg);
+			if (conn_opts == NULL)
+				pg_fatal("%s", err_msg);
+
+			for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
+			{
+				if (conn_opt->val != NULL && conn_opt->val[0] != '\0' &&
+					strcmp(conn_opt->keyword, "dbname") != 0)
+					argcount++;
+			}
+
+			keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
+			values = pg_malloc0((argcount + 1) * sizeof(*values));
+
+			for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
+			{
+				if (conn_opt->val != NULL && conn_opt->val[0] != '\0' &&
+					strcmp(conn_opt->keyword, "dbname") != 0)
+				{
+					keywords[i] = conn_opt->keyword;
+					values[i] = conn_opt->val;
+					i++;
+				}
+			}
+		}
+		else
+		{
+			keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
+			values = pg_malloc0((argcount + 1) * sizeof(*values));
+		}
+
+		if (pghost)
+		{
+			keywords[i] = "host";
+			values[i] = pghost;
+			i++;
+		}
+		if (pgport)
+		{
+			keywords[i] = "port";
+			values[i] = pgport;
+			i++;
+		}
+		if (pguser)
+		{
+			keywords[i] = "user";
+			values[i] = pguser;
+			i++;
+		}
+		if (password)
+		{
+			keywords[i] = "password";
+			values[i] = password;
+			i++;
+		}
+		if (dbname)
+		{
+			keywords[i] = "dbname";
+			values[i] = dbname;
+			i++;
+		}
+		if (override_dbname)
+		{
+			keywords[i] = "dbname";
+			values[i++] = override_dbname;
+		}
+
+		keywords[i] = "fallback_application_name";
+		values[i] = progname;
+		i++;
+
+		new_pass = false;
+		conn = PQconnectdbParams(keywords, values, true);
+
+		if (!conn)
+			pg_fatal("could not connect to database \"%s\"", dbname);
+
+		if (PQstatus(conn) == CONNECTION_BAD &&
+			PQconnectionNeedsPassword(conn) &&
+			!password &&
+			prompt_password != TRI_NO)
+		{
+			PQfinish(conn);
+			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_on_error)
+			pg_fatal("%s", PQerrorMessage(conn));
+		else
+		{
+			PQfinish(conn);
+
+			free(keywords);
+			free(values);
+			PQconninfoFree(conn_opts);
+
+			return NULL;
+		}
+	}
+
+	/*
+	 * Ok, connected successfully. If requested, remember the options used, in the
+	 * form of a connection string.
+	 */
+	if (connstr)
+		*connstr = constructConnStr(keywords, values);
+
+	free(keywords);
+	free(values);
+	PQconninfoFree(conn_opts);
+
+	/* Check version */
+	remoteversion_str = PQparameterStatus(conn, "server_version");
+	if (!remoteversion_str)
+		pg_fatal("could not get server version");
+
+	server_version_temp = PQserverVersion(conn);
+	if (server_version_temp == 0)
+		pg_fatal("could not parse server version \"%s\"",
+				 remoteversion_str);
+
+	/* If requested, then copy server version to out variable. */
+	if (server_version)
+		*server_version = server_version_temp;
+
+	my_version = PG_VERSION_NUM;
+
+	/*
+	 * We allow the server to be back to 9.2, and up to any minor release of
+	 * our own major version.  (See also version check in pg_dump.c.)
+	 */
+	if (my_version != server_version_temp
+		&& (server_version_temp < 90200 ||
+			(server_version_temp / 100) > (my_version / 100)))
+	{
+		pg_log_error("aborting because of server version mismatch");
+		pg_log_error_detail("server version: %s; %s version: %s",
+							remoteversion_str, progname, PG_VERSION);
+		exit_nicely(1);
+	}
+
+	PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL));
+
+	return conn;
+}
+
+/*
+ * constructConnStr
+ *
+ * Construct a connection string from the given keyword/value pairs. It is
+ * used to pass the connection options to the pg_dump subprocess.
+ *
+ * The following parameters are excluded:
+ *	dbname		- varies in each pg_dump invocation
+ *	password	- it's not secure to pass a password on the command line
+ *	fallback_application_name - we'll let pg_dump set it
+ */
+static char *
+constructConnStr(const char **keywords, const char **values)
+{
+	PQExpBuffer buf = createPQExpBuffer();
+	char	   *connstr;
+	int			i;
+	bool		firstkeyword = true;
+
+	/* Construct a new connection string in key='value' format. */
+	for (i = 0; keywords[i] != NULL; i++)
+	{
+		if (strcmp(keywords[i], "dbname") == 0 ||
+			strcmp(keywords[i], "password") == 0 ||
+			strcmp(keywords[i], "fallback_application_name") == 0)
+			continue;
+
+		if (!firstkeyword)
+			appendPQExpBufferChar(buf, ' ');
+		firstkeyword = false;
+		appendPQExpBuffer(buf, "%s=", keywords[i]);
+		appendConnStrVal(buf, values[i]);
+	}
+
+	connstr = pg_strdup(buf->data);
+	destroyPQExpBuffer(buf);
+	return connstr;
+}
+
+/*
+ * executeQuery
+ *
+ * Run a query, return the results, exit program on failure.
+ */
+PGresult *
+executeQuery(PGconn *conn, const char *query)
+{
+	PGresult   *res;
+
+	pg_log_info("executing %s", query);
+
+	res = PQexec(conn, query);
+	if (!res ||
+		PQresultStatus(res) != PGRES_TUPLES_OK)
+	{
+		pg_log_error("query failed: %s", PQerrorMessage(conn));
+		pg_log_error_detail("Query was: %s", query);
+		PQfinish(conn);
+		exit_nicely(1);
+	}
+
+	return res;
+}
diff --git a/src/bin/pg_dump/connectdb.h b/src/bin/pg_dump/connectdb.h
new file mode 100644
index 00000000000..9e1e7ef33d0
--- /dev/null
+++ b/src/bin/pg_dump/connectdb.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * connectdb.h
+ *      Common header file for connection to the database.
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *    src/bin/pg_dump/connectdb.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CONNECTDB_H
+#define CONNECTDB_H
+
+#include "pg_backup.h"
+#include "pg_backup_utils.h"
+
+extern PGconn *ConnectDatabase(const char *dbname, const char *connection_string, const char *pghost,
+							   const char *pgport, const char *pguser,
+							   trivalue prompt_password, bool fail_on_error,
+							   const char *progname, const char **connstr, int *server_version,
+							   char *password, char *override_dbname);
+extern  PGresult *executeQuery(PGconn *conn, const char *query);
+#endif                          /* CONNECTDB_H */
diff --git a/src/bin/pg_dump/meson.build b/src/bin/pg_dump/meson.build
index 603ba6cfbf0..9031737d013 100644
--- a/src/bin/pg_dump/meson.build
+++ b/src/bin/pg_dump/meson.build
@@ -6,6 +6,7 @@ pg_dump_common_sources = files(
   'compress_lz4.c',
   'compress_none.c',
   'compress_zstd.c',
+  'connectdb.c',
   'dumputils.c',
   'filter.c',
   'parallel.c',
@@ -48,6 +49,7 @@ bin_targets += pg_dump

 pg_dumpall_sources = files(
+  'connectdb.c',
   'pg_dumpall.c',
 )
 
@@ -67,6 +69,7 @@ bin_targets += pg_dumpall

 pg_restore_sources = files(
+  'connectdb.c',
   'pg_restore.c',
 )
 
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 658986de6f8..c68a21027fa 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -293,7 +293,7 @@ typedef void (*SetupWorkerPtrType) (Archive *AH);
  * Main archiver interface.
  */
 
-extern void ConnectDatabase(Archive *AHX,
+extern void ConnectDatabaseAhx(Archive *AHX,
 							const ConnParams *cparams,
 							bool isReconnect);
 extern void DisconnectDatabase(Archive *AHX);
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 82d51c89ac6..3fd2818223c 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -414,7 +414,7 @@ RestoreArchive(Archive *AHX)
 		AHX->minRemoteVersion = 0;
 		AHX->maxRemoteVersion = 9999999;
 
-		ConnectDatabase(AHX, &ropt->cparams, false);
+		ConnectDatabaseAhx(AHX, &ropt->cparams, false);
 
 		/*
 		 * If we're talking to the DB directly, don't send comments since they
@@ -4437,7 +4437,7 @@ restore_toc_entries_postfork(ArchiveHandle *AH, TocEntry *pending_list)
 	/*
 	 * Now reconnect the single parent connection.
 	 */
-	ConnectDatabase((Archive *) AH, &ropt->cparams, true);
+	ConnectDatabaseAhx((Archive *) AH, &ropt->cparams, true);
 
 	/* re-establish fixed state */
 	_doSetFixedOutputState(AH);
@@ -5054,7 +5054,7 @@ CloneArchive(ArchiveHandle *AH)
 	 * Connect our new clone object to the database, using the same connection
 	 * parameters used for the original connection.
 	 */
-	ConnectDatabase((Archive *) clone, &clone->public.ropt->cparams, true);
+	ConnectDatabaseAhx((Archive *) clone, &clone->public.ropt->cparams, true);
 
 	/* re-establish fixed state */
 	if (AH->mode == archModeRead)
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c
index 71c55d2466a..227dd963984 100644
--- a/src/bin/pg_dump/pg_backup_db.c
+++ b/src/bin/pg_dump/pg_backup_db.c
@@ -19,6 +19,7 @@
 
 #include "common/connect.h"
 #include "common/string.h"
+#include "connectdb.h"
 #include "parallel.h"
 #include "pg_backup_archiver.h"
 #include "pg_backup_db.h"
@@ -86,9 +87,9 @@ ReconnectToServer(ArchiveHandle *AH, const char *dbname)
 	 * ArchiveHandle's connCancel, before closing old connection.  Otherwise
 	 * an ill-timed SIGINT could try to access a dead connection.
 	 */
-	AH->connection = NULL;		/* dodge error check in ConnectDatabase */
+	AH->connection = NULL;		/* dodge error check in ConnectDatabaseAhx */
 
-	ConnectDatabase((Archive *) AH, &ropt->cparams, true);
+	ConnectDatabaseAhx((Archive *) AH, &ropt->cparams, true);
 
 	PQfinish(oldConn);
 }
@@ -105,14 +106,13 @@ ReconnectToServer(ArchiveHandle *AH, const char *dbname)
  * username never does change, so one savedPassword is sufficient.
  */
 void
-ConnectDatabase(Archive *AHX,
+ConnectDatabaseAhx(Archive *AHX,
 				const ConnParams *cparams,
 				bool isReconnect)
 {
 	ArchiveHandle *AH = (ArchiveHandle *) AHX;
 	trivalue	prompt_password;
 	char	   *password;
-	bool		new_pass;
 
 	if (AH->connection)
 		pg_fatal("already connected to a database");
@@ -125,69 +125,10 @@ ConnectDatabase(Archive *AHX,
 	if (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->username;
-		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;
-		AH->connection = PQconnectdbParams(keywords, values, true);
-
-		if (!AH->connection)
-			pg_fatal("could not connect to database");
-
-		if (PQstatus(AH->connection) == CONNECTION_BAD &&
-			PQconnectionNeedsPassword(AH->connection) &&
-			password == NULL &&
-			prompt_password != TRI_NO)
-		{
-			PQfinish(AH->connection);
-			password = simple_prompt("Password: ", false);
-			new_pass = true;
-		}
-	} while (new_pass);
-
-	/* check to see that the backend connection was successfully made */
-	if (PQstatus(AH->connection) == CONNECTION_BAD)
-	{
-		if (isReconnect)
-			pg_fatal("reconnection failed: %s",
-					 PQerrorMessage(AH->connection));
-		else
-			pg_fatal("%s",
-					 PQerrorMessage(AH->connection));
-	}
+	AH->connection = ConnectDatabase(cparams->dbname, NULL, cparams->pghost,
+			cparams->pgport, cparams->username,
+			prompt_password, true,
+			progname, NULL, NULL, password, cparams->override_dbname);
 
 	/* Start strict; later phases may override this. */
 	PQclear(ExecuteSqlQueryForSingleRow((Archive *) AH,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e41e645f649..015a434fc0b 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -966,7 +966,7 @@ main(int argc, char **argv)
 	 * Open the database using the Archiver, so it knows about it. Errors mean
 	 * death.
 	 */
-	ConnectDatabase(fout, &dopt.cparams, false);
+	ConnectDatabaseAhx(fout, &dopt.cparams, false);
 	setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
 
 	/*
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 2ea574b0f06..573a8b61a45 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -24,11 +24,11 @@
 #include "common/hashfn_unstable.h"
 #include "common/logging.h"
 #include "common/string.h"
+#include "connectdb.h"
 #include "dumputils.h"
 #include "fe_utils/string_utils.h"
 #include "filter.h"
 #include "getopt_long.h"
-#include "pg_backup.h"
 
 /* version string we expect back from pg_dump */
 #define PGDUMP_VERSIONSTR "pg_dump (PostgreSQL) " PG_VERSION "\n"
@@ -71,21 +71,14 @@ static void buildShSecLabels(PGconn *conn,
 							 const char *catalog_name, Oid objectId,
 							 const char *objtype, const char *objname,
 							 PQExpBuffer buffer);
-static PGconn *connectDatabase(const char *dbname,
-							   const char *connection_string, const char *pghost,
-							   const char *pgport, const char *pguser,
-							   trivalue prompt_password, bool fail_on_error);
-static char *constructConnStr(const char **keywords, const char **values);
-static PGresult *executeQuery(PGconn *conn, const char *query);
 static void executeCommand(PGconn *conn, const char *query);
 static void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns,
 								   SimpleStringList *names);
 static void read_dumpall_filters(const char *filename, SimpleStringList *pattern);
 
 static char pg_dump_bin[MAXPGPATH];
-static const char *progname;
 static PQExpBuffer pgdumpopts;
-static char *connstr = "";
+static const char *connstr = "";
 static bool output_clean = false;
 static bool skip_acls = false;
 static bool verbose = false;
@@ -129,8 +122,6 @@ static char *filename = NULL;
 static SimpleStringList database_exclude_patterns = {NULL, NULL};
 static SimpleStringList database_exclude_names = {NULL, NULL};
 
-#define exit_nicely(code) exit(code)
-
 int
 main(int argc, char *argv[])
 {
@@ -499,19 +490,22 @@ main(int argc, char *argv[])
 	 */
 	if (pgdb)
 	{
-		conn = connectDatabase(pgdb, connstr, pghost, pgport, pguser,
-							   prompt_password, false);
+		conn = ConnectDatabase(pgdb, connstr, pghost, pgport, pguser,
+							   prompt_password, false,
+							   progname, &connstr, &server_version, NULL, NULL);
 
 		if (!conn)
 			pg_fatal("could not connect to database \"%s\"", pgdb);
 	}
 	else
 	{
-		conn = connectDatabase("postgres", connstr, pghost, pgport, pguser,
-							   prompt_password, false);
+		conn = ConnectDatabase("postgres", connstr, pghost, pgport, pguser,
+							   prompt_password, false,
+							   progname, &connstr, &server_version, NULL, NULL);
 		if (!conn)
-			conn = connectDatabase("template1", connstr, pghost, pgport, pguser,
-								   prompt_password, true);
+			conn = ConnectDatabase("template1", connstr, pghost, pgport, pguser,
+								   prompt_password, true,
+								   progname, &connstr, &server_version, NULL, NULL);
 
 		if (!conn)
 		{
@@ -1738,256 +1732,6 @@ buildShSecLabels(PGconn *conn, const char *catalog_name, Oid objectId,
 	destroyPQExpBuffer(sql);
 }
 
-/*
- * Make a database connection with the given parameters.  An
- * interactive password prompt is automatically issued if required.
- *
- * If fail_on_error is false, we return NULL without printing any message
- * on failure, but preserve any prompted password for the next try.
- *
- * On success, the global variable 'connstr' is set to a connection string
- * containing the options used.
- */
-static PGconn *
-connectDatabase(const char *dbname, const char *connection_string,
-				const char *pghost, const char *pgport, const char *pguser,
-				trivalue prompt_password, bool fail_on_error)
-{
-	PGconn	   *conn;
-	bool		new_pass;
-	const char *remoteversion_str;
-	int			my_version;
-	const char **keywords = NULL;
-	const char **values = NULL;
-	PQconninfoOption *conn_opts = NULL;
-	static char *password = NULL;
-
-	if (prompt_password == TRI_YES && !password)
-		password = simple_prompt("Password: ", false);
-
-	/*
-	 * Start the connection.  Loop until we have a password if requested by
-	 * backend.
-	 */
-	do
-	{
-		int			argcount = 6;
-		PQconninfoOption *conn_opt;
-		char	   *err_msg = NULL;
-		int			i = 0;
-
-		free(keywords);
-		free(values);
-		PQconninfoFree(conn_opts);
-
-		/*
-		 * Merge the connection info inputs given in form of connection string
-		 * and other options.  Explicitly discard any dbname value in the
-		 * connection string; otherwise, PQconnectdbParams() would interpret
-		 * that value as being itself a connection string.
-		 */
-		if (connection_string)
-		{
-			conn_opts = PQconninfoParse(connection_string, &err_msg);
-			if (conn_opts == NULL)
-				pg_fatal("%s", err_msg);
-
-			for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
-			{
-				if (conn_opt->val != NULL && conn_opt->val[0] != '\0' &&
-					strcmp(conn_opt->keyword, "dbname") != 0)
-					argcount++;
-			}
-
-			keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
-			values = pg_malloc0((argcount + 1) * sizeof(*values));
-
-			for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
-			{
-				if (conn_opt->val != NULL && conn_opt->val[0] != '\0' &&
-					strcmp(conn_opt->keyword, "dbname") != 0)
-				{
-					keywords[i] = conn_opt->keyword;
-					values[i] = conn_opt->val;
-					i++;
-				}
-			}
-		}
-		else
-		{
-			keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
-			values = pg_malloc0((argcount + 1) * sizeof(*values));
-		}
-
-		if (pghost)
-		{
-			keywords[i] = "host";
-			values[i] = pghost;
-			i++;
-		}
-		if (pgport)
-		{
-			keywords[i] = "port";
-			values[i] = pgport;
-			i++;
-		}
-		if (pguser)
-		{
-			keywords[i] = "user";
-			values[i] = pguser;
-			i++;
-		}
-		if (password)
-		{
-			keywords[i] = "password";
-			values[i] = password;
-			i++;
-		}
-		if (dbname)
-		{
-			keywords[i] = "dbname";
-			values[i] = dbname;
-			i++;
-		}
-		keywords[i] = "fallback_application_name";
-		values[i] = progname;
-		i++;
-
-		new_pass = false;
-		conn = PQconnectdbParams(keywords, values, true);
-
-		if (!conn)
-			pg_fatal("could not connect to database \"%s\"", dbname);
-
-		if (PQstatus(conn) == CONNECTION_BAD &&
-			PQconnectionNeedsPassword(conn) &&
-			!password &&
-			prompt_password != TRI_NO)
-		{
-			PQfinish(conn);
-			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_on_error)
-			pg_fatal("%s", PQerrorMessage(conn));
-		else
-		{
-			PQfinish(conn);
-
-			free(keywords);
-			free(values);
-			PQconninfoFree(conn_opts);
-
-			return NULL;
-		}
-	}
-
-	/*
-	 * Ok, connected successfully. Remember the options used, in the form of a
-	 * connection string.
-	 */
-	connstr = constructConnStr(keywords, values);
-
-	free(keywords);
-	free(values);
-	PQconninfoFree(conn_opts);
-
-	/* Check version */
-	remoteversion_str = PQparameterStatus(conn, "server_version");
-	if (!remoteversion_str)
-		pg_fatal("could not get server version");
-	server_version = PQserverVersion(conn);
-	if (server_version == 0)
-		pg_fatal("could not parse server version \"%s\"",
-				 remoteversion_str);
-
-	my_version = PG_VERSION_NUM;
-
-	/*
-	 * We allow the server to be back to 9.2, and up to any minor release of
-	 * our own major version.  (See also version check in pg_dump.c.)
-	 */
-	if (my_version != server_version
-		&& (server_version < 90200 ||
-			(server_version / 100) > (my_version / 100)))
-	{
-		pg_log_error("aborting because of server version mismatch");
-		pg_log_error_detail("server version: %s; %s version: %s",
-							remoteversion_str, progname, PG_VERSION);
-		exit_nicely(1);
-	}
-
-	PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL));
-
-	return conn;
-}
-
-/* ----------
- * Construct a connection string from the given keyword/value pairs. It is
- * used to pass the connection options to the pg_dump subprocess.
- *
- * The following parameters are excluded:
- *	dbname		- varies in each pg_dump invocation
- *	password	- it's not secure to pass a password on the command line
- *	fallback_application_name - we'll let pg_dump set it
- * ----------
- */
-static char *
-constructConnStr(const char **keywords, const char **values)
-{
-	PQExpBuffer buf = createPQExpBuffer();
-	char	   *connstr;
-	int			i;
-	bool		firstkeyword = true;
-
-	/* Construct a new connection string in key='value' format. */
-	for (i = 0; keywords[i] != NULL; i++)
-	{
-		if (strcmp(keywords[i], "dbname") == 0 ||
-			strcmp(keywords[i], "password") == 0 ||
-			strcmp(keywords[i], "fallback_application_name") == 0)
-			continue;
-
-		if (!firstkeyword)
-			appendPQExpBufferChar(buf, ' ');
-		firstkeyword = false;
-		appendPQExpBuffer(buf, "%s=", keywords[i]);
-		appendConnStrVal(buf, values[i]);
-	}
-
-	connstr = pg_strdup(buf->data);
-	destroyPQExpBuffer(buf);
-	return connstr;
-}
-
-/*
- * Run a query, return the results, exit program on failure.
- */
-static PGresult *
-executeQuery(PGconn *conn, const char *query)
-{
-	PGresult   *res;
-
-	pg_log_info("executing %s", query);
-
-	res = PQexec(conn, query);
-	if (!res ||
-		PQresultStatus(res) != PGRES_TUPLES_OK)
-	{
-		pg_log_error("query failed: %s", PQerrorMessage(conn));
-		pg_log_error_detail("Query was: %s", query);
-		PQfinish(conn);
-		exit_nicely(1);
-	}
-
-	return res;
-}
-
 /*
  * As above for a SQL command (which returns nothing).
  */
-- 
2.34.1

From e3e3d4fc316b22202b7369947e00312e3acd92e2 Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 19 Mar 2025 01:30:12 +0530
Subject: [PATCH 2/4] pg_dumpall with directory|tar|custom format and restore
 it by pg_restore

new option to pg_dumpall:
-F, --format=d|t|c|p  output file format ( plain text (default))

Ex:1 ./pg_dumpall --format=directory --file=dumpDirName
Ex:2 ./pg_dumpall --format=tar --file=dumpDirName
Ex:3 ./pg_dumpall --format=custom --file=dumpDirName
Ex:4 ./pg_dumpall --format=plain --file=dumpDirName

dumps are as:
global.dat ::: global sql commands in simple plain format
map.dat.   ::: dboid dbname ---entries for all databases in simple text form
databases. :::
      subdir     dboid1  -> toc.dat and data files in archive format
      subdir     dboid2. -> toc.dat and data files in archive format
              etc
---------------------------------------------------------------------------
NOTE:
if needed, restore single db by particular subdir

Ex: ./pg_restore --format=directory -d postgres dumpDirName/databases/5
   -- here, 5 is the dboid of postgres db
   -- to get dboid, refer dbname in map.file

--------------------------------------------------------------------------
new options to pg_restore:
-g, --globals-only           restore only global objects, no databases
--exclude-database=PATTERN   exclude database whose name matches pattern

When we give -g/--globals-only option, then only restore globals, no db restoring.

Design:
When --format=d|t|c is specified and there is no toc.dat in main directory, then check
for global.dat to restore all databases. If global.dat file is exist in directory,
then first restore all globals from global.dat and then restore all databases one by one
from map.dat list (if exist)

for --exclude-database=PATTERN for pg_restore

as of now, SELECT 1 WHERE XXX OPERATOR(pg_catalog.~) '^(PATTERN)$' COLLATE pg_catalog.default
if no db connection, then PATTERN=NAME matching only

for each database, we are cleaning on_exit_nicely_index list.

at the end of restore, we are giving warning with total number of errors (including global.dat,
and each database errors) and for each database, we are printing warning with dbname and total
errors.

thread:
https://www.postgresql.org/message-id/flat/CAKYtNAp9vOtydXL3_pnGJ%2BTetZtN%3DFYSnZSMCqXceU3mkHPxPg%40mail.gmail.com#066433cb5ae007cbe35fefddf796d52f
---
 doc/src/sgml/ref/pg_dumpall.sgml     |  78 ++-
 doc/src/sgml/ref/pg_restore.sgml     |  41 +-
 src/bin/pg_dump/parallel.c           |  11 +-
 src/bin/pg_dump/pg_backup.h          |   2 +-
 src/bin/pg_dump/pg_backup_archiver.c |  20 +-
 src/bin/pg_dump/pg_backup_archiver.h |   3 +-
 src/bin/pg_dump/pg_backup_tar.c      |   2 +-
 src/bin/pg_dump/pg_backup_utils.c    |  22 +-
 src/bin/pg_dump/pg_backup_utils.h    |   3 +-
 src/bin/pg_dump/pg_dump.c            |   2 +-
 src/bin/pg_dump/pg_dumpall.c         | 278 +++++++--
 src/bin/pg_dump/pg_restore.c         | 847 ++++++++++++++++++++++++++-
 src/bin/pg_dump/t/001_basic.pl       |   9 +
 src/tools/pgindent/typedefs.list     |   2 +
 14 files changed, 1244 insertions(+), 76 deletions(-)
 mode change 100644 => 100755 src/bin/pg_dump/t/001_basic.pl

diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index 765b30a3a66..82ea2028469 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -16,7 +16,7 @@ PostgreSQL documentation
 
  <refnamediv>
   <refname>pg_dumpall</refname>
-  <refpurpose>extract a <productname>PostgreSQL</productname> database cluster into a script file</refpurpose>
+  <refpurpose>extract a <productname>PostgreSQL</productname> database cluster based on specified dump format </refpurpose>
  </refnamediv>
 
  <refsynopsisdiv>
@@ -121,10 +121,86 @@ PostgreSQL documentation
        <para>
         Send output to the specified file.  If this is omitted, the
         standard output is used.
+        Note: This option can be omitted only when <option>--format</option> is plain
        </para>
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-F <replaceable class="parameter">format</replaceable></option></term>
+      <term><option>--format=<replaceable class="parameter">format</replaceable></option></term>
+      <listitem>
+       <para>
+        Specify format of dump files.  If we want to dump all the databases,
+        then pass this as non-plain so that dump of all databases can be taken
+        in separate subdirectory in archive format.
+        by default, this is plain format.
+
+        If non-plain mode is passed, then global.dat (global sql commands) and
+        map.dat(dboid and dbnames list of all the databases) files will be created.
+        Apart from these files, one subdirectory with databases name will be created.
+        Under this databases subdirectory, there will be files with dboid name for each
+        database and if <option>--format</option> is directory, then toc.dat and other
+        dump files will be under dboid subdirectory.
+
+       <variablelist>
+        <varlistentry>
+         <term><literal>d</literal></term>
+         <term><literal>directory</literal></term>
+         <listitem>
+          <para>
+           Output a directory-format archive suitable for input into pg_restore. Under dboid
+           subdirectory, this will create a directory with one file for each table and large
+           object being dumped, plus a so-called Table of Contents file describing the dumped
+           objects in a machine-readable format that pg_restore can read. A directory format
+           archive can be manipulated with standard Unix tools; for example, files in an
+           uncompressed archive can be compressed with the gzip, lz4, or zstd tools. This
+           format is compressed by default using gzip and also supports parallel dumps.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term><literal>p</literal></term>
+         <term><literal>plain</literal></term>
+         <listitem>
+          <para>
+           Output a plain-text SQL script file (the default).
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term><literal>c</literal></term>
+         <term><literal>custom</literal></term>
+         <listitem>
+          <para>
+           Output a custom-format archive suitable for input into pg_restore. Together with the
+           directory output format, this is the most flexible output format in that it allows manual
+           selection and reordering of archived items during restore. This format is also
+           compressed by default.
+          </para>
+         </listitem>
+        </varlistentry>
+
+         <varlistentry>
+         <term><literal>t</literal></term>
+         <term><literal>tar</literal></term>
+         <listitem>
+          <para>
+           Output a tar-format archive suitable for input into pg_restore. The tar format is
+           compatible with the directory format: extracting a tar-format archive produces a valid
+           directory-format archive. However, the tar format does not support compression. Also,
+           when using tar format the relative order of table data items cannot be changed during restore.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        </variablelist>
+        </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--filter=<replaceable class="parameter">filename</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index c840a807ae9..f0a24134595 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -18,8 +18,9 @@ PostgreSQL documentation
   <refname>pg_restore</refname>
 
   <refpurpose>
-   restore a <productname>PostgreSQL</productname> database from an
-   archive file created by <application>pg_dump</application>
+   restore <productname>PostgreSQL</productname> database from an
+   archive file created by <application>pg_dump</application> or
+   <application>pg_dumpall</application>
   </refpurpose>
  </refnamediv>
 
@@ -37,9 +38,10 @@ PostgreSQL documentation
   <title>Description</title>
 
   <para>
-   <application>pg_restore</application> is a utility for restoring a
+   <application>pg_restore</application> is a utility for restoring
    <productname>PostgreSQL</productname> database from an archive
-   created by <xref linkend="app-pgdump"/> in one of the non-plain-text
+   created by <xref linkend="app-pgdump"/> or
+   <xref linkend="app-pg-dumpall"/> in one of the non-plain-text
    formats.  It will issue the commands necessary to reconstruct the
    database to the state it was in at the time it was saved.  The
    archive files also allow <application>pg_restore</application> to
@@ -140,6 +142,8 @@ PostgreSQL documentation
         commands that mention this database.
         Access privileges for the database itself are also restored,
         unless <option>--no-acl</option> is specified.
+        <option>--create</option> is required when restoring multiple databases
+        from dump of <application>pg_dumpall</application>.
        </para>
 
        <para>
@@ -166,6 +170,25 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--exclude-database=<replaceable class="parameter">pattern</replaceable></option></term>
+      <listitem>
+       <para>
+        Do not restore databases whose name matches
+        <replaceable class="parameter">pattern</replaceable>.
+        Multiple patterns can be excluded by writing multiple
+        <option>--exclude-database</option> switches.  The
+        <replaceable class="parameter">pattern</replaceable> parameter is
+        interpreted as a pattern according to the same rules used by
+        <application>psql</application>'s <literal>\d</literal>
+        commands (see <xref linkend="app-psql-patterns"/>),
+        so multiple databases can also be excluded by writing wildcard
+        characters in the pattern.  When using wildcards, be careful to
+        quote the pattern if needed to prevent shell wildcard expansion.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-e</option></term>
       <term><option>--exit-on-error</option></term>
@@ -315,6 +338,16 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-g</option></term>
+      <term><option>--globals-only</option></term>
+      <listitem>
+       <para>
+        Restore only global objects (roles and tablespaces), no databases.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-I <replaceable class="parameter">index</replaceable></option></term>
       <term><option>--index=<replaceable class="parameter">index</replaceable></option></term>
diff --git a/src/bin/pg_dump/parallel.c b/src/bin/pg_dump/parallel.c
index 086adcdc502..a36d2a5bf84 100644
--- a/src/bin/pg_dump/parallel.c
+++ b/src/bin/pg_dump/parallel.c
@@ -326,11 +326,18 @@ getThreadLocalPQExpBuffer(void)
  * pg_dump and pg_restore call this to register the cleanup handler
  * as soon as they've created the ArchiveHandle.
  */
-void
+int
 on_exit_close_archive(Archive *AHX)
 {
 	shutdown_info.AHX = AHX;
-	on_exit_nicely(archive_close_connection, &shutdown_info);
+	return on_exit_nicely(archive_close_connection, &shutdown_info);
+}
+
+void
+replace_on_exit_close_archive(Archive *AHX, int idx)
+{
+	shutdown_info.AHX = AHX;
+	set_on_exit_nicely_entry(archive_close_connection, &shutdown_info, idx);
 }
 
 /*
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index c68a21027fa..89459dedc4b 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -311,7 +311,7 @@ extern void SetArchiveOptions(Archive *AH, DumpOptions *dopt, RestoreOptions *ro
 
 extern void ProcessArchiveRestoreOptions(Archive *AHX);
 
-extern void RestoreArchive(Archive *AHX);
+extern void RestoreArchive(Archive *AHX, bool append_data);
 
 /* Open an existing archive */
 extern Archive *OpenArchive(const char *FileSpec, const ArchiveFormat fmt);
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 3fd2818223c..e22a8810b45 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -85,7 +85,7 @@ static int	RestoringToDB(ArchiveHandle *AH);
 static void dump_lo_buf(ArchiveHandle *AH);
 static void dumpTimestamp(ArchiveHandle *AH, const char *msg, time_t tim);
 static void SetOutput(ArchiveHandle *AH, const char *filename,
-					  const pg_compress_specification compression_spec);
+			const pg_compress_specification compression_spec, bool append_data);
 static CompressFileHandle *SaveOutput(ArchiveHandle *AH);
 static void RestoreOutput(ArchiveHandle *AH, CompressFileHandle *savedOutput);
 
@@ -337,9 +337,14 @@ ProcessArchiveRestoreOptions(Archive *AHX)
 		StrictNamesCheck(ropt);
 }
 
-/* Public */
+/*
+ * RestoreArchive
+ *
+ * If append_data is set, then append data into file as we are restoring dump
+ * of multiple databases which was taken by pg_dumpall.
+ */
 void
-RestoreArchive(Archive *AHX)
+RestoreArchive(Archive *AHX, bool append_data)
 {
 	ArchiveHandle *AH = (ArchiveHandle *) AHX;
 	RestoreOptions *ropt = AH->public.ropt;
@@ -456,7 +461,7 @@ RestoreArchive(Archive *AHX)
 	 */
 	sav = SaveOutput(AH);
 	if (ropt->filename || ropt->compression_spec.algorithm != PG_COMPRESSION_NONE)
-		SetOutput(AH, ropt->filename, ropt->compression_spec);
+		SetOutput(AH, ropt->filename, ropt->compression_spec, append_data);
 
 	ahprintf(AH, "--\n-- PostgreSQL database dump\n--\n\n");
 
@@ -1292,7 +1297,7 @@ PrintTOCSummary(Archive *AHX)
 
 	sav = SaveOutput(AH);
 	if (ropt->filename)
-		SetOutput(AH, ropt->filename, out_compression_spec);
+		SetOutput(AH, ropt->filename, out_compression_spec, false);
 
 	if (strftime(stamp_str, sizeof(stamp_str), PGDUMP_STRFTIME_FMT,
 				 localtime(&AH->createDate)) == 0)
@@ -1671,7 +1676,8 @@ archprintf(Archive *AH, const char *fmt,...)
 
 static void
 SetOutput(ArchiveHandle *AH, const char *filename,
-		  const pg_compress_specification compression_spec)
+		  const pg_compress_specification compression_spec,
+		  bool append_data)
 {
 	CompressFileHandle *CFH;
 	const char *mode;
@@ -1691,7 +1697,7 @@ SetOutput(ArchiveHandle *AH, const char *filename,
 	else
 		fn = fileno(stdout);
 
-	if (AH->mode == archModeAppend)
+	if (append_data || AH->mode == archModeAppend)
 		mode = PG_BINARY_A;
 	else
 		mode = PG_BINARY_W;
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index a2064f471ed..ae433132435 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -385,7 +385,8 @@ struct _tocEntry
 };
 
 extern int	parallel_restore(ArchiveHandle *AH, TocEntry *te);
-extern void on_exit_close_archive(Archive *AHX);
+extern int on_exit_close_archive(Archive *AHX);
+extern void replace_on_exit_close_archive(Archive *AHX, int idx);
 
 extern void warn_or_exit_horribly(ArchiveHandle *AH, const char *fmt,...) pg_attribute_printf(2, 3);
 
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index b5ba3b46dd9..d94d0de2a5d 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -826,7 +826,7 @@ _CloseArchive(ArchiveHandle *AH)
 		savVerbose = AH->public.verbose;
 		AH->public.verbose = 0;
 
-		RestoreArchive((Archive *) AH);
+		RestoreArchive((Archive *) AH, false);
 
 		SetArchiveOptions((Archive *) AH, savDopt, savRopt);
 
diff --git a/src/bin/pg_dump/pg_backup_utils.c b/src/bin/pg_dump/pg_backup_utils.c
index 79aec5f5158..59ece2999a8 100644
--- a/src/bin/pg_dump/pg_backup_utils.c
+++ b/src/bin/pg_dump/pg_backup_utils.c
@@ -61,14 +61,26 @@ set_dump_section(const char *arg, int *dumpSections)

 /* Register a callback to be run when exit_nicely is invoked. */
-void
+int
 on_exit_nicely(on_exit_nicely_callback function, void *arg)
 {
-	if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY)
-		pg_fatal("out of on_exit_nicely slots");
-	on_exit_nicely_list[on_exit_nicely_index].function = function;
-	on_exit_nicely_list[on_exit_nicely_index].arg = arg;
+	set_on_exit_nicely_entry(function, arg, on_exit_nicely_index);
 	on_exit_nicely_index++;
+
+	return (on_exit_nicely_index - 1);
+}
+
+void
+set_on_exit_nicely_entry(on_exit_nicely_callback function, void *arg, int i)
+{
+	if (i >= MAX_ON_EXIT_NICELY)
+		pg_fatal("out of on_exit_nicely slots");
+
+	if (i > on_exit_nicely_index)
+		pg_fatal("no entry exists on %d index into on_exit_nicely slots", i);
+
+	on_exit_nicely_list[i].function = function;
+	on_exit_nicely_list[i].arg = arg;
 }
 
 /*
diff --git a/src/bin/pg_dump/pg_backup_utils.h b/src/bin/pg_dump/pg_backup_utils.h
index ba042016879..bbefdc112f5 100644
--- a/src/bin/pg_dump/pg_backup_utils.h
+++ b/src/bin/pg_dump/pg_backup_utils.h
@@ -28,7 +28,8 @@ typedef void (*on_exit_nicely_callback) (int code, void *arg);
 extern const char *progname;
 
 extern void set_dump_section(const char *arg, int *dumpSections);
-extern void on_exit_nicely(on_exit_nicely_callback function, void *arg);
+extern int on_exit_nicely(on_exit_nicely_callback function, void *arg);
+extern void set_on_exit_nicely_entry(on_exit_nicely_callback function, void *arg, int idx);
 pg_noreturn extern void exit_nicely(int code);
 
 /* In pg_dump, we modify pg_fatal to call exit_nicely instead of exit */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 015a434fc0b..608b0696c03 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1219,7 +1219,7 @@ main(int argc, char **argv)
 	 * right now.
 	 */
 	if (plainText)
-		RestoreArchive(fout);
+		RestoreArchive(fout, false);
 
 	CloseArchive(fout);
 
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 573a8b61a45..a3dcc585ace 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -15,6 +15,7 @@
 
 #include "postgres_fe.h"
 
+#include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -64,9 +65,10 @@ static void dropTablespaces(PGconn *conn);
 static void dumpTablespaces(PGconn *conn);
 static void dropDBs(PGconn *conn);
 static void dumpUserConfig(PGconn *conn, const char *username);
-static void dumpDatabases(PGconn *conn);
+static void dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat);
 static void dumpTimestamp(const char *msg);
-static int	runPgDump(const char *dbname, const char *create_opts);
+static int	runPgDump(const char *dbname, const char *create_opts,
+		char *dbfile, ArchiveFormat archDumpFormat);
 static void buildShSecLabels(PGconn *conn,
 							 const char *catalog_name, Oid objectId,
 							 const char *objtype, const char *objname,
@@ -75,6 +77,8 @@ static void executeCommand(PGconn *conn, const char *query);
 static void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns,
 								   SimpleStringList *names);
 static void read_dumpall_filters(const char *filename, SimpleStringList *pattern);
+static void create_or_open_dir(const char *dirname);
+static ArchiveFormat parseDumpFormat(const char *format);
 
 static char pg_dump_bin[MAXPGPATH];
 static PQExpBuffer pgdumpopts;
@@ -146,6 +150,7 @@ main(int argc, char *argv[])
 		{"password", no_argument, NULL, 'W'},
 		{"no-privileges", no_argument, NULL, 'x'},
 		{"no-acl", no_argument, NULL, 'x'},
+		{"format", required_argument, NULL, 'F'},
 
 		/*
 		 * the following options don't have an equivalent short option letter
@@ -195,6 +200,8 @@ main(int argc, char *argv[])
 	char	   *pgdb = NULL;
 	char	   *use_role = NULL;
 	const char *dumpencoding = NULL;
+	ArchiveFormat archDumpFormat = archNull;
+	const char *formatName = "p";
 	trivalue	prompt_password = TRI_DEFAULT;
 	bool		data_only = false;
 	bool		globals_only = false;
@@ -244,7 +251,7 @@ main(int argc, char *argv[])
 
 	pgdumpopts = createPQExpBuffer();
 
-	while ((c = getopt_long(argc, argv, "acd:E:f:gh:l:Op:rsS:tU:vwWx", long_options, &optindex)) != -1)
+	while ((c = getopt_long(argc, argv, "acd:E:f:F:gh:l:Op:rsS:tU:vwWx", long_options, &optindex)) != -1)
 	{
 		switch (c)
 		{
@@ -272,7 +279,9 @@ main(int argc, char *argv[])
 				appendPQExpBufferStr(pgdumpopts, " -f ");
 				appendShellString(pgdumpopts, filename);
 				break;
-
+			case 'F':
+				formatName = pg_strdup(optarg);
+				break;
 			case 'g':
 				globals_only = true;
 				break;
@@ -421,6 +430,21 @@ main(int argc, char *argv[])
 		exit_nicely(1);
 	}
 
+	/* Get format for dump. */
+	archDumpFormat = parseDumpFormat(formatName);
+
+	/*
+	 * If non-plain format is specified then we must provide the
+	 * file name to create one main directory.
+	 */
+	if (archDumpFormat != archNull &&
+			(!filename || strcmp(filename, "") == 0))
+	{
+		pg_log_error("options -F/--format=d|c|t requires option -f/--file with non-empty string");
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+		exit_nicely(1);
+	}
+
 	/*
 	 * If password values are not required in the dump, switch to using
 	 * pg_roles which is equally useful, just more likely to have unrestricted
@@ -483,6 +507,33 @@ main(int argc, char *argv[])
 	if (statistics_only)
 		appendPQExpBufferStr(pgdumpopts, " --statistics-only");
 
+	/*
+	 * Open the output file if required, otherwise use stdout.  If required,
+	 * then create new directory and global.dat file.
+	 */
+	if (archDumpFormat != archNull)
+	{
+		char	toc_path[MAXPGPATH];
+
+		/* Create new directory or accept the empty existing directory. */
+		create_or_open_dir(filename);
+
+		snprintf(toc_path, MAXPGPATH, "%s/global.dat", filename);
+
+		OPF = fopen(toc_path, PG_BINARY_W);
+		if (!OPF)
+			pg_fatal("could not open global.dat file: %s", strerror(errno));
+	}
+	else if (filename)
+	{
+		OPF = fopen(filename, PG_BINARY_W);
+		if (!OPF)
+			pg_fatal("could not open output file \"%s\": %m",
+					 filename);
+	}
+	else
+		OPF = stdout;
+
 	/*
 	 * If there was a database specified on the command line, use that,
 	 * otherwise try to connect to database "postgres", and failing that
@@ -522,19 +573,6 @@ main(int argc, char *argv[])
 	expand_dbname_patterns(conn, &database_exclude_patterns,
 						   &database_exclude_names);
 
-	/*
-	 * Open the output file if required, otherwise use stdout
-	 */
-	if (filename)
-	{
-		OPF = fopen(filename, PG_BINARY_W);
-		if (!OPF)
-			pg_fatal("could not open output file \"%s\": %m",
-					 filename);
-	}
-	else
-		OPF = stdout;
-
 	/*
 	 * Set the client encoding if requested.
 	 */
@@ -634,7 +672,7 @@ main(int argc, char *argv[])
 	}
 
 	if (!globals_only && !roles_only && !tablespaces_only)
-		dumpDatabases(conn);
+		dumpDatabases(conn, archDumpFormat);
 
 	PQfinish(conn);
 
@@ -647,7 +685,7 @@ main(int argc, char *argv[])
 		fclose(OPF);
 
 		/* sync the resulting file, errors are not fatal */
-		if (dosync)
+		if (dosync && (archDumpFormat == archNull))
 			(void) fsync_fname(filename, false);
 	}
 
@@ -658,12 +696,14 @@ main(int argc, char *argv[])
 static void
 help(void)
 {
-	printf(_("%s extracts a PostgreSQL database cluster into an SQL script file.\n\n"), progname);
+	printf(_("%s extracts a PostgreSQL database cluster based on specified dump format.\n\n"), progname);
 	printf(_("Usage:\n"));
 	printf(_("  %s [OPTION]...\n"), progname);
 
 	printf(_("\nGeneral options:\n"));
 	printf(_("  -f, --file=FILENAME          output file name\n"));
+	printf(_("  -F, --format=c|d|t|p         output file format (custom, directory, tar,\n"
+			 "                               plain text (default))\n"));
 	printf(_("  -v, --verbose                verbose mode\n"));
 	printf(_("  -V, --version                output version information, then exit\n"));
 	printf(_("  --lock-wait-timeout=TIMEOUT  fail after waiting TIMEOUT for a table lock\n"));
@@ -1570,10 +1610,13 @@ expand_dbname_patterns(PGconn *conn,
  * Dump contents of databases.
  */
 static void
-dumpDatabases(PGconn *conn)
+dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 {
 	PGresult   *res;
 	int			i;
+	char		db_subdir[MAXPGPATH];
+	char		dbfilepath[MAXPGPATH];
+	FILE       *map_file = NULL;
 
 	/*
 	 * Skip databases marked not datallowconn, since we'd be unable to connect
@@ -1587,7 +1630,7 @@ dumpDatabases(PGconn *conn)
 	 * doesn't have some failure mode with --clean.
 	 */
 	res = executeQuery(conn,
-					   "SELECT datname "
+					   "SELECT datname, oid "
 					   "FROM pg_database d "
 					   "WHERE datallowconn AND datconnlimit != -2 "
 					   "ORDER BY (datname <> 'template1'), datname");
@@ -1595,9 +1638,33 @@ dumpDatabases(PGconn *conn)
 	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Databases\n--\n\n");
 
+	/*
+	 * If directory/tar/custom format is specified then create a subdirectory
+	 * under the main directory and each database dump file subdirectory will
+	 * be created under the subdirectory in archive mode as per single db pg_dump.
+	 */
+	if (archDumpFormat != archNull)
+	{
+		char	map_file_path[MAXPGPATH];
+
+		snprintf(db_subdir, MAXPGPATH, "%s/databases", filename);
+
+		/* Create a subdirectory with 'databases' name under main directory. */
+		if (mkdir(db_subdir, 0755) != 0)
+			pg_fatal("could not create subdirectory \"%s\": %m", db_subdir);
+
+		snprintf(map_file_path, MAXPGPATH, "%s/map.dat", filename);
+
+		/* Create a map file (to store dboid and dbname) */
+		map_file = fopen(map_file_path, PG_BINARY_W);
+		if (!map_file)
+			pg_fatal("could not open map file: %s", strerror(errno));
+	}
+
 	for (i = 0; i < PQntuples(res); i++)
 	{
 		char	   *dbname = PQgetvalue(res, i, 0);
+		char	   *oid = PQgetvalue(res, i, 1);
 		const char *create_opts;
 		int			ret;
 
@@ -1612,6 +1679,18 @@ dumpDatabases(PGconn *conn)
 			continue;
 		}
 
+		/*
+		 * If this is non-plain dump format, then append dboid and dbname to
+		 * the map.dat file.
+		 */
+		if (archDumpFormat != archNull)
+		{
+			snprintf(dbfilepath, MAXPGPATH, "\"%s\"/\"%s\"", db_subdir, oid);
+
+			/* Put one line entry for dboid and dbname in map file. */
+			fprintf(map_file, "%s %s\n", oid, pg_strdup(dbname));
+		}
+
 		pg_log_info("dumping database \"%s\"", dbname);
 
 		fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n", dbname);
@@ -1630,9 +1709,17 @@ dumpDatabases(PGconn *conn)
 				create_opts = "--clean --create";
 			else
 			{
-				create_opts = "";
 				/* Since pg_dump won't emit a \connect command, we must */
-				fprintf(OPF, "\\connect %s\n\n", dbname);
+				if (archDumpFormat == archNull)
+				{
+					create_opts = "";
+					fprintf(OPF, "\\connect %s\n\n", dbname);
+				}
+				else
+				{
+					/* Dumping all databases so add --create option. */
+					create_opts = "--create";
+				}
 			}
 		}
 		else
@@ -1641,19 +1728,30 @@ dumpDatabases(PGconn *conn)
 		if (filename)
 			fclose(OPF);
 
-		ret = runPgDump(dbname, create_opts);
+		ret = runPgDump(dbname, create_opts, dbfilepath, archDumpFormat);
 		if (ret != 0)
 			pg_fatal("pg_dump failed on database \"%s\", exiting", dbname);
 
 		if (filename)
 		{
-			OPF = fopen(filename, PG_BINARY_A);
+			char	toc_path[MAXPGPATH];
+
+			if (archDumpFormat != archNull)
+				snprintf(toc_path, MAXPGPATH, "%s/global.dat", filename);
+			else
+				snprintf(toc_path, MAXPGPATH, "%s", filename);
+
+			OPF = fopen(toc_path, PG_BINARY_A);
 			if (!OPF)
 				pg_fatal("could not re-open the output file \"%s\": %m",
-						 filename);
+						 toc_path);
 		}
 	}
 
+	/* Close map file */
+	if (archDumpFormat != archNull)
+		fclose(map_file);
+
 	PQclear(res);
 }
 
@@ -1663,7 +1761,8 @@ dumpDatabases(PGconn *conn)
  * Run pg_dump on dbname, with specified options.
  */
 static int
-runPgDump(const char *dbname, const char *create_opts)
+runPgDump(const char *dbname, const char *create_opts, char *dbfile,
+		ArchiveFormat archDumpFormat)
 {
 	PQExpBufferData connstrbuf;
 	PQExpBufferData cmd;
@@ -1672,17 +1771,36 @@ runPgDump(const char *dbname, const char *create_opts)
 	initPQExpBuffer(&connstrbuf);
 	initPQExpBuffer(&cmd);
 
-	printfPQExpBuffer(&cmd, "\"%s\" %s %s", pg_dump_bin,
-					  pgdumpopts->data, create_opts);
-
 	/*
-	 * If we have a filename, use the undocumented plain-append pg_dump
-	 * format.
+	 * If this is non-plain format dump, then append file name and dump
+	 * format to the pg_dump command to get archive dump.
 	 */
-	if (filename)
-		appendPQExpBufferStr(&cmd, " -Fa ");
+	if (archDumpFormat != archNull)
+	{
+		printfPQExpBuffer(&cmd, "\"%s\" -f %s %s", pg_dump_bin,
+						  dbfile, create_opts);
+
+		if (archDumpFormat == archDirectory)
+			appendPQExpBufferStr(&cmd, "  --format=directory ");
+		else if (archDumpFormat == archCustom)
+			appendPQExpBufferStr(&cmd, "  --format=custom ");
+		else if (archDumpFormat == archTar)
+			appendPQExpBufferStr(&cmd, "  --format=tar ");
+	}
 	else
-		appendPQExpBufferStr(&cmd, " -Fp ");
+	{
+		printfPQExpBuffer(&cmd, "\"%s\" %s %s", pg_dump_bin,
+						pgdumpopts->data, create_opts);
+
+		/*
+		* If we have a filename, use the undocumented plain-append pg_dump
+		* format.
+		*/
+		if (filename)
+			appendPQExpBufferStr(&cmd, " -Fa ");
+		else
+			appendPQExpBufferStr(&cmd, " -Fp ");
+	}
 
 	/*
 	 * Append the database name to the already-constructed stem of connection
@@ -1827,3 +1945,91 @@ read_dumpall_filters(const char *filename, SimpleStringList *pattern)
 
 	filter_free(&fstate);
 }
+
+/*
+ * create_or_open_dir
+ *
+ * This will create a new directory with given name.  If there is already same
+ * empty directory exist, then use it.
+ */
+static void
+create_or_open_dir(const char *dirname)
+{
+	struct stat		st;
+	bool			is_empty = false;
+
+	/* we accept an empty existing directory */
+	if (stat(dirname, &st) == 0 && S_ISDIR(st.st_mode))
+	{
+		DIR		*dir = opendir(dirname);
+
+		if (dir)
+		{
+			struct dirent	*d;
+
+			is_empty = true;
+
+			while (errno = 0, (d = readdir(dir)))
+			{
+				if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0)
+				{
+					is_empty = false;
+					break;
+				}
+			}
+
+			if (errno)
+				pg_fatal("could not read directory \"%s\": %m",
+						dirname);
+
+			if (closedir(dir))
+				pg_fatal("could not close directory \"%s\": %m",
+						dirname);
+		}
+
+		if(!is_empty)
+		{
+			pg_log_error("directory \"%s\" exists but is not empty", dirname);
+			pg_log_error_hint("If you want to dump data on this directory, either remove or empty "
+							  "this directory \"%s\" or run %s "
+							  "with an argument other than \"%s\".",
+							  dirname, progname, dirname);
+			exit_nicely(1);
+		}
+	}
+	else if (mkdir(dirname, 0700) < 0)
+		pg_fatal("could not create directory \"%s\": %m", dirname);
+}
+
+/*
+ * parseDumpFormat
+ *
+ * This will validate dump formats.
+ */
+static ArchiveFormat
+parseDumpFormat(const char *format)
+{
+	ArchiveFormat	archDumpFormat;
+
+	if (pg_strcasecmp(format, "c") == 0)
+		archDumpFormat = archCustom;
+	else if (pg_strcasecmp(format, "custom") == 0)
+		archDumpFormat = archCustom;
+	else if (pg_strcasecmp(format, "d") == 0)
+		archDumpFormat = archDirectory;
+	else if (pg_strcasecmp(format, "directory") == 0)
+		archDumpFormat = archDirectory;
+	else if (pg_strcasecmp(format, "p") == 0)
+		archDumpFormat = archNull;
+	else if (pg_strcasecmp(format, "plain") == 0)
+		archDumpFormat = archNull;
+	else if (pg_strcasecmp(format, "t") == 0)
+		archDumpFormat = archTar;
+	else if (pg_strcasecmp(format, "tar") == 0)
+		archDumpFormat = archTar;
+	else
+		pg_fatal("unrecognized archive format \"%s\"; please specify \"c\", \"d\", \"p\", or \"t\"",
+				format);
+
+	return archDumpFormat;
+}
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 47f7b0dd3a1..e4093427e2f 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -2,7 +2,7 @@
  *
  * pg_restore.c
  *	pg_restore is an utility extracting postgres database definitions
- *	from a backup archive created by pg_dump using the archiver
+ *	from a backup archive created by pg_dump/pg_dumpall using the archiver
  *	interface.
  *
  *	pg_restore will read the backup archive and
@@ -41,30 +41,77 @@
 #include "postgres_fe.h"
 
 #include <ctype.h>
+#include <sys/stat.h>
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
 
+#include "common/connect.h"
+#include "compress_io.h"
+#include "common/string.h"
+#include "connectdb.h"
 #include "fe_utils/option_utils.h"
+#include "fe_utils/string_utils.h"
 #include "filter.h"
 #include "getopt_long.h"
 #include "parallel.h"
+#include "pg_backup_archiver.h"
 #include "pg_backup_utils.h"
 
+typedef struct SimpleDatabaseOidListCell
+{
+	struct SimpleDatabaseOidListCell	*next;
+	Oid									db_oid;
+	const char							*db_name;
+} SimpleDatabaseOidListCell;
+
+typedef struct SimpleDatabaseOidList
+{
+	SimpleDatabaseOidListCell *head;
+	SimpleDatabaseOidListCell *tail;
+} SimpleDatabaseOidList;
+
 static void usage(const char *progname);
 static void read_restore_filters(const char *filename, RestoreOptions *opts);
+static bool IsFileExistsInDirectory(const char *dir, const char *filename);
+static int restoreOneDatabase(const char *inputFileSpec, RestoreOptions *opts,
+							  int numWorkers, bool append_data, int num);
+static int ReadOneStatement(StringInfo inBuf, FILE *pfile);
+static int restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
+							   SimpleStringList db_exclude_patterns, RestoreOptions *opts, int numWorkers);
+static int process_global_sql_commands(PGconn *conn, const char *dumpdirpath,
+										const char *outfile);
+static void copy_or_print_global_file(const char *outfile, FILE *pfile);
+static int get_dbnames_list_to_restore(PGconn *conn,
+		SimpleDatabaseOidList *dbname_oid_list,
+		SimpleStringList db_exclude_patterns);
+static int get_dbname_oid_list_from_mfile(const char *dumpdirpath,
+										  SimpleDatabaseOidList *dbname_oid_list);
+static void simple_db_oid_list_append(SimpleDatabaseOidList *list, Oid db_oid,
+									  const char *dbname);
+static void simple_string_full_list_delete(SimpleStringList *list);
+static void simple_db_oid_full_list_delete(SimpleDatabaseOidList *list);
+static void simple_db_oid_list_delete(SimpleDatabaseOidList *list,
+									  SimpleDatabaseOidListCell *cell,
+									  SimpleDatabaseOidListCell *prev);
+static void simple_db_oid_list_append(SimpleDatabaseOidList *list,
+		Oid db_oid, const char *dbname);
+static size_t quote_literal_internal(char *dst, const char *src, size_t len);
+static char *quote_literal_cstr(const char *rawstr);
+static int on_exit_index = 0;
 
 int
 main(int argc, char **argv)
 {
 	RestoreOptions *opts;
 	int			c;
-	int			exit_code;
 	int			numWorkers = 1;
-	Archive    *AH;
 	char	   *inputFileSpec;
 	bool		data_only = false;
 	bool		schema_only = false;
+	int			n_errors = 0;
+	bool		globals_only = false;
+	SimpleStringList	db_exclude_patterns = {NULL, NULL};
 	static int	disable_triggers = 0;
 	static int	enable_row_security = 0;
 	static int	if_exists = 0;
@@ -90,6 +137,7 @@ main(int argc, char **argv)
 		{"clean", 0, NULL, 'c'},
 		{"create", 0, NULL, 'C'},
 		{"data-only", 0, NULL, 'a'},
+		{"globals-only", 0, NULL, 'g'},
 		{"dbname", 1, NULL, 'd'},
 		{"exit-on-error", 0, NULL, 'e'},
 		{"exclude-schema", 1, NULL, 'N'},
@@ -144,6 +192,7 @@ main(int argc, char **argv)
 		{"with-statistics", no_argument, &with_statistics, 1},
 		{"statistics-only", no_argument, &statistics_only, 1},
 		{"filter", required_argument, NULL, 4},
+		{"exclude-database", required_argument, NULL, 6},
 
 		{NULL, 0, NULL, 0}
 	};
@@ -172,7 +221,7 @@ main(int argc, char **argv)
 		}
 	}
 
-	while ((c = getopt_long(argc, argv, "acCd:ef:F:h:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1",
+	while ((c = getopt_long(argc, argv, "acCd:ef:F:gh:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1",
 							cmdopts, NULL)) != -1)
 	{
 		switch (c)
@@ -199,11 +248,14 @@ main(int argc, char **argv)
 				if (strlen(optarg) != 0)
 					opts->formatName = pg_strdup(optarg);
 				break;
+			case 'g':
+				/* restore only global.dat file from directory */
+				globals_only = true;
+				break;
 			case 'h':
 				if (strlen(optarg) != 0)
 					opts->cparams.pghost = pg_strdup(optarg);
 				break;
-
 			case 'j':			/* number of restore jobs */
 				if (!option_parse_int(optarg, "-j/--jobs", 1,
 									  PG_MAX_JOBS,
@@ -318,6 +370,10 @@ main(int argc, char **argv)
 					exit(1);
 				opts->exit_on_error = true;
 				break;
+			case 6:
+				/* list of databases patterns those needs to skip while restoring */
+				simple_string_list_append(&db_exclude_patterns, optarg);
+				break;
 
 			default:
 				/* getopt_long already emitted a complaint */
@@ -345,6 +401,13 @@ main(int argc, char **argv)
 	if (!opts->cparams.dbname && !opts->filename && !opts->tocSummary)
 		pg_fatal("one of -d/--dbname and -f/--file must be specified");
 
+	if (db_exclude_patterns.head != NULL && globals_only)
+	{
+		pg_log_error("option --exclude-database cannot be used together with -g/--globals-only");
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+		exit_nicely(1);
+	}
+
 	/* Should get at most one of -d and -f, else user is confused */
 	if (opts->cparams.dbname)
 	{
@@ -452,6 +515,108 @@ main(int argc, char **argv)
 					 opts->formatName);
 	}
 
+	/*
+	 * If toc.dat file does not present in current path, then check for
+	 * global.dat.  If global.dat file is present, then restore all the
+	 * databases from map.dat(if exist) file list and skip restoring for
+	 * --exclude-database patterns.
+	 */
+	if (inputFileSpec != NULL && !IsFileExistsInDirectory(inputFileSpec, "toc.dat") &&
+			IsFileExistsInDirectory(inputFileSpec, "global.dat"))
+	{
+		PGconn  *conn = NULL; /* Connection to restore global sql commands. */
+
+		/*
+		 * User is suggested to use single database dump for --list option.
+		 */
+		if (opts->tocSummary)
+			pg_fatal("option -l/--list cannot be used when restoring multiple databases by archive of pg_dumpall");
+
+		/*
+		 * To restore multiple databases, -C (create database) option should be specified.
+		 * Even there is single database in dump, report error because it might be possible
+		 * that database hasn't created so better we report error.
+		 */
+		if (!globals_only && opts->createDB != 1)
+		{
+			pg_log_error("-C/--create option should be specified when restoring multiple databases by archive of pg_dumpall");
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+			pg_log_error_hint("If db is already created and dump has single db dump, then use particular dump file.");
+			exit_nicely(1);
+		}
+
+		/*
+		 * Connect to database to execute global sql commands from global.dat file.
+		 */
+		if (opts->cparams.dbname)
+		{
+			conn = ConnectDatabase(opts->cparams.dbname, NULL, opts->cparams.pghost,
+					opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT,
+					false, progname, NULL, NULL, NULL, NULL);
+
+			if (!conn)
+				pg_fatal("could not connect to database \"%s\"", opts->cparams.dbname);
+		}
+
+		/* If globals-only, then return from here. */
+		if (globals_only)
+		{
+			/* Open global.dat file and execute/append all the global sql commands. */
+			n_errors = process_global_sql_commands(conn, inputFileSpec,
+					opts->filename);
+
+			if (conn)
+				PQfinish(conn);
+
+			pg_log_info("databases restoring is skipped as -g/--globals-only option is specified");
+		}
+		else
+		{
+			/* Now restore all the databases from map.dat file. */
+			n_errors = restoreAllDatabases(conn, inputFileSpec, db_exclude_patterns,
+					opts, numWorkers);
+		}
+
+		/* Free db pattern list. */
+		simple_string_full_list_delete(&db_exclude_patterns);
+	}
+	else /* process if global.dat file does not exist. */
+	{
+		if (db_exclude_patterns.head != NULL)
+			pg_fatal("option --exclude-database can be used only when restoring multiple databases by archive of pg_dumpall");
+
+		if (globals_only)
+			pg_fatal("option -g/--globals-only can be used only when restoring multiple databases by archive of pg_dumpall");
+
+		n_errors = restoreOneDatabase(inputFileSpec, opts, numWorkers, false, 0);
+	}
+
+	on_exit_index = 0; /* Reset index. */
+
+	/* Done, print a summary of ignored errors during restore. */
+	if (n_errors)
+	{
+		pg_log_warning("errors ignored on restore: %d", n_errors);
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * restoreOneDatabase
+ *
+ * This will restore one database using toc.dat file.
+ *
+ * returns the number of errors while doing restore.
+ */
+static int
+restoreOneDatabase(const char *inputFileSpec, RestoreOptions *opts,
+				   int numWorkers, bool append_data, int num)
+{
+	Archive		*AH;
+	int			n_errors;
+
 	AH = OpenArchive(inputFileSpec, opts->format);
 
 	SetArchiveOptions(AH, NULL, opts);
@@ -460,8 +625,14 @@ main(int argc, char **argv)
 	 * We don't have a connection yet but that doesn't matter. The connection
 	 * is initialized to NULL and if we terminate through exit_nicely() while
 	 * it's still NULL, the cleanup function will just be a no-op.
+	 * If we are restoring multiple databases, then save index of exit_nicely
+	 * so that we can use same slot for all the databases as we already closed
+	 * the previous archive by CloseArchive.
 	 */
-	on_exit_close_archive(AH);
+	if (!append_data || num == 0)
+		on_exit_index = on_exit_close_archive(AH);
+	else
+		replace_on_exit_close_archive(AH, on_exit_index);
 
 	/* Let the archiver know how noisy to be */
 	AH->verbose = opts->verbose;
@@ -481,25 +652,22 @@ main(int argc, char **argv)
 	else
 	{
 		ProcessArchiveRestoreOptions(AH);
-		RestoreArchive(AH);
+		RestoreArchive(AH, append_data);
 	}
 
-	/* done, print a summary of ignored errors */
-	if (AH->n_errors)
-		pg_log_warning("errors ignored on restore: %d", AH->n_errors);
+	n_errors = AH->n_errors;
 
 	/* AH may be freed in CloseArchive? */
-	exit_code = AH->n_errors ? 1 : 0;
-
 	CloseArchive(AH);
 
-	return exit_code;
+	return n_errors;
 }
 
 static void
 usage(const char *progname)
 {
-	printf(_("%s restores a PostgreSQL database from an archive created by pg_dump.\n\n"), progname);
+	printf(_("%s restores a PostgreSQL database from an archive created by pg_dump.\n"
+				"If archive is created by pg_dumpall, then restores multiple databases also. \n\n"), progname);
 	printf(_("Usage:\n"));
 	printf(_("  %s [OPTION]... [FILE]\n"), progname);
 
@@ -517,6 +685,7 @@ usage(const char *progname)
 	printf(_("  -c, --clean                  clean (drop) database objects before recreating\n"));
 	printf(_("  -C, --create                 create the target database\n"));
 	printf(_("  -e, --exit-on-error          exit on error, default is to continue\n"));
+	printf(_("  -g, --globals-only           restore only global objects, no databases\n"));
 	printf(_("  -I, --index=NAME             restore named index\n"));
 	printf(_("  -j, --jobs=NUM               use this many parallel jobs to restore\n"));
 	printf(_("  -L, --use-list=FILENAME      use table of contents from this file for\n"
@@ -529,6 +698,7 @@ usage(const char *progname)
 	printf(_("  -S, --superuser=NAME         superuser user name to use for disabling triggers\n"));
 	printf(_("  -t, --table=NAME             restore named relation (table, view, etc.)\n"));
 	printf(_("  -T, --trigger=NAME           restore named trigger\n"));
+	printf(_("  --exclude-database=PATTERN   exclude databases whose name matches with pattern\n"));
 	printf(_("  -x, --no-privileges          skip restoration of access privileges (grant/revoke)\n"));
 	printf(_("  -1, --single-transaction     restore as a single transaction\n"));
 	printf(_("  --disable-triggers           disable triggers during data-only restore\n"));
@@ -569,8 +739,8 @@ usage(const char *progname)
 	printf(_("  --role=ROLENAME          do SET ROLE before restore\n"));
 
 	printf(_("\n"
-			 "The options -I, -n, -N, -P, -t, -T, and --section can be combined and specified\n"
-			 "multiple times to select multiple objects.\n"));
+			 "The options -I, -n, -N, -P, -t, -T, --section, and --exclude-database can be combined\n"
+			 "and specified multiple times to select multiple objects.\n"));
 	printf(_("\nIf no input file name is supplied, then standard input is used.\n\n"));
 	printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
 	printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
@@ -675,3 +845,648 @@ read_restore_filters(const char *filename, RestoreOptions *opts)
 
 	filter_free(&fstate);
 }
+
+/*
+ * IsFileExistsInDirectory
+ *
+ * Returns true if file exist in current directory.
+ */
+static bool
+IsFileExistsInDirectory(const char *dir, const char *filename)
+{
+	struct stat			st;
+	char				buf[MAXPGPATH];
+
+	if (snprintf(buf, MAXPGPATH, "%s/%s", dir, filename) >= MAXPGPATH)
+		pg_fatal("directory name too long: \"%s\"", dir);
+
+	return (stat(buf, &st) == 0 && S_ISREG(st.st_mode));
+}
+
+/*
+ * ReadOneStatement
+ *
+ * This will start reading from passed file pointer using fgetc and read till
+ * semicolon(sql statement terminator for global.dat file)
+ *
+ * EOF is returned if end-of-file input is seen; time to shut down.
+ */
+
+static int
+ReadOneStatement(StringInfo inBuf, FILE *pfile)
+{
+	int			c; /* character read from getc() */
+	int			m;
+
+	StringInfoData	q;
+	initStringInfo(&q);
+
+	resetStringInfo(inBuf);
+
+	/*
+	 * Read characters until EOF or the appropriate delimiter is seen.
+	 */
+	while ((c = fgetc(pfile)) != EOF)
+	{
+		if (c != '\'' && c != '"' && c != '\n' && c != ';')
+		{
+			appendStringInfoChar(inBuf, (char) c);
+			while ((c = fgetc(pfile)) != EOF)
+			{
+				if (c != '\'' && c != '"' && c != ';' && c != '\n')
+					appendStringInfoChar(inBuf, (char) c);
+				else
+					break;
+			}
+		}
+
+		if (c == '\'' || c == '"')
+		{
+			appendStringInfoChar(&q, (char) c);
+			m = c;
+
+			while ((c = fgetc(pfile)) != EOF)
+			{
+				appendStringInfoChar(&q, (char) c);
+
+				if(c == m)
+				{
+					appendStringInfoString(inBuf, q.data);
+					resetStringInfo(&q);
+					break;
+				}
+			}
+		}
+
+		if (c == ';')
+		{
+			appendStringInfoChar(inBuf, (char) ';');
+			break;
+		}
+
+		if (c == '\n')
+			appendStringInfoChar(inBuf, (char) '\n');
+	}
+
+	/* No input before EOF signal means time to quit. */
+	if (c == EOF && inBuf->len == 0)
+		return EOF;
+
+	/* Add '\0' to make it look the same as message case. */
+	appendStringInfoChar(inBuf, (char) '\0');
+
+	return 'Q';
+}
+
+/*
+ * get_dbnames_list_to_restore
+ *
+ * This will remove entries from dbname_oid_list that pattern matching any
+ * in the db_exclude_patterns list.  dbname_oid_list maybe inplace modified.
+ *
+ * returns, number of database will be restored.
+ *
+ */
+static int
+get_dbnames_list_to_restore(PGconn *conn,
+		SimpleDatabaseOidList *dbname_oid_list,
+		SimpleStringList db_exclude_patterns)
+{
+	SimpleDatabaseOidListCell	*dboid_cell = dbname_oid_list->head;
+	SimpleDatabaseOidListCell	*dboidprecell = NULL;
+	int							count_db = 0;
+	PQExpBuffer query;
+	PGresult   *res;
+
+	/* Return 0 if there is no database to restore. */
+	if (dboid_cell == NULL)
+		return 0;
+
+	query = createPQExpBuffer();
+
+	if (!conn)
+		pg_log_info("considering PATTERN as NAME for --exclude-database option as no db connection while doing pg_restore.");
+
+	/*
+	 * Process one by one all dbnames and if specified to skip restoring, then
+	 * remove dbname from list.
+	 */
+	while (dboid_cell != NULL)
+	{
+		bool						skip_db_restore = false;
+		SimpleDatabaseOidListCell	*next = dboid_cell->next;
+
+		for (SimpleStringListCell *celldb = db_exclude_patterns.head; celldb; celldb = celldb->next)
+		{
+			/*
+			 * the construct pattern matching query:
+			 * SELECT 1 WHERE XXX OPERATOR(pg_catalog.~) '^(PATTERN)$' COLLATE
+			 * pg_catalog.default
+			 *
+			 * XXX represents the string literal database name derived from the
+			 * dbname_oid_list, which is initially extracted from the map.dat
+			 * file located in the backup directory.  that's why we need
+			 * quote_literal_cstr.
+			 *
+			 * If no db connection, then consider PATTERN as NAME.
+			 */
+			if (pg_strcasecmp(dboid_cell->db_name, celldb->val) == 0)
+			   skip_db_restore = true;
+			else if (conn)
+			{
+				int	dotcnt;
+
+				appendPQExpBufferStr(query, "SELECT 1 ");
+				processSQLNamePattern(conn, query, celldb->val, false,
+									  false, NULL, quote_literal_cstr(dboid_cell->db_name),
+									  NULL, NULL, NULL, &dotcnt);
+
+				if (dotcnt > 0)
+				{
+					pg_log_error("improper qualified name (too many dotted names): %s",
+								  celldb->val);
+					PQfinish(conn);
+					exit_nicely(1);
+				}
+
+				res = executeQuery(conn, query->data);
+
+				if ((PQresultStatus(res) == PGRES_TUPLES_OK) && PQntuples(res))
+				{
+					skip_db_restore = true;
+					pg_log_info("database \"%s\" is matching with exclude pattern: \"%s\"", dboid_cell->db_name, celldb->val);
+				}
+
+				PQclear(res);
+				resetPQExpBuffer(query);
+			}
+
+			if (skip_db_restore)
+				break;
+		}
+
+		/* Increment count if database needs to be restored. */
+		if (skip_db_restore)
+		{
+			pg_log_info("excluding database \"%s\"", dboid_cell->db_name);
+			simple_db_oid_list_delete(dbname_oid_list, dboid_cell, dboidprecell);
+		}
+		else
+		{
+			count_db++;
+			dboidprecell = dboid_cell;
+		}
+
+		/* Process next dbname from dbname list. */
+		dboid_cell = next;
+	}
+
+	return count_db;
+}
+
+/*
+ * get_dbname_oid_list_from_mfile
+ *
+ * Open map.dat file and read line by line and then prepare a list of database
+ * names and corresponding db_oid.
+ *
+ * Returns, total number of database names in map.dat file.
+ */
+static int
+get_dbname_oid_list_from_mfile(const char *dumpdirpath, SimpleDatabaseOidList *dbname_oid_list)
+{
+	FILE    *pfile;
+	char    map_file_path[MAXPGPATH];
+	char    line[MAXPGPATH];
+	int     count = 0;
+
+	/*
+	 * If there is only global.dat file in dump, then return from here as there
+	 * is no database to restore.
+	 */
+	if (!IsFileExistsInDirectory(pg_strdup(dumpdirpath), "map.dat"))
+	{
+		pg_log_info("databases restoring is skipped as map.dat file is not present in \"%s\"", dumpdirpath);
+		return 0;
+	}
+
+	snprintf(map_file_path, MAXPGPATH, "%s/map.dat", dumpdirpath);
+
+	/* Open map.dat file. */
+	pfile = fopen(map_file_path, PG_BINARY_R);
+
+	if (pfile == NULL)
+		pg_fatal("could not open map.dat file: \"%s\"", map_file_path);
+
+	/* Append all the dbname and db_oid to the list. */
+	while((fgets(line, MAXPGPATH, pfile)) != NULL)
+	{
+		Oid         db_oid = InvalidOid;
+		char		db_oid_str[MAXPGPATH + 1] = {'\0'};
+		char        dbname[MAXPGPATH + 1] = {'\0'};
+
+		/* Extract dboid. */
+		sscanf(line, "%u" , &db_oid);
+		sscanf(line, "%s" , db_oid_str);
+
+		/* Now copy dbname. */
+		strcpy(dbname, line + strlen(db_oid_str) + 1);
+
+		/* Remove \n from dbanme. */
+		dbname[strlen(dbname) - 1] = '\0';
+
+		pg_log_info("found database \"%s\" (OID: %u) in map.dat file while restoring.", dbname, db_oid);
+
+		/* Report error and exit if the file has any corrupted data. */
+		if (!OidIsValid(db_oid) || strlen(dbname) == 0)
+			pg_fatal("invalid entry in map.dat file at line : %d", count + 1);
+
+		/*
+		 * XXX : before adding dbname into list, we can verify that this db
+		 * needs to skipped for restore or not but as of now, we are making
+		 * a list of all the databases.
+		 */
+		simple_db_oid_list_append(dbname_oid_list, db_oid, dbname);
+		count++;
+	}
+
+	/* Close map.dat file. */
+	fclose(pfile);
+
+	return count;
+}
+
+/*
+ * restoreAllDatabases
+ *
+ * This will restore databases those dumps are present in
+ * directory based on map.dat file mapping.
+ *
+ * This will skip restoring for databases that are specified with
+ * exclude-database option.
+ *
+ * returns, number of errors while doing restore.
+ */
+static int
+restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
+					SimpleStringList db_exclude_patterns, RestoreOptions *opts,
+					int numWorkers)
+{
+	SimpleDatabaseOidList			dbname_oid_list = {NULL, NULL};
+	SimpleDatabaseOidListCell		*dboid_cell;
+	int								num_db_restore = 0;
+	int								num_total_db;
+	int								n_errors_total;
+	int								count = 0;
+
+	num_total_db = get_dbname_oid_list_from_mfile(dumpdirpath, &dbname_oid_list);
+
+	/*
+	 * If map.dat has no entry, return from here after processing
+	 * global.dat file.
+	 */
+	if (dbname_oid_list.head == NULL)
+		return process_global_sql_commands(conn, dumpdirpath, opts->filename);
+
+	pg_log_info("found total %d database names in map.dat file", num_total_db);
+
+	if (!conn)
+	{
+		pg_log_info("trying to connect database \"postgres\"  to dump into out file");
+
+		conn = ConnectDatabase("postgres", NULL, opts->cparams.pghost,
+							   opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT,
+							   false, progname, NULL, NULL, NULL, NULL);
+
+		/* Try with template1. */
+		if (!conn)
+		{
+			pg_log_info("trying to connect database \"template1\" as failed to connect to database \"postgres\" to dump into out file");
+
+			conn = ConnectDatabase("template1", NULL, opts->cparams.pghost,
+								   opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT,
+								   false, progname, NULL, NULL, NULL, NULL);
+		}
+	}
+
+	/*
+	 * processing pg_retsore --exclude-database=PATTERN/NAME if no connection.
+	 */
+	num_db_restore = get_dbnames_list_to_restore(conn, &dbname_oid_list,
+			db_exclude_patterns);
+
+	/* Open global.dat file and execute/append all the global sql commands. */
+	n_errors_total = process_global_sql_commands(conn, dumpdirpath, opts->filename);
+
+	/* Close the db connection as we are done with globals and patterns. */
+	if (conn)
+		PQfinish(conn);
+
+	/* Exit if no db needs to be restored. */
+	if (dbname_oid_list.head == NULL)
+	{
+		pg_log_info("no database needs to restore out of %d databases", num_total_db);
+		return n_errors_total;
+	}
+
+	pg_log_info("needs to restore %d databases out of %d databases", num_db_restore, num_total_db);
+
+	/*
+	 * Till now, we made a list of databases, those needs to be restored
+	 * after skipping names of exclude-database.  Now we can launch parallel
+	 * workers to restore these databases.
+	 */
+	dboid_cell = dbname_oid_list.head;
+
+	while(dboid_cell != NULL)
+	{
+		char		subdirpath[MAXPGPATH];
+		int			n_errors;
+
+		/*
+		 * We need to reset override_dbname so that objects can be restored into
+		 * already created database. (used with -d/--dbname option)
+		 */
+		if (opts->cparams.override_dbname)
+		{
+			pfree(opts->cparams.override_dbname);
+			opts->cparams.override_dbname = NULL;
+		}
+
+		snprintf(subdirpath, MAXPGPATH, "%s/databases/%u", dumpdirpath, dboid_cell->db_oid);
+
+		pg_log_info("restoring database \"%s\"", dboid_cell->db_name);
+
+		/* Restore single database. */
+		n_errors = restoreOneDatabase(subdirpath, opts, numWorkers, true, count);
+
+		/* Print a summary of ignored errors during single database restore. */
+		if (n_errors)
+		{
+			n_errors_total += n_errors;
+			pg_log_warning("errors ignored on database \"%s\" restore: %d", dboid_cell->db_name, n_errors);
+		}
+
+		dboid_cell = dboid_cell->next;
+		count++;
+	}
+
+	/* Log number of processed databases.*/
+	pg_log_info("number of restored databases are %d", num_db_restore);
+
+	/* Free dbname and dboid list. */
+	simple_db_oid_full_list_delete(&dbname_oid_list);
+
+	return n_errors_total;
+}
+
+/*
+ * process_global_sql_commands
+ *
+ * This will open global.dat file and will execute all global sql commands one
+ * by one statement.
+ * Semicolon is considered as statement terminator.  If outfile is passed, then
+ * this will copy all sql commands into outfile rather then executing them.
+ *
+ * returns the number of errors while processing global.dat
+ */
+static int
+process_global_sql_commands(PGconn *conn, const char *dumpdirpath, const char *outfile)
+{
+	char            global_file_path[MAXPGPATH];
+	PGresult		*result;
+	StringInfoData	sqlstatement;
+	FILE			*pfile;
+	int				n_errors = 0;
+
+	snprintf(global_file_path, MAXPGPATH, "%s/global.dat", dumpdirpath);
+
+	/* Open global.dat file. */
+	pfile = fopen(global_file_path, PG_BINARY_R);
+
+	if (pfile == NULL)
+		pg_fatal("could not open global.dat file: \"%s\"", global_file_path);
+
+	/*
+	 * If outfile is given, then just copy all global.dat file data into
+	 * outfile.
+	 */
+	if (outfile)
+	{
+		copy_or_print_global_file(outfile, pfile);
+		return 0;
+	}
+
+	/* Init sqlstatement to append commands. */
+	initStringInfo(&sqlstatement);
+
+	/* Process file till EOF and execute sql statements. */
+	while (ReadOneStatement(&sqlstatement, pfile) != EOF)
+	{
+		pg_log_info("executing query: %s", sqlstatement.data);
+		result = PQexec(conn, sqlstatement.data);
+
+		switch (PQresultStatus(result))
+		{
+			case PGRES_COMMAND_OK:
+			case PGRES_TUPLES_OK:
+			case PGRES_EMPTY_QUERY:
+				break;
+			default:
+				n_errors++;
+				pg_log_error("could not execute query: \"%s\" \nCommand was: \"%s\"", PQerrorMessage(conn), sqlstatement.data);
+		}
+		PQclear(result);
+	}
+
+	/* Print a summary of ignored errors during global.dat. */
+	if (n_errors)
+		pg_log_warning("errors ignored on global.dat file restore: %d", n_errors);
+
+	fclose(pfile);
+
+	return n_errors;
+}
+
+/*
+ * copy_or_print_global_file
+ *
+ * This will copy global.dat file into out file.  If "-" is used as outfile,
+ * then print commands to the stdout.
+ */
+static void
+copy_or_print_global_file(const char *outfile, FILE *pfile)
+{
+	char	out_file_path[MAXPGPATH];
+	FILE	*OPF;
+	int		c;
+
+	/* "-" is used for stdout. */
+	if (strcmp(outfile, "-") == 0)
+		OPF = stdout;
+	else
+	{
+		snprintf(out_file_path, MAXPGPATH, "%s", outfile);
+		OPF = fopen(out_file_path, PG_BINARY_W);
+
+		if (OPF == NULL)
+		{
+			fclose(pfile);
+			pg_fatal("could not open file: \"%s\"", outfile);
+		}
+	}
+
+	/* Append global.dat into out file or print to the stdout. */
+	while ((c = fgetc(pfile)) != EOF)
+		fputc(c, OPF);
+
+	fclose(pfile);
+
+	/* Close out file. */
+	if (strcmp(outfile, "-") != 0)
+		fclose(OPF);
+}
+
+/*
+ * simple_db_oid_list_append
+ *
+ * appends a node to the list in the end.
+ */
+static void
+simple_db_oid_list_append(SimpleDatabaseOidList *list, Oid db_oid,
+		const char *dbname)
+{
+	SimpleDatabaseOidListCell *cell;
+
+	cell = pg_malloc_object(SimpleDatabaseOidListCell);
+
+	cell->next = NULL;
+	cell->db_oid = db_oid;
+	cell->db_name = pg_strdup(dbname);
+
+	if (list->tail)
+		list->tail->next = cell;
+	else
+		list->head = cell;
+	list->tail = cell;
+}
+
+/*
+ * simple_db_oid_full_list_delete
+ *
+ * delete all cell from dbname and dboid list.
+ */
+static void
+simple_db_oid_full_list_delete(SimpleDatabaseOidList *list)
+{
+	SimpleDatabaseOidListCell	*cell = list->head;
+	SimpleDatabaseOidListCell	*nextcell = NULL;
+
+	while (cell)
+	{
+		nextcell = cell->next;
+		pfree (cell);
+		cell = nextcell;
+	}
+
+	list->head = NULL;
+	list->tail = NULL;
+}
+
+/*
+ * simple_string_full_list_delete
+ *
+ * delete all cell from string list.
+ */
+static void
+simple_string_full_list_delete(SimpleStringList *list)
+{
+	SimpleStringListCell	*cell = list->head;
+	SimpleStringListCell    *cellnext = NULL;
+
+	while (cell)
+	{
+		cellnext = cell->next;
+		pfree(cell);
+		cell = cellnext;
+	}
+
+	list->head = NULL;
+	list->tail = NULL;
+}
+
+/*
+ * simple_db_oid_list_delete
+ *
+ * delete cell from database and oid list.
+ */
+static void
+simple_db_oid_list_delete(SimpleDatabaseOidList *list,
+		SimpleDatabaseOidListCell *cell,
+		SimpleDatabaseOidListCell *prev)
+{
+	if (prev == NULL)
+	{
+		list->head = cell->next;
+		pfree(cell);
+	}
+	else
+	{
+		prev->next = cell->next;
+		pfree(cell);
+	}
+}
+
+/*
+ * quote_literal_internal
+ */
+static size_t
+quote_literal_internal(char *dst, const char *src, size_t len)
+{
+	const char *s;
+	char	   *savedst = dst;
+
+	for (s = src; s < src + len; s++)
+	{
+		if (*s == '\\')
+		{
+			*dst++ = ESCAPE_STRING_SYNTAX;
+			break;
+		}
+	}
+
+	*dst++ = '\'';
+	while (len-- > 0)
+	{
+		if (SQL_STR_DOUBLE(*src, true))
+			*dst++ = *src;
+		*dst++ = *src++;
+	}
+	*dst++ = '\'';
+
+	return dst - savedst;
+}
+
+/*
+ * quote_literal_cstr
+ *
+ *	  returns a properly quoted literal
+ * copied from src/backend/utils/adt/quote.c
+ */
+static char *
+quote_literal_cstr(const char *rawstr)
+{
+	char	   *result;
+	int			len;
+	int			newlen;
+
+	len = strlen(rawstr);
+
+	/* We make a worst-case result area; wasting a little space is OK */
+	result = pg_malloc(len * 2 + 3 + 1);
+
+	newlen = quote_literal_internal(result, rawstr, len);
+	result[newlen] = '\0';
+
+	return result;
+}
diff --git a/src/bin/pg_dump/t/001_basic.pl b/src/bin/pg_dump/t/001_basic.pl
old mode 100644
new mode 100755
index 37d893d5e6a..0bbcdbe84a7
--- a/src/bin/pg_dump/t/001_basic.pl
+++ b/src/bin/pg_dump/t/001_basic.pl
@@ -237,6 +237,11 @@ command_fails_like(
 	'pg_restore: options -C\/--create and -1\/--single-transaction cannot be used together'
 );
 
+command_fails_like(
+	[ 'pg_restore', '--exclude-database=foo', '--globals-only', '-d', 'xxx' ],
+	qr/\Qpg_restore: error: option --exclude-database cannot be used together with -g\/--globals-only\E/,
+	'pg_restore: option --exclude-database cannot be used together with -g/--globals-only');
+
 # also fails for -r and -t, but it seems pointless to add more tests for those.
 command_fails_like(
 	[ 'pg_dumpall', '--exclude-database=foo', '--globals-only' ],
@@ -244,4 +249,8 @@ command_fails_like(
 	'pg_dumpall: option --exclude-database cannot be used together with -g/--globals-only'
 );
 
+command_fails_like(
+	[ 'pg_dumpall', '--format', 'x' ],
+	qr/\Qpg_dumpall: error: unrecognized archive format "x";\E/,
+	'pg_dumpall: unrecognized archive format');
 done_testing();
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 3fbf5a4c212..f3999ee3f9d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2726,6 +2726,8 @@ ShutdownMode
 SignTSVector
 SimpleActionList
 SimpleActionListCell
+SimpleDatabaseOidList
+SimpleDatabaseOidListCell
 SimpleEcontextStackEntry
 SimpleOidList
 SimpleOidListCell
-- 
2.34.1

From bfc7c37284cf38ecad18b79c6e08a9fda06512eb Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <[email-masked]>
Date: Fri, 28 Mar 2025 18:10:24 -0400
Subject: [PATCH 3/4] add new list type simple_oid_string_list to
 fe-utils/simple_list

---
 src/fe_utils/simple_list.c         | 41 ++++++++++++++++++++++++++++++
 src/include/fe_utils/simple_list.h | 16 ++++++++++++
 2 files changed, 57 insertions(+)

diff --git a/src/fe_utils/simple_list.c b/src/fe_utils/simple_list.c
index 483d5455594..b0686e57c4a 100644
--- a/src/fe_utils/simple_list.c
+++ b/src/fe_utils/simple_list.c
@@ -192,3 +192,44 @@ simple_ptr_list_destroy(SimplePtrList *list)
 		cell = next;
 	}
 }
+
+/*
+ * Add to an oid_string list
+ */
+void
+simple_oid_string_list_append(SimpleOidStringList *list, Oid oid, const char *str)
+{
+	SimpleOidStringListCell *cell;
+
+	cell = (SimpleOidStringListCell *)
+		pg_malloc(offsetof(SimpleOidStringListCell, str) + strlen(str) + 1);
+
+	cell->next = NULL;
+	cell->oid = oid;
+	strcpy(cell->str, str);
+
+	if (list->tail)
+		list->tail->next = cell;
+	else
+		list->head = cell;
+	list->tail = cell;
+}
+
+/*
+ * Destroy an oid_string list
+ */
+void
+simple_oid_string_list_destroy(SimpleOidStringList *list)
+{
+	SimpleOidStringListCell *cell;
+
+	cell = list->head;
+	while (cell != NULL)
+	{
+		SimpleOidStringListCell *next;
+
+		next = cell->next;
+		pg_free(cell);
+		cell = next;
+	}
+}
diff --git a/src/include/fe_utils/simple_list.h b/src/include/fe_utils/simple_list.h
index 3b8e38414ec..d5492408d6c 100644
--- a/src/include/fe_utils/simple_list.h
+++ b/src/include/fe_utils/simple_list.h
@@ -55,6 +55,19 @@ typedef struct SimplePtrList
 	SimplePtrListCell *tail;
 } SimplePtrList;
 
+typedef struct SimpleOidStringListCell
+{
+	struct SimpleOidStringListCell *next;
+	Oid		    oid;
+	char		str[FLEXIBLE_ARRAY_MEMBER]; /* null-terminated string here */
+} SimpleOidStringListCell;
+
+typedef struct SimpleOidStringList
+{
+	SimpleOidStringListCell *head;
+	SimpleOidStringListCell *tail;
+} SimpleOidStringList;
+
 extern void simple_oid_list_append(SimpleOidList *list, Oid val);
 extern bool simple_oid_list_member(SimpleOidList *list, Oid val);
 extern void simple_oid_list_destroy(SimpleOidList *list);
@@ -68,4 +81,7 @@ extern const char *simple_string_list_not_touched(SimpleStringList *list);
 extern void simple_ptr_list_append(SimplePtrList *list, void *ptr);
 extern void simple_ptr_list_destroy(SimplePtrList *list);
 
+extern void simple_oid_string_list_append(SimpleOidStringList *list, Oid oid, const char *str);
+extern void simple_oid_string_list_destroy(SimpleOidStringList *list);
+
 #endif							/* SIMPLE_LIST_H */
-- 
2.34.1

From b51df1f2584f99d67381a3702cec87cb3264e4a3 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <[email-masked]>
Date: Fri, 28 Mar 2025 18:10:57 -0400
Subject: [PATCH 4/4] cleanups and use new simple list type

---
 src/bin/pg_dump/pg_dumpall.c |  28 ++---
 src/bin/pg_dump/pg_restore.c | 208 +++++++----------------------------
 2 files changed, 56 insertions(+), 180 deletions(-)

 	/*

 
 
 		/*

 
 		if (filename)
 		{

 
 
 	/*
index e4093427e2f..44a24791a6e 100644
 
@@ -55,47 +53,24 @@
 
-typedef struct SimpleDatabaseOidListCell
-{
-	struct SimpleDatabaseOidListCell	*next;
-	Oid									db_oid;
-	const char							*db_name;
-} SimpleDatabaseOidListCell;
-
-typedef struct SimpleDatabaseOidList
-{
-	SimpleDatabaseOidListCell *head;
-	SimpleDatabaseOidListCell *tail;
-} SimpleDatabaseOidList;
-
 static void usage(const char *progname);
 static void read_restore_filters(const char *filename, RestoreOptions *opts);
-static bool IsFileExistsInDirectory(const char *dir, const char *filename);
+static bool file_exists_in_directory(const char *dir, const char *filename);
 static int restoreOneDatabase(const char *inputFileSpec, RestoreOptions *opts,
 							  int numWorkers, bool append_data, int num);
-static int ReadOneStatement(StringInfo inBuf, FILE *pfile);
+static int read_one_statement(StringInfo inBuf, FILE *pfile);
 static int restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
 							   SimpleStringList db_exclude_patterns, RestoreOptions *opts, int numWorkers);
 static int process_global_sql_commands(PGconn *conn, const char *dumpdirpath,
 										const char *outfile);
 static void copy_or_print_global_file(const char *outfile, FILE *pfile);
 static int get_dbnames_list_to_restore(PGconn *conn,
-		SimpleDatabaseOidList *dbname_oid_list,
+		SimpleOidStringList *dbname_oid_list,
 		SimpleStringList db_exclude_patterns);
 static int get_dbname_oid_list_from_mfile(const char *dumpdirpath,
-										  SimpleDatabaseOidList *dbname_oid_list);
-static void simple_db_oid_list_append(SimpleDatabaseOidList *list, Oid db_oid,
-									  const char *dbname);
-static void simple_string_full_list_delete(SimpleStringList *list);
-static void simple_db_oid_full_list_delete(SimpleDatabaseOidList *list);
-static void simple_db_oid_list_delete(SimpleDatabaseOidList *list,
-									  SimpleDatabaseOidListCell *cell,
-									  SimpleDatabaseOidListCell *prev);
-static void simple_db_oid_list_append(SimpleDatabaseOidList *list,
-		Oid db_oid, const char *dbname);
+										  SimpleOidStringList *dbname_oid_list);
 static size_t quote_literal_internal(char *dst, const char *src, size_t len);
 static char *quote_literal_cstr(const char *rawstr);
 static int on_exit_index = 0;
@@ -521,8 +496,8 @@ main(int argc, char **argv)
 	 * databases from map.dat(if exist) file list and skip restoring for
 	 * --exclude-database patterns.
 	 */
-	if (inputFileSpec != NULL && !IsFileExistsInDirectory(inputFileSpec, "toc.dat") &&
-			IsFileExistsInDirectory(inputFileSpec, "global.dat"))
+	if (inputFileSpec != NULL && !file_exists_in_directory(inputFileSpec, "toc.dat") &&
+			file_exists_in_directory(inputFileSpec, "global.dat"))
 	{
 		PGconn  *conn = NULL; /* Connection to restore global sql commands. */
 
@@ -578,7 +553,7 @@ main(int argc, char **argv)
 		}
 
@@ -847,12 +822,12 @@ read_restore_filters(const char *filename, RestoreOptions *opts)
 }
 
 /*
@@ -864,7 +839,7 @@ IsFileExistsInDirectory(const char *dir, const char *filename)
 }
 
 /*
- * ReadOneStatement
+ * read_one_statement
  *
  * This will start reading from passed file pointer using fgetc and read till
  * semicolon(sql statement terminator for global.dat file)
@@ -873,7 +848,7 @@ IsFileExistsInDirectory(const char *dir, const char *filename)
  */
 
 static int
-ReadOneStatement(StringInfo inBuf, FILE *pfile)
+read_one_statement(StringInfo inBuf, FILE *pfile)
 {
 	int			c; /* character read from getc() */
 	int			m;
@@ -941,27 +916,21 @@ ReadOneStatement(StringInfo inBuf, FILE *pfile)
 /*
  * get_dbnames_list_to_restore
  *
- * This will remove entries from dbname_oid_list that pattern matching any
- * in the db_exclude_patterns list.  dbname_oid_list maybe inplace modified.
+ * This will mark for skipping any entries from dbname_oid_list that pattern match an
+ * entry in the db_exclude_patterns list.
  *
- * returns, number of database will be restored.
+ * Returns the number of database to be restored.
  *
  */
 static int
 get_dbnames_list_to_restore(PGconn *conn,
-		SimpleDatabaseOidList *dbname_oid_list,
+		SimpleOidStringList *dbname_oid_list,
 		SimpleStringList db_exclude_patterns)
 {
-	SimpleDatabaseOidListCell	*dboid_cell = dbname_oid_list->head;
-	SimpleDatabaseOidListCell	*dboidprecell = NULL;
 	int							count_db = 0;
 	PQExpBuffer query;
 	PGresult   *res;
 
-	/* Return 0 if there is no database to restore. */
-	if (dboid_cell == NULL)
-		return 0;
-
 	query = createPQExpBuffer();
 
 	if (!conn)
@@ -971,12 +940,12 @@ get_dbnames_list_to_restore(PGconn *conn,
 	 * Process one by one all dbnames and if specified to skip restoring, then
 	 * remove dbname from list.
 	 */
-	while (dboid_cell != NULL)
+	for (SimpleOidStringListCell *db_cell = dbname_oid_list->head;
+		 db_cell; db_cell = db_cell->next)
 	{
 		bool						skip_db_restore = false;
-		SimpleDatabaseOidListCell	*next = dboid_cell->next;
 
-		for (SimpleStringListCell *celldb = db_exclude_patterns.head; celldb; celldb = celldb->next)
+		for (SimpleStringListCell *pat_cell = db_exclude_patterns.head; pat_cell; pat_cell = pat_cell->next)
 		{
 			/*
 			 * the construct pattern matching query:
@@ -990,21 +959,21 @@ get_dbnames_list_to_restore(PGconn *conn,
 			 *
 			 * If no db connection, then consider PATTERN as NAME.
 			 */
-			if (pg_strcasecmp(dboid_cell->db_name, celldb->val) == 0)
+			if (pg_strcasecmp(db_cell->str, pat_cell->val) == 0)
 			   skip_db_restore = true;
 			else if (conn)
 			{
 				int	dotcnt;
 
 				appendPQExpBufferStr(query, "SELECT 1 ");
-				processSQLNamePattern(conn, query, celldb->val, false,
-									  false, NULL, quote_literal_cstr(dboid_cell->db_name),
+				processSQLNamePattern(conn, query, pat_cell->val, false,
+									  false, NULL, quote_literal_cstr(db_cell->str),
 									  NULL, NULL, NULL, &dotcnt);
 
 				if (dotcnt > 0)
 				{
 					pg_log_error("improper qualified name (too many dotted names): %s",
-								  celldb->val);
+								  db_cell->str);
 					PQfinish(conn);
 					exit_nicely(1);
 				}
@@ -1014,7 +983,7 @@ get_dbnames_list_to_restore(PGconn *conn,
 				if ((PQresultStatus(res) == PGRES_TUPLES_OK) && PQntuples(res))
 				{
 					skip_db_restore = true;
-					pg_log_info("database \"%s\" is matching with exclude pattern: \"%s\"", dboid_cell->db_name, celldb->val);
+					pg_log_info("database \"%s\" matches exclude pattern: \"%s\"", db_cell->str, pat_cell->val);
 				}
 
 				PQclear(res);
@@ -1028,17 +997,13 @@ get_dbnames_list_to_restore(PGconn *conn,
 		/* Increment count if database needs to be restored. */
 		if (skip_db_restore)
 		{
-			pg_log_info("excluding database \"%s\"", dboid_cell->db_name);
-			simple_db_oid_list_delete(dbname_oid_list, dboid_cell, dboidprecell);
+			pg_log_info("excluding database \"%s\"", db_cell->str);
+			db_cell->oid = InvalidOid;
 		}
 		else
 		{
 			count_db++;
-			dboidprecell = dboid_cell;
 		}
-
-		/* Process next dbname from dbname list. */
-		dboid_cell = next;
 	}
 
 	return count_db;
@@ -1053,7 +1018,7 @@ get_dbnames_list_to_restore(PGconn *conn,
  * Returns, total number of database names in map.dat file.
  */
 static int
-get_dbname_oid_list_from_mfile(const char *dumpdirpath, SimpleDatabaseOidList *dbname_oid_list)
+get_dbname_oid_list_from_mfile(const char *dumpdirpath, SimpleOidStringList *dbname_oid_list)
 {
 	FILE    *pfile;
 	char    map_file_path[MAXPGPATH];
@@ -1064,7 +1029,7 @@ get_dbname_oid_list_from_mfile(const char *dumpdirpath, SimpleDatabaseOidList *d
 	 * If there is only global.dat file in dump, then return from here as there
 	 * is no database to restore.
 	 */
-	if (!IsFileExistsInDirectory(pg_strdup(dumpdirpath), "map.dat"))
+	if (!file_exists_in_directory(dumpdirpath, "map.dat"))
 	{
 		pg_log_info("databases restoring is skipped as map.dat file is not present in \"%s\"", dumpdirpath);
 		return 0;
@@ -1087,7 +1052,7 @@ get_dbname_oid_list_from_mfile(const char *dumpdirpath, SimpleDatabaseOidList *d
 
 		/* Extract dboid. */
 		sscanf(line, "%u" , &db_oid);
-		sscanf(line, "%s" , db_oid_str);
+		sscanf(line, "%20s" , db_oid_str);
 
 		/* Now copy dbname. */
 		strcpy(dbname, line + strlen(db_oid_str) + 1);
@@ -1106,7 +1071,7 @@ get_dbname_oid_list_from_mfile(const char *dumpdirpath, SimpleDatabaseOidList *d
 		 * needs to skipped for restore or not but as of now, we are making
 		 * a list of all the databases.
 		 */
-		simple_db_oid_list_append(dbname_oid_list, db_oid, dbname);
+		simple_oid_string_list_append(dbname_oid_list, db_oid, dbname);
 		count++;
 	}
 
@@ -1132,8 +1097,7 @@ restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
 					SimpleStringList db_exclude_patterns, RestoreOptions *opts,
 					int numWorkers)
 {
-	SimpleDatabaseOidList			dbname_oid_list = {NULL, NULL};
-	SimpleDatabaseOidListCell		*dboid_cell;
+	SimpleOidStringList			    dbname_oid_list = {NULL, NULL};
 	int								num_db_restore = 0;
 	int								num_total_db;
 	int								n_errors_total;
@@ -1183,7 +1147,7 @@ restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
 		PQfinish(conn);
 
 	/* Exit if no db needs to be restored. */
-	if (dbname_oid_list.head == NULL)
+	if (dbname_oid_list.head == NULL || num_db_restore == 0)
 	{
 		pg_log_info("no database needs to restore out of %d databases", num_total_db);
 		return n_errors_total;
@@ -1196,13 +1160,16 @@ restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
 	 * after skipping names of exclude-database.  Now we can launch parallel
 	 * workers to restore these databases.
 	 */
-	dboid_cell = dbname_oid_list.head;
-
-	while(dboid_cell != NULL)
+	for (SimpleOidStringListCell *db_cell = dbname_oid_list.head;
+		 db_cell; db_cell = db_cell->next)
 	{
 		char		subdirpath[MAXPGPATH];
 		int			n_errors;
 
+		/* ignore dbs marked for skipping */
+		if (db_cell->oid == InvalidOid)
+			continue;
+
 		/*
 		 * We need to reset override_dbname so that objects can be restored into
 		 * already created database. (used with -d/--dbname option)
@@ -1213,9 +1180,9 @@ restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
 			opts->cparams.override_dbname = NULL;
 		}
 
-		snprintf(subdirpath, MAXPGPATH, "%s/databases/%u", dumpdirpath, dboid_cell->db_oid);
+		snprintf(subdirpath, MAXPGPATH, "%s/databases/%u", dumpdirpath, db_cell->oid);
 
-		pg_log_info("restoring database \"%s\"", dboid_cell->db_name);
+		pg_log_info("restoring database \"%s\"", db_cell->str);
 
 		/* Restore single database. */
 		n_errors = restoreOneDatabase(subdirpath, opts, numWorkers, true, count);
@@ -1224,10 +1191,9 @@ restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
 		if (n_errors)
 		{
 			n_errors_total += n_errors;
-			pg_log_warning("errors ignored on database \"%s\" restore: %d", dboid_cell->db_name, n_errors);
+			pg_log_warning("errors ignored on database \"%s\" restore: %d", db_cell->str, n_errors);
 		}
 
-		dboid_cell = dboid_cell->next;
 		count++;
 	}
 
@@ -1235,7 +1201,7 @@ restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
 	pg_log_info("number of restored databases are %d", num_db_restore);
 
 	/* Free dbname and dboid list. */
-	simple_db_oid_full_list_delete(&dbname_oid_list);
+	simple_oid_string_list_destroy(&dbname_oid_list);
 
 	return n_errors_total;
 }
@@ -1281,7 +1247,7 @@ process_global_sql_commands(PGconn *conn, const char *dumpdirpath, const char *o
 	initStringInfo(&sqlstatement);
 
@@ -1347,96 +1313,6 @@ copy_or_print_global_file(const char *outfile, FILE *pfile)
 		fclose(OPF);
 }
 
-/*
- * simple_db_oid_list_append
- *
- * appends a node to the list in the end.
- */
-static void
-simple_db_oid_list_append(SimpleDatabaseOidList *list, Oid db_oid,
-		const char *dbname)
-{
-	SimpleDatabaseOidListCell *cell;
-
-	cell = pg_malloc_object(SimpleDatabaseOidListCell);
-
-	cell->next = NULL;
-	cell->db_oid = db_oid;
-	cell->db_name = pg_strdup(dbname);
-
-	if (list->tail)
-		list->tail->next = cell;
-	else
-		list->head = cell;
-	list->tail = cell;
-}
-
-/*
- * simple_db_oid_full_list_delete
- *
- * delete all cell from dbname and dboid list.
- */
-static void
-simple_db_oid_full_list_delete(SimpleDatabaseOidList *list)
-{
-	SimpleDatabaseOidListCell	*cell = list->head;
-	SimpleDatabaseOidListCell	*nextcell = NULL;
-
-	while (cell)
-	{
-		nextcell = cell->next;
-		pfree (cell);
-		cell = nextcell;
-	}
-
-	list->head = NULL;
-	list->tail = NULL;
-}
-
-/*
- * simple_string_full_list_delete
- *
- * delete all cell from string list.
- */
-static void
-simple_string_full_list_delete(SimpleStringList *list)
-{
-	SimpleStringListCell	*cell = list->head;
-	SimpleStringListCell    *cellnext = NULL;
-
-	while (cell)
-	{
-		cellnext = cell->next;
-		pfree(cell);
-		cell = cellnext;
-	}
-
-	list->head = NULL;
-	list->tail = NULL;
-}
-
-/*
- * simple_db_oid_list_delete
- *
- * delete cell from database and oid list.
- */
-static void
-simple_db_oid_list_delete(SimpleDatabaseOidList *list,
-		SimpleDatabaseOidListCell *cell,
-		SimpleDatabaseOidListCell *prev)
-{
-	if (prev == NULL)
-	{
-		list->head = cell->next;
-		pfree(cell);
-	}
-	else
-	{
-		prev->next = cell->next;
-		pfree(cell);
-	}
-}
-
 /*
  * quote_literal_internal
  */
-- 
2.34.1

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Sat, 29 Mar 2025 10:47:21 +0530

> going to work on that tomorrow - I don't think it will be terribly

Thanks Andrew.

Here, I am attaching a delta patch for oid.tar and oid.dmp format.

---

From: Andrew Dunstan <[email-masked]>
Date: Sun, 30 Mar 2025 12:50:51 -0400

> Here, I am attaching a delta patch for oid.tar and oid.dmp format.

OK, looks good, I have incorporated that.

There are a couple of rough edges, though.

First, I see this:

andrew@ub22arm:inst $ bin/pg_restore -C -d postgres 
--exclude-database=regression_dummy_seclabel 
--exclude-database=regression_test_extensions 
--exclude-database=regression_test_pg_dump dest
pg_restore: error: could not execute query: "ERROR:  role "andrew" 
already exists
"
Command was: "

--
-- Roles
--

CREATE ROLE andrew;"
pg_restore: warning: errors ignored on global.dat file restore: 1
pg_restore: error: could not execute query: ERROR:  database "template1" 
already exists
Command was: CREATE DATABASE template1 WITH TEMPLATE = template0 
ENCODING = 'SQL_ASCII' LOCALE_PROVIDER = libc LOCALE = 'C';

pg_restore: warning: errors ignored on database "template1" restore: 1
pg_restore: error: could not execute query: ERROR:  database "postgres" 
already exists
Command was: CREATE DATABASE postgres WITH TEMPLATE = template0 ENCODING 
= 'SQL_ASCII' LOCALE_PROVIDER = libc LOCALE = 'C';

pg_restore: warning: errors ignored on database "postgres" restore: 1
pg_restore: warning: errors ignored on restore: 3

It seems pointless to be trying to create the rolw that we are connected 
as, and we also expect template1 and postgres to exist.

In a similar vein, I don't see why we are setting the --create flag in 
pg_dumpall for those databases. I'm attaching a patch that is designed 
to stop that, but it doesn't solve the above issues.

I also notice a bunch of these in globals.dat:

--
-- Databases
--

--
-- Database "template1" dump
--

--
-- Database "andrew" dump
--

--
-- Database "isolation_regression_brin" dump
--

--
-- Database "isolation_regression_delay_execution" dump
--

  ...

The patch also tries to fix this.

Lastly, this badly needs some TAP tests written.

I'm going to work on reviewing the documentation next.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com
From ed53d8b5ad82e49bb56bc5bc48ddb8426fdb4c80 Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 19 Mar 2025 01:18:46 +0530
Subject: [PATCH v20250330 1/3] Move common pg_dump code related to connections
 to a new file

ConnectDatabase is used by pg_dumpall, pg_restore and pg_dump so move
common code to new file.

Author:    Mahendra Singh Thalor <[email-masked]>
---
 src/bin/pg_dump/Makefile             |   5 +-
 src/bin/pg_dump/connectdb.c          | 294 +++++++++++++++++++++++++++
 src/bin/pg_dump/connectdb.h          |  26 +++
 src/bin/pg_dump/meson.build          |   1 +
 src/bin/pg_dump/pg_backup.h          |   6 +-
 src/bin/pg_dump/pg_backup_archiver.c |   6 +-
 src/bin/pg_dump/pg_backup_db.c       |  79 +------
 src/bin/pg_dump/pg_dump.c            |   2 +-
 src/bin/pg_dump/pg_dumpall.c         | 278 +------------------------
 9 files changed, 352 insertions(+), 345 deletions(-)
 create mode 100644 src/bin/pg_dump/connectdb.c
 create mode 100644 src/bin/pg_dump/connectdb.h

 
 install: all installdirs
index 00000000000..9e593b70e81
+	 * Ok, connected successfully. If requested, remember the options used, in
+	 * the form of a connection string.
index 00000000000..6c1e1954769
index 603ba6cfbf0..25989e8f16b 100644
index 658986de6f8..49bc1ee71ef 100644
@@ -293,9 +293,9 @@ typedef void (*SetupWorkerPtrType) (Archive *AH);
 
-extern void ConnectDatabase(Archive *AHX,
-							const ConnParams *cparams,
-							bool isReconnect);
+extern void ConnectDatabaseAhx(Archive *AHX,
+							   const ConnParams *cparams,
+							   bool isReconnect);
 extern void DisconnectDatabase(Archive *AHX);
 extern PGconn *GetConnection(Archive *AHX);
 
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 1d131e5a57d..3f59f8f9d9d 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -415,7 +415,7 @@ RestoreArchive(Archive *AHX)
 		AHX->minRemoteVersion = 0;
 		AHX->maxRemoteVersion = 9999999;

 		/*
@@ -4458,7 +4458,7 @@ restore_toc_entries_postfork(ArchiveHandle *AH, TocEntry *pending_list)
 
@@ -5076,7 +5076,7 @@ CloneArchive(ArchiveHandle *AH)
 
index 71c55d2466a..5c349279beb 100644

 
 	PQfinish(oldConn);
 }
@@ -105,14 +106,13 @@ ReconnectToServer(ArchiveHandle *AH, const char *dbname)
  * username never does change, so one savedPassword is sufficient.
  */
 void
-ConnectDatabase(Archive *AHX,
-				const ConnParams *cparams,
-				bool isReconnect)
+ConnectDatabaseAhx(Archive *AHX,
+				   const ConnParams *cparams,
+				   bool isReconnect)
 {
 	ArchiveHandle *AH = (ArchiveHandle *) AHX;
 	trivalue	prompt_password;
 	char	   *password;
-	bool		new_pass;
 
 	if (AH->connection)
 
-	/*
 
index 84a78625820..bfa70369c47 100644
 
 	/*

 
 
 		if (!conn)
 
 		if (!conn)
 		{
 
-/*

From 17af42d16fa7f47a7e07a0e5bb02f323b224c680 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <[email-masked]>
Date: Fri, 28 Mar 2025 18:10:24 -0400
Subject: [PATCH v20250330 2/3] add new list type simple_oid_string_list to
 fe-utils/simple_list

---

index 483d5455594..bbcc4ef618d 100644
+simple_oid_string_list_append(SimpleOidStringList * list, Oid oid, const char *str)
+simple_oid_string_list_destroy(SimpleOidStringList * list)
index 3b8e38414ec..af61545d7ff 100644

+extern void simple_oid_string_list_append(SimpleOidStringList * list, Oid oid, const char *str);
+extern void simple_oid_string_list_destroy(SimpleOidStringList * list);
+
 #endif							/* SIMPLE_LIST_H */
-- 
2.34.1

From 95ddbd6726ebb0eb573a5fbaa80aac9f5a4e7cfb Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 19 Mar 2025 01:30:12 +0530
Subject: [PATCH v20250330 3/3] pg_dumpall with directory|tar|custom format and
 restore it by pg_restore

dumps are as:

Design:

thread:

Author:    Mahendra Singh Thalor <[email-masked]>
---
 doc/src/sgml/ref/pg_dumpall.sgml     |  78 ++-
 doc/src/sgml/ref/pg_restore.sgml     |  41 +-
 src/bin/pg_dump/parallel.c           |  11 +-
 src/bin/pg_dump/pg_backup.h          |   2 +-
 src/bin/pg_dump/pg_backup_archiver.c |  20 +-
 src/bin/pg_dump/pg_backup_archiver.h |   3 +-
 src/bin/pg_dump/pg_backup_tar.c      |   2 +-
 src/bin/pg_dump/pg_backup_utils.c    |  22 +-
 src/bin/pg_dump/pg_backup_utils.h    |   3 +-
 src/bin/pg_dump/pg_dump.c            |   2 +-
 src/bin/pg_dump/pg_dumpall.c         | 284 ++++++++--
 src/bin/pg_dump/pg_restore.c         | 750 ++++++++++++++++++++++++++-
 src/bin/pg_dump/t/001_basic.pl       |   9 +
 src/tools/pgindent/typedefs.list     |   2 +
 14 files changed, 1152 insertions(+), 77 deletions(-)
 mode change 100644 => 100755 src/bin/pg_dump/t/001_basic.pl

  <refnamediv>
 
  <refsynopsisdiv>
 
+     <varlistentry>
 
   <refpurpose>

   <para>
 
        <para>
 
+     <varlistentry>
 
+     <varlistentry>
 
 /*
index 49bc1ee71ef..17d6e06ec25 100644

 
index 3f59f8f9d9d..54eb4728928 100644
 
@@ -338,9 +338,14 @@ ProcessArchiveRestoreOptions(Archive *AHX)
 		StrictNamesCheck(ropt);
 }
 
-/* Public */
+/*
+ * RestoreArchive
+ *
@@ -457,7 +462,7 @@ RestoreArchive(Archive *AHX)

@@ -1293,7 +1298,7 @@ PrintTOCSummary(Archive *AHX)
 
 	sav = SaveOutput(AH);
 	if (ropt->filename)
 
 	if (strftime(stamp_str, sizeof(stamp_str), PGDUMP_STRFTIME_FMT,
 				 localtime(&AH->createDate)) == 0)
@@ -1672,7 +1677,8 @@ archprintf(Archive *AH, const char *fmt,...)
 
 static void
 SetOutput(ArchiveHandle *AH, const char *filename,
-		  const pg_compress_specification compression_spec)
+		  const pg_compress_specification compression_spec,
+		  bool append_data)
 {
 	CompressFileHandle *CFH;
 	const char *mode;
@@ -1692,7 +1698,7 @@ SetOutput(ArchiveHandle *AH, const char *filename,
 	else
 		fn = fileno(stdout);
 
index a2064f471ed..dc045b852e9 100644

 

 

 /*
index ba042016879..1ce1077096d 100644

index bfa70369c47..d8a62736ef1 100644
 
 	CloseArchive(fout);
 
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 573a8b61a45..7ceaba27419 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -15,6 +15,7 @@
 
 #include "postgres_fe.h"
 
+#include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>

 
 		/*

 
 
+	/*
 
-	/*

 	PQfinish(conn);

 

 	/*
@@ -1595,9 +1638,34 @@ dumpDatabases(PGconn *conn)
 
+	/*
+	 * be created under the subdirectory in archive mode as per single db
+	 * pg_dump.
 
@@ -1612,6 +1680,23 @@ dumpDatabases(PGconn *conn)
 			continue;
 		}
 
+		/*
+		 * If this is not a plain format dump, then append dboid and dbname to
+		 * the map.dat file.
+		 */
+		if (archDumpFormat != archNull)
+		{
+			if (archDumpFormat == archCustom)
+				snprintf(dbfilepath, MAXPGPATH, "\"%s\"/\"%s\".dmp", db_subdir, oid);
+			else if (archDumpFormat == archTar)
+				snprintf(dbfilepath, MAXPGPATH, "\"%s\"/\"%s\".tar", db_subdir, oid);
+			else
+				snprintf(dbfilepath, MAXPGPATH, "\"%s\"/\"%s\"", db_subdir, oid);
+
+			/* Put one line entry for dboid and dbname in map file. */
+			fprintf(map_file, "%s %s\n", oid, dbname);
+		}
+
 		pg_log_info("dumping database \"%s\"", dbname);
 
@@ -1630,9 +1715,17 @@ dumpDatabases(PGconn *conn)
@@ -1641,19 +1734,30 @@ dumpDatabases(PGconn *conn)
 		if (filename)
 			fclose(OPF);

 		if (filename)
 		{
 
+	/* Close map file */
 
@@ -1663,7 +1767,8 @@ dumpDatabases(PGconn *conn)
  * Run pg_dump on dbname, with specified options.
  */
 static int
-runPgDump(const char *dbname, const char *create_opts)
+runPgDump(const char *dbname, const char *create_opts, char *dbfile,
+		  ArchiveFormat archDumpFormat)
 {
 	PQExpBufferData connstrbuf;
 	PQExpBufferData cmd;
@@ -1672,17 +1777,36 @@ runPgDump(const char *dbname, const char *create_opts)
 	initPQExpBuffer(&connstrbuf);
 	initPQExpBuffer(&cmd);

 	/*
 	 * Append the database name to the already-constructed stem of connection
@@ -1827,3 +1951,91 @@ read_dumpall_filters(const char *filename, SimpleStringList *pattern)
 
 	filter_free(&fstate);
 }
+
+/*
+ * create_or_open_dir
+ *
+		if (!is_empty)
index 47f7b0dd3a1..ce70b7e12b2 100644
@@ -41,11 +41,15 @@
 #include "postgres_fe.h"
 
 #include <ctype.h>
+#include <sys/stat.h>
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
 
@@ -53,18 +57,36 @@
 
+										SimpleOidStringList * dbname_oid_list,
+										   SimpleOidStringList * dbname_oid_list);
 
 int
@@ -90,6 +112,7 @@ main(int argc, char **argv)
@@ -144,6 +167,7 @@ main(int argc, char **argv)
 
 		{NULL, 0, NULL, 0}
 	};
@@ -172,7 +196,7 @@ main(int argc, char **argv)
 		}
 	}
 
-	while ((c = getopt_long(argc, argv, "acCd:ef:F:h:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1",
+	while ((c = getopt_long(argc, argv, "acCd:ef:F:gh:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1",
 							cmdopts, NULL)) != -1)
 	{
 		switch (c)
@@ -199,11 +223,14 @@ main(int argc, char **argv)
 				if (strlen(optarg) != 0)
 					opts->formatName = pg_strdup(optarg);
 				break;
+			case 'g':
+				/* restore only global.dat file from directory */
+				globals_only = true;
+				break;
 			case 'h':
 				if (strlen(optarg) != 0)
 					opts->cparams.pghost = pg_strdup(optarg);
 				break;
-
 			case 'j':			/* number of restore jobs */
 				if (!option_parse_int(optarg, "-j/--jobs", 1,
 									  PG_MAX_JOBS,
@@ -318,6 +345,9 @@ main(int argc, char **argv)
 					exit(1);
 				opts->exit_on_error = true;
 				break;
+			case 6:				/* database patterns to skip */
+				simple_string_list_append(&db_exclude_patterns, optarg);
+				break;
 
 			default:
 				/* getopt_long already emitted a complaint */
@@ -345,6 +375,13 @@ main(int argc, char **argv)
 	if (!opts->cparams.dbname && !opts->filename && !opts->tocSummary)
 		pg_fatal("one of -d/--dbname and -f/--file must be specified");
 
@@ -452,6 +489,114 @@ main(int argc, char **argv)
 					 opts->formatName);
 	}
 
+	/*
+		PGconn	   *conn = NULL;	/* Connection to restore global sql
+									 * commands. */
+		 * To restore multiple databases, -C (create database) option should
+		 * be specified. Even there is single database in dump, report error
+		 * because it might be possible that database hasn't created so better
+		 * we report error.
+		 * Connect to database to execute global sql commands from global.dat
+		 * file.
+			 * Open global.dat file and execute/append all the global sql
+			 * commands.
 
 	SetArchiveOptions(AH, NULL, opts);
@@ -459,9 +604,15 @@ main(int argc, char **argv)
 	/*
 	 * We don't have a connection yet but that doesn't matter. The connection
 	 * is initialized to NULL and if we terminate through exit_nicely() while
-	 * it's still NULL, the cleanup function will just be a no-op.
+	 * it's still NULL, the cleanup function will just be a no-op. If we are
+	 * restoring multiple databases, then save index of exit_nicely so that we
+	 * can use same slot for all the databases as we already closed the
+	 * previous archive by CloseArchive.
 	 */
-	on_exit_close_archive(AH);
+	if (!append_data || num == 0)
+		on_exit_index = on_exit_close_archive(AH);
+	else
+		replace_on_exit_close_archive(AH, on_exit_index);
 
@@ -481,25 +632,22 @@ main(int argc, char **argv)

 
-	return exit_code;
+	return n_errors;
 }
 
 static void
 
@@ -517,6 +665,7 @@ usage(const char *progname)
@@ -529,6 +678,7 @@ usage(const char *progname)
@@ -569,8 +719,8 @@ usage(const char *progname)
 
 	printf(_("\n"
@@ -675,3 +825,569 @@ read_restore_filters(const char *filename, RestoreOptions *opts)
 
 	filter_free(&fstate);
 }
+
+/*
+				if (c == m)
+							SimpleOidStringList * dbname_oid_list,
+	for (SimpleOidStringListCell * db_cell = dbname_oid_list->head;
+			 * the construct pattern matching query: SELECT 1 WHERE XXX
+			 * OPERATOR(pg_catalog.~) '^(PATTERN)$' COLLATE pg_catalog.default
+			 * XXX represents the string literal database name derived from
+			 * the dbname_oid_list, which is initially extracted from the
+			 * map.dat file located in the backup directory.  that's why we
+			 * need quote_literal_cstr.
+get_dbname_oid_list_from_mfile(const char *dumpdirpath, SimpleOidStringList * dbname_oid_list)
+	 * If there is only global.dat file in dump, then return from here as
+	 * there is no database to restore.
+	while ((fgets(line, MAXPGPATH, pfile)) != NULL)
+		sscanf(line, "%u", &db_oid);
+		sscanf(line, "%20s", db_oid_str);
+		 * needs to skipped for restore or not but as of now, we are making a
+		 * list of all the databases.
+	 * If map.dat has no entry, return from here after processing global.dat
+	 * file.
+	 * Till now, we made a list of databases, those needs to be restored after
+	 * skipping names of exclude-database.  Now we can launch parallel workers
+	 * to restore these databases.
+	for (SimpleOidStringListCell * db_cell = dbname_oid_list.head;
+		char		subdirdbpath[MAXPGPATH];
+		char		dbfilename[MAXPGPATH];
+		 * We need to reset override_dbname so that objects can be restored
+		 * into already created database. (used with -d/--dbname option)
+		snprintf(subdirdbpath, MAXPGPATH, "%s/databases", dumpdirpath);
+		 * Validate database dump file.  If there is .tar or .dmp file exist
+		 * then consider particular file, otherwise just append dboid to the
+		 * databases folder.
+		snprintf(dbfilename, MAXPGPATH, "%u.tar", db_cell->oid);
+		if (file_exists_in_directory(subdirdbpath, dbfilename))
+			snprintf(subdirpath, MAXPGPATH, "%s/databases/%u.tar", dumpdirpath, db_cell->oid);
+			snprintf(dbfilename, MAXPGPATH, "%u.dmp", db_cell->oid);
+			if (file_exists_in_directory(subdirdbpath, dbfilename))
+				snprintf(subdirpath, MAXPGPATH, "%s/databases/%u.dmp", dumpdirpath, db_cell->oid);
+	/* Log number of processed databases. */
 
+command_fails_like(
 
+command_fails_like(
+	[ 'pg_dumpall', '--format', 'x' ],
+	qr/\Qpg_dumpall: error: unrecognized archive format "x";\E/,
+	'pg_dumpall: unrecognized archive format');
 done_testing();
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b66cecd8799..95ec8fbb141 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2732,6 +2732,8 @@ ShutdownMode
 SignTSVector
 SimpleActionList
 SimpleActionListCell
+SimpleDatabaseOidList
+SimpleDatabaseOidListCell
 SimpleEcontextStackEntry
 SimpleOidList
 SimpleOidListCell
-- 
2.34.1

diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 7ceaba27419..7a06e595b88 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -1639,10 +1639,9 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 		fprintf(OPF, "--\n-- Databases\n--\n\n");
 
 	/*
-	 * If directory/tar/custom format is specified then create a subdirectory
-	 * under the main directory and each database dump file subdirectory will
-	 * be created under the subdirectory in archive mode as per single db
-	 * pg_dump.
+	 * If directory/tar/custom format is specified, create a subdirectory
+	 * under the main directory and each database dump file or subdirectory
+	 * will be created in that subdirectory by pg_dump.
 	 */
 	if (archDumpFormat != archNull)
 	{
@@ -1666,7 +1665,7 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 	{
 		char	   *dbname = PQgetvalue(res, i, 0);
 		char	   *oid = PQgetvalue(res, i, 1);
-		const char *create_opts;
+		const char *create_opts = "";
 		int			ret;
 
 		/* Skip template0, even if it's not marked !datallowconn. */
@@ -1699,7 +1698,8 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)

-		fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n", dbname);
+		if (archDumpFormat == archNull)
+			 fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n", dbname);
 
 		/*
 		 * We assume that "template1" and "postgres" already exist in the
@@ -1713,20 +1713,8 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 		{
 			if (output_clean)
 				create_opts = "--clean --create";
-			else
-			{
-				/* Since pg_dump won't emit a \connect command, we must */
-				if (archDumpFormat == archNull)
-				{
-					create_opts = "";
-					fprintf(OPF, "\\connect %s\n\n", dbname);
-				}
-				else
-				{
-					/* Dumping all databases so add --create option. */
-					create_opts = "--create";
-				}
-			}
+			else if (archDumpFormat == archNull)
+				fprintf(OPF, "\\connect %s\n\n", dbname);
 		}
 		else
 			create_opts = "--create";

---

From: Andrew Dunstan <[email-masked]>
Date: Sun, 30 Mar 2025 18:05:57 -0400

On 2025-03-30 Su 12:50 PM, Andrew Dunstan wrote:
> pg_restore: error: could not execute query: ERROR:  database 
> "template1" already exists
> pg_restore: error: could not execute query: ERROR:  database 
> "postgres" already exists
> Command was: CREATE DATABASE postgres WITH TEMPLATE = template0 
> It seems pointless to be trying to create the rolw that we are 
> connected as, and we also expect template1 and postgres to exist.
>

I have reworked the documentation some. See attached.

cheers

andrew

index 765b30a3a66..43fdab2d77e 100644
 
  <refnamediv>
   <refname>pg_dumpall</refname>
-  <refpurpose>extract a <productname>PostgreSQL</productname> database cluster into a script file</refpurpose>
+  <refpurpose>extract a <productname>PostgreSQL</productname> database cluster using a specified dump format</refpurpose>
  </refnamediv>
 
  <refsynopsisdiv>
@@ -33,7 +33,7 @@ PostgreSQL documentation
   <para>
    <application>pg_dumpall</application> is a utility for writing out
    (<quote>dumping</quote>) all <productname>PostgreSQL</productname> databases
-   of a cluster into one script file.  The script file contains
+   of a cluster into an archive.  The archive contains
    <acronym>SQL</acronym> commands that can be used as input to <xref
    linkend="app-psql"/> to restore the databases.  It does this by
    calling <xref linkend="app-pgdump"/> for each database in the cluster.
@@ -52,11 +52,16 @@ PostgreSQL documentation
   </para>
 
   <para>
-   The SQL script will be written to the standard output.  Use the
+   Plain text SQL scripts will be written to the standard output.  Use the
    <option>-f</option>/<option>--file</option> option or shell operators to
    redirect it into a file.
   </para>
 
+  <para>
+   Archives in other formats will be placed in a directory named using the
+   <option>-f</option>/<option>--file</option>, which is required in this case.
+  </para>
+
   <para>
   <application>pg_dumpall</application> needs to connect several
   times to the <productname>PostgreSQL</productname> server (once per
@@ -121,10 +126,85 @@ PostgreSQL documentation
        <para>
         Send output to the specified file.  If this is omitted, the
         standard output is used.
+        Note: This option can only be omitted when <option>--format</option> is plain
        </para>
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-F <replaceable class="parameter">format</replaceable></option></term>
+      <term><option>--format=<replaceable class="parameter">format</replaceable></option></term>
+      <listitem>
+       <para>
+        Specify the format of dump files.  In plain format, all the dump data is
+        sent in a single text stream. This is the default.
+
+        In all other modes, <application>pg_dumpall</application> first creates two files:
+        <filename>global.dat</filename> and <filename>map.dat</filename>, in the directory
+        specified by <option>--file</option>.
+        The first file contains global data, such as roles and tablespaces. The second
+        contains a mapping between database oids and names. These files are used by
+        <application>pg_restore</application>. Data for individual databases is placed in
+        <filename>databases</filename> subdirectory, named using the database's <type>oid</type>.
+
+       <variablelist>
+        <varlistentry>
+         <term><literal>d</literal></term>
+         <term><literal>directory</literal></term>
+         <listitem>
+          <para>
+           Output directory-format archives for each database,
+           suitable for input into pg_restore. The directory
+           will have database <type>oid</type> as its name.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term><literal>p</literal></term>
+         <term><literal>plain</literal></term>
+         <listitem>
+          <para>
+           Output a plain-text SQL script file (the default).
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term><literal>c</literal></term>
+         <term><literal>custom</literal></term>
+         <listitem>
+          <para>
+           Output a custom-format archive for each database,
+           suitable for input into pg_restore. The archive
+           will be named <filename>dboid.dmp</filename> where <type>dboid</type> is the
+           <type>oid</type> of the database.
+          </para>
+         </listitem>
+        </varlistentry>
+
+         <varlistentry>
+         <term><literal>t</literal></term>
+         <term><literal>tar</literal></term>
+         <listitem>
+          <para>
+           Output a tar-format archive for each database,
+           suitable for input into pg_restore. The archive
+           will be named <filename>dboid.tar</filename> where <type>dboid</type> is the
+           <type>oid</type> of the database.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        </variablelist>
+
+       Note: see <xref linkend="app-pgdump"/> for details
+       of how the various non plain text archives work.
+
+        </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--filter=<replaceable class="parameter">filename</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index c840a807ae9..f14e5866f6c 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -18,8 +18,9 @@ PostgreSQL documentation
   <refname>pg_restore</refname>
 
   <refpurpose>
-   restore a <productname>PostgreSQL</productname> database from an
-   archive file created by <application>pg_dump</application>
+   restore a <productname>PostgreSQL</productname> database or cluster
+   from an archive created by <application>pg_dump</application> or
+   <application>pg_dumpall</application>
   </refpurpose>
  </refnamediv>
 
@@ -38,13 +39,14 @@ PostgreSQL documentation
 
   <para>
    <application>pg_restore</application> is a utility for restoring a
-   <productname>PostgreSQL</productname> database from an archive
-   created by <xref linkend="app-pgdump"/> in one of the non-plain-text
+   <productname>PostgreSQL</productname> database or cluster from an archive
+   created by <xref linkend="app-pgdump"/> or
+   <xref linkend="app-pg-dumpall"/> in one of the non-plain-text
    formats.  It will issue the commands necessary to reconstruct the
-   database to the state it was in at the time it was saved.  The
-   archive files also allow <application>pg_restore</application> to
+   database or cluster to the state it was in at the time it was saved. The
+   archives also allow <application>pg_restore</application> to
    be selective about what is restored, or even to reorder the items
-   prior to being restored. The archive files are designed to be
+   prior to being restored. The archive formats are designed to be
    portable across architectures.
   </para>
 
@@ -52,10 +54,17 @@ PostgreSQL documentation
    <application>pg_restore</application> can operate in two modes.
    If a database name is specified, <application>pg_restore</application>
    connects to that database and restores archive contents directly into
-   the database.  Otherwise, a script containing the SQL
-   commands necessary to rebuild the database is created and written
+   the database.
+   When restoring from a dump made by<application>pg_dumpall</application>,
+   each database will be created and then the restoration will be run in that
+   database.
+
+   Otherwise, when a database name is not specified, a script containing the SQL
+   commands necessary to rebuild the database or cluster is created and written
    to a file or standard output.  This script output is equivalent to
-   the plain text output format of <application>pg_dump</application>.
+   the plain text output format of <application>pg_dump</application> or
+   <application>pg_dumpall</application>.
+
    Some of the options controlling the output are therefore analogous to
    <application>pg_dump</application> options.
   </para>
@@ -140,6 +149,8 @@ PostgreSQL documentation
         commands that mention this database.
         Access privileges for the database itself are also restored,
         unless <option>--no-acl</option> is specified.
+        <option>--create</option> is required when restoring multiple databases
+        from an archive created by <application>pg_dumpall</application>.
        </para>
 
        <para>
@@ -166,6 +177,28 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+        This option is only relevant when restoring from an archive made using <application>pg_dumpall</application>.
@@ -315,6 +348,19 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+        This option is only relevant when restoring from an archive made using <application>pg_dumpall</application>.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Mon, 31 Mar 2025 15:04:38 +0530

> pg_restore: error: could not execute query: "ERROR:  role "andrew"

Thanks Andrew for the updated patches.

Here, I am attaching a delta patch which solves the errors for the
already created database and we need to reset some flags also. Please have
a look over this delta patch and merge it.

If we want to skip errors for connected user (CREATE ROLE username), then
we need to handle it by comparing sql commands in
process_global_sql_commands function or we can compare errors after
executing it.
delta_0002* patch is doing some handling but this is not a proper fix.

I think we can merge delta_0001* and later, we can work on delta_0002.

> I also notice a bunch of these in globals.dat:

Thank you.

---

From: Andrew Dunstan <[email-masked]>
Date: Mon, 31 Mar 2025 09:57:56 -0400

On 2025-03-31 Mo 5:34 AM, Mahendra Singh Thalor wrote:
> already created database and we need to reset some flags also. Please 
> have a look over this delta patch and merge it.
>
> If we want to skip errors for connected user (CREATE ROLE username), 
> then we need to handle it by comparing sql commands in 

Yes, delta 1 looks OK, except that the pstrdup() calls are probably 
unnecessary. Delta 2 needs some significant surgery at least. I think we 
can use it as at least a partial fix, to avoid trying to create the role 
we're running as (Should use PQuser() for that rather than cparams.user).

BTW, if you're sending delta patches, make sure they don't have .patch 
extensions. Otherwise, the CFBot gets upset. I usually just add .noci to 
the file names.

cheers

andrew

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Mon, 31 Mar 2025 21:46:01 +0530

> we're running as (Should use PQuser() for that rather than cparams.user).

Thanks for the quick review.

I fixed the above comments and made 2 delta patches. Please have a
look over these.

> extensions. Otherwise, the CFBot gets upset. I usually just add .noci to

Sure. I will also use .noci. Thanks for feedback.

---

From: Andrew Dunstan <[email-masked]>
Date: Mon, 31 Mar 2025 13:16:19 -0400

> Sure. I will also use .noci. Thanks for feedback.

Thanks. Here are patches that contain (my version of) all the cleanups. 
With this I get a clean restore run in my test case with no error messages.

cheers

andrew

---

From: Andrew Dunstan <[email-masked]>
Date: Mon, 31 Mar 2025 13:20:24 -0400

On 2025-03-31 Mo 1:16 PM, Andrew Dunstan wrote:
>
>
>
> Thanks. Here are patches that contain (my version of) all the 
> cleanups. With this I get a clean restore run in my test case with no 
> error messages.
>
>
>

This time with patches

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com
From 6286701ff360ccb8c105fa5aa0a8f9bba3b1d1d7 Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 19 Mar 2025 01:18:46 +0530
Subject: [PATCH v20250331 1/3] Move common pg_dump code related to connections
 to a new file

 
 
 install: all installdirs

 
 
 		/*

 

 	PQfinish(oldConn);
 }
 
 	if (AH->connection)
 
-	/*
 
index 4ca34be230c..f84ea6ecc48 100644
 
 	/*

 
 
 		if (!conn)
 
 		if (!conn)
 		{
 
-/*

From f172c7db61a75d4bbb586d597ae8e9e44d02642c Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <[email-masked]>
Date: Fri, 28 Mar 2025 18:10:24 -0400
Subject: [PATCH v20250331 2/3] add new list type simple_oid_string_list to
 fe-utils/simple_list

---

 

From 00c534f0e4f501cadb84f2d26b78c92995971f3b Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 19 Mar 2025 01:30:12 +0530
Subject: [PATCH v20250331 3/3] pg_dumpall with directory|tar|custom format and
 restore it by pg_restore

dumps are as:

Design:

thread:

Author:    Mahendra Singh Thalor <[email-masked]>
---
 doc/src/sgml/ref/pg_dumpall.sgml     |  86 ++-
 doc/src/sgml/ref/pg_restore.sgml     |  66 ++-
 src/bin/pg_dump/parallel.c           |  11 +-
 src/bin/pg_dump/pg_backup.h          |   2 +-
 src/bin/pg_dump/pg_backup_archiver.c |  20 +-
 src/bin/pg_dump/pg_backup_archiver.h |   3 +-
 src/bin/pg_dump/pg_backup_tar.c      |   2 +-
 src/bin/pg_dump/pg_backup_utils.c    |  22 +-
 src/bin/pg_dump/pg_backup_utils.h    |   3 +-
 src/bin/pg_dump/pg_dump.c            |   2 +-
 src/bin/pg_dump/pg_dumpall.c         | 295 ++++++++--
 src/bin/pg_dump/pg_restore.c         | 802 ++++++++++++++++++++++++++-
 src/bin/pg_dump/t/001_basic.pl       |   9 +
 src/tools/pgindent/typedefs.list     |   2 +
 14 files changed, 1231 insertions(+), 94 deletions(-)
 mode change 100644 => 100755 src/bin/pg_dump/t/001_basic.pl

  <refnamediv>
 
  <refsynopsisdiv>
 
   <para>
 
+  <para>
 
+     <varlistentry>
 
   <refpurpose>

   <para>

        <para>
 
+     <varlistentry>
 
+     <varlistentry>
 
 /*

 

-/* Public */
+/*
+ * RestoreArchive
+ *

 
 	sav = SaveOutput(AH);
 	if (ropt->filename)

 static void

 

 

 
 /*

index f84ea6ecc48..832a6af7091 100644
 
 	CloseArchive(fout);
 
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 573a8b61a45..9d08c6ca0e6 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -15,6 +15,7 @@
 
 #include "postgres_fe.h"
 
+#include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>

 
 		/*

 
 
+	/*
 
-	/*

 	PQfinish(conn);

 
 
 	printf(_("\nGeneral options:\n"));
 	printf(_("  -f, --file=FILENAME          output file name\n"));
+	printf(_("  -F, --format=c|d|t|p         output file format (custom, directory, tar,\n"
+			 "                               plain text (default))\n"));
 	printf(_("  -v, --verbose                verbose mode\n"));
 	printf(_("  -V, --version                output version information, then exit\n"));
 	printf(_("  --lock-wait-timeout=TIMEOUT  fail after waiting TIMEOUT for a table lock\n"));
@@ -969,9 +1009,6 @@ dumpRoles(PGconn *conn)
 	 * We do it this way because config settings for roles could mention the
 	 * names of other roles.
 	 */
-	if (PQntuples(res) > 0)
-		fprintf(OPF, "\n--\n-- User Configurations\n--\n");
-
 	for (i = 0; i < PQntuples(res); i++)
 		dumpUserConfig(conn, PQgetvalue(res, i, i_rolname));
 
@@ -1485,6 +1522,7 @@ dumpUserConfig(PGconn *conn, const char *username)
 {
 	PQExpBuffer buf = createPQExpBuffer();
 	PGresult   *res;
+	static bool header_done = false;
 
 	printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
 					  "WHERE setdatabase = 0 AND setrole = "
@@ -1496,7 +1534,13 @@ dumpUserConfig(PGconn *conn, const char *username)
 	res = executeQuery(conn, buf->data);
 
 	if (PQntuples(res) > 0)
+	{
+		if (!header_done)
+			fprintf(OPF, "\n--\n-- User Configurations\n--\n");
+		header_done = true;
+
 		fprintf(OPF, "\n--\n-- User Config \"%s\"\n--\n\n", username);
+	}
 
 	for (int i = 0; i < PQntuples(res); i++)
 	{
@@ -1570,10 +1614,13 @@ expand_dbname_patterns(PGconn *conn,
  * Dump contents of databases.
  */
 static void
-dumpDatabases(PGconn *conn)
+dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 {
 	PGresult   *res;
 	int			i;
+	char		db_subdir[MAXPGPATH];
+	char		dbfilepath[MAXPGPATH];
+	FILE	   *map_file = NULL;
 
 	/*
@@ -1587,18 +1634,42 @@ dumpDatabases(PGconn *conn)
 
-	if (PQntuples(res) > 0)
+	if (archDumpFormat == archNull && PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Databases\n--\n\n");
 
+	/*
 
 		/* Skip template0, even if it's not marked !datallowconn. */
@@ -1612,9 +1683,27 @@ dumpDatabases(PGconn *conn)
 			continue;
 		}
 
+		/*

 		/*
 		 * We assume that "template1" and "postgres" already exist in the
@@ -1628,12 +1717,9 @@ dumpDatabases(PGconn *conn)
 		{
 			if (output_clean)
 				create_opts = "--clean --create";
-			else
-			{
-				create_opts = "";
-				/* Since pg_dump won't emit a \connect command, we must */
+			/* Since pg_dump won't emit a \connect command, we must */
+			else if (archDumpFormat == archNull)
 				fprintf(OPF, "\\connect %s\n\n", dbname);
-			}
 		}
 		else
 			create_opts = "--create";
@@ -1641,19 +1727,30 @@ dumpDatabases(PGconn *conn)
 		if (filename)
 			fclose(OPF);

 		if (filename)
 		{
 
+	/* Close map file */
 
@@ -1663,7 +1760,8 @@ dumpDatabases(PGconn *conn)
  * Run pg_dump on dbname, with specified options.
  */
 static int
-runPgDump(const char *dbname, const char *create_opts)
+runPgDump(const char *dbname, const char *create_opts, char *dbfile,
+		  ArchiveFormat archDumpFormat)
 {
 	PQExpBufferData connstrbuf;
 	PQExpBufferData cmd;
@@ -1672,17 +1770,36 @@ runPgDump(const char *dbname, const char *create_opts)
 	initPQExpBuffer(&connstrbuf);
 	initPQExpBuffer(&cmd);

 	/*
 	 * Append the database name to the already-constructed stem of connection
@@ -1827,3 +1944,91 @@ read_dumpall_filters(const char *filename, SimpleStringList *pattern)
 
 	filter_free(&fstate);
 }
+
+/*
+ * create_or_open_dir
+ *
index 47f7b0dd3a1..5ba0c4b768f 100644
 
 #include <ctype.h>
+#include <sys/stat.h>
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif

+static int	restore_one_database(const char *inputFileSpec, RestoreOptions *opts,
+static int	restore_all_databases(PGconn *conn, const char *dumpdirpath,
 
 int
 
 		{NULL, 0, NULL, 0}
 	};

 			default:
 
@@ -452,6 +489,115 @@ main(int argc, char **argv)
 					 opts->formatName);
 	}
 
+	/*
+			n_errors = restore_all_databases(conn, inputFileSpec, db_exclude_patterns,
+		n_errors = restore_one_database(inputFileSpec, opts, numWorkers, false, 0);
+ * restore_one_database
+restore_one_database(const char *inputFileSpec, RestoreOptions *opts,
 
@@ -459,9 +605,15 @@ main(int argc, char **argv)
 
@@ -481,25 +633,22 @@ main(int argc, char **argv)

 
-	return exit_code;
+	return n_errors;
 }
 
 static void
 
@@ -517,6 +666,7 @@ usage(const char *progname)
@@ -529,6 +679,7 @@ usage(const char *progname)
@@ -569,8 +720,8 @@ usage(const char *progname)
 
 	printf(_("\n"
@@ -675,3 +826,620 @@ read_restore_filters(const char *filename, RestoreOptions *opts)
 
 	filter_free(&fstate);
 }
+
+/*
+ * restore_all_databases
+restore_all_databases(PGconn *conn, const char *dumpdirpath,
+	char		*connected_db = NULL;
+	bool		dumpData = opts->dumpData;
+	bool		dumpSchema = opts->dumpSchema;
+	bool		dumpStatistics = opts->dumpSchema;
+	/* Save db name to reuse it for all the database. */
+		connected_db = opts->cparams.dbname;
+	 * processing pg_restore --exclude-database=PATTERN/NAME if no connection.
+		/* If database is already created, then don't set createDB flag. */
+			PGconn *test_conn;
+			test_conn = ConnectDatabase(db_cell->str, NULL, opts->cparams.pghost,
+			if (test_conn)
+				PQfinish(test_conn);
+				/* Use already created database for connection. */
+				opts->createDB = 0;
+				opts->cparams.dbname = db_cell->str;
+				/* we'll have to create it */
+				opts->createDB = 1;
+				opts->cparams.dbname = connected_db;
+		 * Reset flags - might have been reset in pg_backup_archiver.c by the
+		 * previous restore.
+		opts->dumpData = dumpData;
+		opts->dumpSchema = dumpSchema;
+		opts->dumpStatistics = dumpStatistics;
+		n_errors = restore_one_database(subdirpath, opts, numWorkers, true, count);
+	StringInfoData sqlstatement, user_create;
+	/* creation statement for our current role */
+	initStringInfo(&user_create);
+	appendStringInfoString(&user_create, "CREATE ROLE ");
+	/* should use fmtId here, but we don't know the encoding */
+	appendStringInfoString(&user_create, PQuser(conn));
+	appendStringInfoString(&user_create, ";");
+		/* don't try to create the role we are connected as */
+		if (strstr(sqlstatement.data, user_create.data))
 
+command_fails_like(
 
+command_fails_like(

---

From: Álvaro Herrera <[email-masked]>
Date: Mon, 31 Mar 2025 20:12:36 +0200

Hi

FWIW I don't think the on_exit_nicely business is in final shape just
yet.  We're doing something super strange and novel about keeping track
of an array index, so that we can modify it later.  Or something like
that, I think?  That doesn't sound all that nice to me.  Elsewhere it
was suggested that we need some way to keep track of the list of things
that need cleanup (a list of connections IIRC?) -- perhaps in a
thread-local variable or a global or something -- and we install the
cleanup function once, and that reads from the variable.  The program
can add things to the list, or remove them, at will; and we don't need
to modify the cleanup function in any way.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Tue, 1 Apr 2025 11:29:43 +0530

> to modify the cleanup function in any way.

Thanks Álvaro for the feedback.

I removed the old handling of on_exit_nicely_list from the last patch
set and added one simple function to just update the archive handle in
shutdown_info.  (shutdown_info.AHX = AHX;)

For first database, we will add entry into on_exit_nicely_list array
and for rest database, we will update only shutdown_info as we already
closed connection for previous database.With this fix, we will not
touch entry of on_exit_nicely_list for each database.

Here, I am attaching updated patches.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Fri, 4 Apr 2025 13:52:56 +0530

On Fri, 4 Apr 2025 at 01:17, Andrew Dunstan <[email-masked]> wrote:
>
>
> On 2025-04-01 Tu 1:59 AM, Mahendra Singh Thalor wrote:
> OK, looks good. here's my latest. I'm currently working on tidying up
> docco and comments.
>
>
> cheers
>
>
> andrew
>
>
>
>
> --
> Andrew Dunstan

Here, I am attaching a delta patch with some more TAP-test cases.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Fri, 4 Apr 2025 14:42:32 +0530

> Here, I am attaching a delta patch with some more TAP-test cases.

Here, I am attaching an updated delta patch which has some more TAP
tests. Please include these tests also. This patch can be applied on
v20250403_0004* patch.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Thu, 10 Apr 2025 22:43:54 +0530

On Sat, 5 Apr 2025 at 01:41, Andrew Dunstan <[email-masked]> wrote:
>
>
> On 2025-04-04 Fr 5:12 AM, Mahendra Singh Thalor wrote:
>
> Thanks. I have pushed these now with a few further small tweaks.
>
>
> cheers
>
>
> andrew
>
> --
> Andrew Dunstan

Thanks Andrew for committing this.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 16 Apr 2025 00:00:25 +0530

> to modify the cleanup function in any way.

Hi Andrew,
I did some refactoring to find out dump file extensions(.dmp/.tar etc)
in pg_restore. With the attached patch, we will not try to find out
file extension with each database, rather we will find out before the
loop.

Here, I am attaching a patch for the same. Please have a look over this.

---

From: Noah Misch <[email-masked]>
Date: Tue, 8 Jul 2025 14:28:19 -0700

> Thanks. I have pushed these now with a few further small tweaks.

This drops all databases:

pg_dumpall --clean -Fd -f /tmp/dump
pg_restore -d template1 --globals-only /tmp/dump

That didn't match my expectations given this help text:

$ pg_restore --help|grep global
  -g, --globals-only           restore only global objects, no databases

This happens in dropDBs().  I found that by searching pg_dumpall.c for "OPF",
which finds all the content we can write to globals.dat.

> +				snprintf(dbfilepath, MAXPGPATH, "\"%s\"/\"%s\"", db_subdir, oid);

Use appendShellString() instead.  Plain mode already does that for the
"pg_dumpall -f" argument, which is part of db_subdir here.  We don't want
weird filename characters to work out differently for plain vs. non-plain
mode.  Also, it's easier to search for appendShellString() than to search for
open-coded shell quoting.

>  				pg_fatal("could not re-open the output file \"%s\": %m",

Minor item: plain mode benefits from reopening, because pg_dump appended to
the plain output file.  There's no analogous need to reopen global.dat, since
just this one process writes to global.dat.

> +						  pgdumpopts->data, create_opts);

This uses pgdumpopts for plain mode only, so many pg_dumpall options silently
have no effect in non-plain mode.  Example:

strace -f pg_dumpall --lock-wait-timeout=10 2>&1 >/dev/null | grep exec
strace -f pg_dumpall --lock-wait-timeout=10 -Fd -f /tmp/dump3 2>&1 >/dev/null | grep exec

> + * EOF is returned if end-of-file input is seen; time to shut down.

What makes it okay to use this particular subset of SQL lexing?

> +		pg_log_info("considering PATTERN as NAME for --exclude-database option as no db connection while doing pg_restore.");

When do we not have a connection here?  We'd need to document this behavior
variation if it stays, but I'd prefer if we can just rely on having a
connection.

> +				opts->cparams.dbname = connected_db;

In released versions, "pg_restore --create" fails if the database exists, and
pg_restore w/o --create fails unless the database exists.  I think we should
continue that pattern in this new feature.  If not, pg_restore should document
how it treats pg_dumpall-sourced dumps with the "create if not exists"
semantics appearing here.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Thu, 10 Jul 2025 00:21:03 +0530

Thanks Noah for the comments.

>   -g, --globals-only           restore only global objects, no databases

Databases are global objects so due to --clean command, we are putting
drop commands in global.dat for all the databases. While restoring, we
used the  "--globals-only" option so we are dropping all these
databases by global.dat file.

Please let us know your expectations for this specific case.

> open-coded shell quoting.

Yes, we can use appendShellString also. We are using snprintf in the
pg_dump.c file also.
Ex: snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
                     loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
If we want to use appendShellString, I can write a patch for these.
Please let me know your opinion.

> just this one process writes to global.dat.

yes, only once we need to open global.dat file but to keep simple
code, we kept old code.

> strace -f pg_dumpall --lock-wait-timeout=10 -Fd -f /tmp/dump3 2>&1 >/dev/null | grep exec

Agreed. We can add pgdumpopts->data to all the dump formats.

> What makes it okay to use this particular subset of SQL lexing?

To support complex syntax, we used this code from another file.

> variation if it stays, but I'd prefer if we can just rely on having a

Yes, we can document this behavior.

> semantics appearing here.

Yes, we can document this behavior also.

I am working on all these review comments and I will post a patch in
the coming days.

Thanks and Regards
Mahendra Singh Thalor

---

From: Noah Misch <[email-masked]>
Date: Tue, 15 Jul 2025 17:19:57 -0700

> Please let us know your expectations for this specific case.

Be consistent with "pg_dump".  A quick check suggests "pg_dump --clean"
affects plain format only.  For non-plain formats, only the pg_restore
argument governs the final commands:

$ rm -r /tmp/dump; pg_dump --clean -Fd -f /tmp/dump && pg_restore -f- /tmp/dump | grep DROP
$ rm -r /tmp/dump; pg_dump --clean -Fd -f /tmp/dump && pg_restore --clean -f- /tmp/dump | grep DROP
DROP TABLE public.example;

That said, you should audit code referencing the --clean flag and see if
there's more to it than that quick test suggests.  Note that aligning with
pg_dump will require changes for object types beyond databases.  "pg_restore
--clean" of a global dump should emit DROP TABLESPACE and DROP ROLE as
appropriate, regardless of whether the original pg_dumpall had --clean.

For my earlier example (pg_dumpall --clean; pg_restore --globals-only) I
expect the same outcome as plain-format "pg_dumpall --globals-only", which is
no databases dropped or created.  The help line says "no databases".  Plain
"pg_dumpall --globals-only" and even "pg_dumpall --globals-only --clean" do
not drop or create databases.

>                      loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);

It's true snprintf() is not banned in these programs, but don't use it to do
the quoting for OS shell command lines or fragments thereof.  dbfilepath is a
fragment of an OS shell command line.  The LARGE OBJECTS string is not one of
those.  Hence, the LARGE OBJECTS scenario should keep using snprintf().

> Please let me know your opinion.

Use appendShellString() for shell quoting.  Don't attempt to use it for other
purposes.

> To support complex syntax, we used this code from another file.

I'm hearing that you copied this code from somewhere.  Running
"git grep 'time to shut down'" suggests you copied it from
InteractiveBackend().  Is that right?  I do see other similarities between
read_one_statement() and InteractiveBackend().

Copying InteractiveBackend() provides negligible assurance that this is the
right subset of SQL lexing.  Only single-user mode uses InteractiveBackend().
Single-user mode survives mostly as a last resort for recovering from having
reached xidStopLimit, is rarely used, and only superusers write queries to it.

> Yes, we can document this behavior.

My review asked a question there.  I don't see an answer to that question.
Would you answer that question?

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Thu, 17 Jul 2025 15:46:41 +0530

Thanks Noah for the feedback.

> not drop or create databases.

To pg_restore, we are giving a dump of pg_dumpall which has a
global.dat file and we have drop commands in the global.dat file so
when we are using 'globals-only', we are dropping databases as we have
DROP commands.
As of now, we don't have any filter for global.dat file in restore. If
a user wants to restore only globals(without droping db), then they
should use 'globals-only' in pg_dumpall.
Or if we don't want to DROP databases by global.dat file, then we
should add a filter in pg_restore (hard to implement as we have SQL
commands in global.dat file). I think, for this case, we can do some
more doc changes.
Example: pg_restore --globals-only : this will restore the global.dat
file(including all drop commands). It might drop databases if any drop
commands.
@Andrew Dunstan Please add your opinion.

> Use appendShellString() for shell quoting.  Don't attempt to use it for other

Okay. Fixed in attached patch.

> reached xidStopLimit, is rarely used, and only superusers write queries to it.

Yes, we copied this from InteractiveBackend to read statements from
global.dat file.

> Would you answer that question?

Example: if there is no active database, even postgres/template1, then
we will consider PATTEREN as NAME. This is the rare case.
In attached patch, I added one doc line also for this case.

Fixed.

Fixed.

Fixed.

> semantics appearing here.

Added one more doc line for this case.

Here, I am attaching a patch. Please let me know feedback.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Thu, 17 Jul 2025 15:48:48 +0530

Attaching the correct patch.

Sorry, I attached the wrong patch in my last email.

---

From: Álvaro Herrera <[email-masked]>
Date: Thu, 17 Jul 2025 13:11:05 +0200

On 2025-Jul-17, Mahendra Singh Thalor wrote:

> commands in global.dat file). 

I think dropping database is dangerous and makes no practical sense;
doing it renders pg_dumpall --clean completely unusable.  You're arguing
from the point of view of ease of implementation, but that doesn't help
users.  

> I think, for this case, we can do some
> more doc changes.

I don't think doc changes are useful.

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"I love the Postgres community. It's all about doing things _properly_. :-)"
(David Garamond)

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Thu, 17 Jul 2025 18:22:38 +0530

> from the point of view of ease of implementation, but that doesn't help

I have 2 more solutions for this case.
*Solution1*: dump DROP database/role/tablespace commands in global_drop.dat
(or dump only DROP DATABASE commands in global_drop.dat file) and skip
restoring this file with globals-only.
*Solution2*: add one more filter in restore to skip the "DROP DATABASE"
command as we already have one filter for "CREATE USER".

Based on *solution1*, I made a WIP patch. Here, I am attaching a patch for
feedback.

Note: please use this v02 patch for review.

---

From: Andrew Dunstan <[email-masked]>
Date: Thu, 17 Jul 2025 12:09:08 -0400

> from the point of view of ease of implementation, but that doesn't help

Yeah. I also agree with Noah that we should be consistent with pg_dump. 
And we should err on the side of caution. If we impose a little 
inconvenience on the user by requiring them to drop a database, it's 
better than surprising them by dropping a database when they didn't 
expect it.

There are some subtleties here. pg_restore will only issue DROP DATABASE 
of you use the -C flag, even if you specify --clean, so we need to be 
very careful about issuing DROP DATABASE.

I confess that all this didn't occur to me when working on the commit.

> I don't think doc changes are useful.

Yeah, I don't think this is something that can be  cured by documentation.

cheers

andrew

--
Andrew Dunstan
EDB:https://www.enterprisedb.com

---

From: Andrew Dunstan <[email-masked]>
Date: Mon, 21 Jul 2025 16:41:03 -0400

On 2025-07-17 Th 6:18 AM, Mahendra Singh Thalor wrote
>> Yes, we copied this from InteractiveBackend to read statements from

Maybe we should ensure that identifiers with CR or LF are turned into 
Unicode quoted identifiers, so each SQL statement would always only 
occupy one line. Or just reject role and tablespace names with CR or LF 
altogether, just as we do for database names.

cheers

andrew

---

From: Noah Misch <[email-masked]>
Date: Mon, 21 Jul 2025 17:53:39 -0700

On Mon, Jul 21, 2025 at 04:41:03PM -0400, Andrew Dunstan wrote:
> Unicode quoted identifiers, so each SQL statement would always only occupy
> one line.

Interesting.  That might work.

> Or just reject role and tablespace names with CR or LF altogether,
> just as we do for database names.

There are other ways to get multi-line statements.  Non-exhaustive list:

- pg_db_role_setting.setconfig
- pg_shdescription.description
- pg_shseclabel.label
- pg_tablespace.spcoptions (if we add a text option in the future)

I think this decision about lexing also ties to other unfinished open item
work of aligning "pg_dumpall -Fd;pg_restore [options]" behavior with "pg_dump
-Fd;pg_restore [options]".  "pg_restore --no-privileges" should not restore
pg_tablespace.spcacl, and "pg_restore --no-comments" should not emit COMMENT
statements.

I suspect this is going to end with a structured dump like we use on the
pg_dump (per-database) side.  It's not an accident that v17 pg_restore doesn't
lex text files to do its job.  pg_dumpall deals with a more-limited set of
statements than pg_dump deals with, but they're not _that much_ more limited.
I won't veto a lexing-based approach if it gets the behaviors right, but I
don't have high hopes for it getting the behaviors right and staying that way.

(I almost said "pg_restore --no-owner" should not restore
pg_tablespace.spcowner, but v17 "pg_dumpall --no-owner" does restore it.  One
could argue for or against aligning $SUBJECT behavior w/ v17's mistake there.)

---

From: Andrew Dunstan <[email-masked]>
Date: Mon, 21 Jul 2025 21:43:01 -0400

> don't have high hopes for it getting the behaviors right and staying that way.

Yeah, that was my original idea. But maybe instead of extending the 
archive mechanism, we could do something more lightweight, e.g. output 
the statements as a JSON array.

cheers

andrew

---

From: Noah Misch <[email-masked]>
Date: Thu, 24 Jul 2025 10:50:36 -0700

> In attached patch, I added one doc line also for this case.

If I change s/pg_log_info/pg_fatal/, check-world still passes.  So no test is
reaching the !conn case.  If one wanted to write a test that reaches the !conn
test, how would they do that?

---

From: Robert Haas <[email-masked]>
Date: Thu, 24 Jul 2025 14:02:39 -0400

> Please let us know your expectations for this specific case.

I am not sure whether pg_dumpall --clean should ever drop databases,
but it certainly shouldn't do it with --globals-only. In that case,
it's not restoring the databases, so dropping them seems
catastrophically bad.

-- 
Robert Haas
EDB: http://www.enterprisedb.com

---

From: Andrew Dunstan <[email-masked]>
Date: Thu, 24 Jul 2025 16:33:15 -0400

> don't have high hopes for it getting the behaviors right and staying that way.

I have been talking offline with Mahendra about this. I agree that we 
would be better off with a structured object for globals. But the thing 
that's been striking me all afternoon as I have pondered it is that we 
should not be designing such an animal at this stage of the cycle. 
Whatever we do we're going to be stuck supporting, so I have very 
reluctantly come to the conclusion that it would probably be better to 
back the feature out and have another go for PG 19.

cheers

andrew

---

From: Noah Misch <[email-masked]>
Date: Fri, 25 Jul 2025 09:21:41 -0700

On Thu, Jul 24, 2025 at 04:33:15PM -0400, Andrew Dunstan wrote:
> I have been talking offline with Mahendra about this. I agree that we would
> be better off with a structured object for globals. But the thing that's
> been striking me all afternoon as I have pondered it is that we should not
> be designing such an animal at this stage of the cycle. Whatever we do we're
> going to be stuck supporting, so I have very reluctantly come to the
> conclusion that it would probably be better to back the feature out and have
> another go for PG 19.

That makes sense to me.  It would be quite a sprint to get this done in time,
and that wouldn't leave much room for additional testing and feedback before
the final release.  I agree with the reluctance and with the conclusion.

---

From: Andrew Dunstan <[email-masked]>
Date: Fri, 25 Jul 2025 15:31:47 -0400

> the final release.  I agree with the reluctance and with the conclusion.

Before we throw the baby out with the bathwater, how about this 
suggestion? pg_dumpall would continue to produce globals.dat, but it 
wouldn't be processed by pg_restore, which would only restore the 
individual databases. Or else we just don't produce globals.dat at all. 
Then we could introduce a structured object that pg_restore could safely 
use for release 19, and I think we'd still have something useful for 
release 18.

cheers

andrew

---

From: Tom Lane <[email-masked]>
Date: Fri, 25 Jul 2025 16:59:29 -0400

> use for release 19, and I think we'd still have something useful for 

I dunno ... that seems like a pretty weird behavior.  People would
have to do a separate text-mode "pg_dumpall -g" and remember to
restore that too.  Admittedly, this could be more convenient than
"pg_dumpall -g" plus separately pg_dump'ing each database, which is
what people have to do today if they want anything smarter than a flat
text dumpfile.  But it still seems like a hack --- and it would not be
compatible with v19, where presumably "pg_dumpall | pg_restore"
*would* restore globals.  I think that the prospect of changing
dump/restore scripts and then having to change them again in v19
isn't too appetizing.

			regards, tom lane

---

From: Noah Misch <[email-masked]>
Date: Sun, 27 Jul 2025 16:56:28 -0700

> dump/restore scripts and then having to change them again in v19

+1

---

From: Andrew Dunstan <[email-masked]>
Date: Mon, 28 Jul 2025 08:04:34 -0400

>> dump/restore scripts and then having to change them again in v19

OK, got it. Will revert.

cheers

andrew

---

From: Andrew Dunstan <[email-masked]>
Date: Tue, 29 Jul 2025 16:09:13 -0400

>>> dump/restore scripts and then having to change them again in v19

here's a reversion patch for master. It applies cleanly to release 18 as 
well. Thanks to Mahendra Singh Thalor for helping me sanity check it 
(Any issues are of course my responsibility)

I'll work on pulling the entry out of the release notes.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com
commit 77d9ccbce21
Author: Andrew Dunstan <[email-masked]>
Date:   Tue Jul 29 15:54:15 2025 -0400

    Revert Non text modes for pg_dumpall, and pg_restore support
    
    Recent discussions of the mechanisms used to manage global data have
    raised concerns about their robustness and security. Rather than try
    to deal with those concerns at a very late stage of the release cycle,
    the conclusion is to revert these features and work on them for the
    next release.
    
    This reverts parts or all of the following commits:
    
    1495eff7bdb Non text modes for pg_dumpall, correspondingly change pg_restore
    5db3bf7391d Clean up from commit 1495eff7bdb
    289f74d0cb2 Add more TAP tests for pg_dumpall
    2ef57908067 Fix a couple of error messages and tests for them
    b52a4a5f285 Clean up error messages from 1495eff7bdb
    4170298b6ec Further cleanup for directory creation on pg_dump/pg_dumpall
    22cb6d28950 Fix memory leak in pg_restore.c
    928394b664b Improve various new-to-v18 appendStringInfo calls
    39729ec01d2 Fix fat fingering in 22cb6d28950
    5822bf21d50 Add missing space in pg_restore documentation.
    f09088a01d3 Free memory properly in pg_restore.c
    40b9c27014d pg_restore cleanups
    4aad2cb7707 Portability fix: isdigit() must be passed an unsigned char.
    88e947136b4 Fix typos and grammar in the code
    f60420cff66 doc: Alphabetize long options for pg_dump[all].
    bc35adee8d7 doc: Put new options in consistent order on man pages
    a876464abc7 Message style improvements
    dec6643487b Improve pg_dump/pg_dumpall help synopses and terminology
    0ebd2425558 Run pgperltidy
    
    Discussion: https://postgr.es/m/[email-masked]

diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index 8ca68da5a55..f4cbc8288e3 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -16,10 +16,7 @@ PostgreSQL documentation
 
  <refnamediv>
   <refname>pg_dumpall</refname>
-
-  <refpurpose>
-   export a <productname>PostgreSQL</productname> database cluster as an SQL script or to other formats
-  </refpurpose>
+  <refpurpose>extract a <productname>PostgreSQL</productname> database cluster into a script file</refpurpose>
  </refnamediv>
 
  <refsynopsisdiv>
@@ -36,7 +33,7 @@ PostgreSQL documentation
   <para>
    <application>pg_dumpall</application> is a utility for writing out
    (<quote>dumping</quote>) all <productname>PostgreSQL</productname> databases
-   of a cluster into an SQL script file or an archive.  The output contains
+   of a cluster into one script file.  The script file contains
    <acronym>SQL</acronym> commands that can be used as input to <xref
    linkend="app-psql"/> to restore the databases.  It does this by
    calling <xref linkend="app-pgdump"/> for each database in the cluster.
@@ -55,16 +52,11 @@ PostgreSQL documentation
   </para>
 
   <para>
-   Plain text SQL scripts will be written to the standard output.  Use the
+   The SQL script will be written to the standard output.  Use the
    <option>-f</option>/<option>--file</option> option or shell operators to
    redirect it into a file.
   </para>
 
-  <para>
-   Archives in other formats will be placed in a directory named using the
-   <option>-f</option>/<option>--file</option>, which is required in this case.
-  </para>
-
   <para>
   <application>pg_dumpall</application> needs to connect several
   times to the <productname>PostgreSQL</productname> server (once per
@@ -129,85 +121,10 @@ PostgreSQL documentation
        <para>
         Send output to the specified file.  If this is omitted, the
         standard output is used.
-        Note: This option can only be omitted when <option>--format</option> is plain
        </para>
       </listitem>
      </varlistentry>
 
-     <varlistentry>
-      <term><option>-F <replaceable class="parameter">format</replaceable></option></term>
-      <term><option>--format=<replaceable class="parameter">format</replaceable></option></term>
-      <listitem>
-       <para>
-        Specify the format of dump files.  In plain format, all the dump data is
-        sent in a single text stream. This is the default.
-
-        In all other modes, <application>pg_dumpall</application> first creates two files:
-        <filename>global.dat</filename> and <filename>map.dat</filename>, in the directory
-        specified by <option>--file</option>.
-        The first file contains global data, such as roles and tablespaces. The second
-        contains a mapping between database oids and names. These files are used by
-        <application>pg_restore</application>. Data for individual databases is placed in
-        <filename>databases</filename> subdirectory, named using the database's <type>oid</type>.
-
-       <variablelist>
-        <varlistentry>
-         <term><literal>d</literal></term>
-         <term><literal>directory</literal></term>
-         <listitem>
-          <para>
-           Output directory-format archives for each database,
-           suitable for input into pg_restore. The directory
-           will have database <type>oid</type> as its name.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry>
-         <term><literal>p</literal></term>
-         <term><literal>plain</literal></term>
-         <listitem>
-          <para>
-           Output a plain-text SQL script file (the default).
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry>
-         <term><literal>c</literal></term>
-         <term><literal>custom</literal></term>
-         <listitem>
-          <para>
-           Output a custom-format archive for each database,
-           suitable for input into pg_restore. The archive
-           will be named <filename>dboid.dmp</filename> where <type>dboid</type> is the
-           <type>oid</type> of the database.
-          </para>
-         </listitem>
-        </varlistentry>
-
-         <varlistentry>
-         <term><literal>t</literal></term>
-         <term><literal>tar</literal></term>
-         <listitem>
-          <para>
-           Output a tar-format archive for each database,
-           suitable for input into pg_restore. The archive
-           will be named <filename>dboid.tar</filename> where <type>dboid</type> is the
-           <type>oid</type> of the database.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        </variablelist>
-
-       Note: see <xref linkend="app-pgdump"/> for details
-       of how the various non plain text archives work.
-
-        </para>
-      </listitem>
-     </varlistentry>
-
      <varlistentry>
       <term><option>-g</option></term>
       <term><option>--globals-only</option></term>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index b649bd3a5ae..2abe05d47e9 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -18,9 +18,8 @@ PostgreSQL documentation
   <refname>pg_restore</refname>
 
   <refpurpose>
-   restore <productname>PostgreSQL</productname> databases from archives
-   created by <application>pg_dump</application> or
-   <application>pg_dumpall</application>
+   restore a <productname>PostgreSQL</productname> database from an
+   archive file created by <application>pg_dump</application>
   </refpurpose>
  </refnamediv>
 
@@ -39,14 +38,13 @@ PostgreSQL documentation
 
   <para>
    <application>pg_restore</application> is a utility for restoring a
-   <productname>PostgreSQL</productname> database or cluster from an archive
-   created by <xref linkend="app-pgdump"/> or
-   <xref linkend="app-pg-dumpall"/> in one of the non-plain-text
+   <productname>PostgreSQL</productname> database from an archive
+   created by <xref linkend="app-pgdump"/> in one of the non-plain-text
    formats.  It will issue the commands necessary to reconstruct the
-   database or cluster to the state it was in at the time it was saved. The
-   archives also allow <application>pg_restore</application> to
+   database to the state it was in at the time it was saved.  The
+   archive files also allow <application>pg_restore</application> to
    be selective about what is restored, or even to reorder the items
-   prior to being restored. The archive formats are designed to be
+   prior to being restored. The archive files are designed to be
    portable across architectures.
   </para>
 
@@ -54,17 +52,10 @@ PostgreSQL documentation
    <application>pg_restore</application> can operate in two modes.
    If a database name is specified, <application>pg_restore</application>
    connects to that database and restores archive contents directly into
-   the database.
-   When restoring from a dump made by <application>pg_dumpall</application>,
-   each database will be created and then the restoration will be run in that
-   database.
-
-   Otherwise, when a database name is not specified, a script containing the SQL
-   commands necessary to rebuild the database or cluster is created and written
+   the database.  Otherwise, a script containing the SQL
+   commands necessary to rebuild the database is created and written
    to a file or standard output.  This script output is equivalent to
-   the plain text output format of <application>pg_dump</application> or
-   <application>pg_dumpall</application>.
-
+   the plain text output format of <application>pg_dump</application>.
    Some of the options controlling the output are therefore analogous to
    <application>pg_dump</application> options.
   </para>
@@ -149,8 +140,6 @@ PostgreSQL documentation
         commands that mention this database.
         Access privileges for the database itself are also restored,
         unless <option>--no-acl</option> is specified.
-        <option>--create</option> is required when restoring multiple databases
-        from an archive created by <application>pg_dumpall</application>.
        </para>
 
        <para>
@@ -246,19 +235,6 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
-     <varlistentry>
-      <term><option>-g</option></term>
-      <term><option>--globals-only</option></term>
-      <listitem>
-       <para>
-        Restore only global objects (roles and tablespaces), no databases.
-       </para>
-       <para>
-        This option is only relevant when restoring from an archive made using <application>pg_dumpall</application>.
-       </para>
-      </listitem>
-     </varlistentry>
-
      <varlistentry>
       <term><option>-I <replaceable class="parameter">index</replaceable></option></term>
       <term><option>--index=<replaceable class="parameter">index</replaceable></option></term>
@@ -603,28 +579,6 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
-     <varlistentry>
-      <term><option>--exclude-database=<replaceable class="parameter">pattern</replaceable></option></term>
-      <listitem>
-       <para>
-        Do not restore databases whose name matches
-        <replaceable class="parameter">pattern</replaceable>.
-        Multiple patterns can be excluded by writing multiple
-        <option>--exclude-database</option> switches.  The
-        <replaceable class="parameter">pattern</replaceable> parameter is
-        interpreted as a pattern according to the same rules used by
-        <application>psql</application>'s <literal>\d</literal>
-        commands (see <xref linkend="app-psql-patterns"/>),
-        so multiple databases can also be excluded by writing wildcard
-        characters in the pattern.  When using wildcards, be careful to
-        quote the pattern if needed to prevent shell wildcard expansion.
-       </para>
-       <para>
-        This option is only relevant when restoring from an archive made using <application>pg_dumpall</application>.
-       </para>
-      </listitem>
-     </varlistentry>
-
      <varlistentry>
       <term><option>--filter=<replaceable class="parameter">filename</replaceable></option></term>
       <listitem>
diff --git a/src/bin/pg_dump/meson.build b/src/bin/pg_dump/meson.build
index 4a4ebbd8ec9..a2233b0a1b4 100644
--- a/src/bin/pg_dump/meson.build
+++ b/src/bin/pg_dump/meson.build
@@ -102,7 +102,6 @@ tests += {
       't/003_pg_dump_with_server.pl',
       't/004_pg_dump_parallel.pl',
       't/005_pg_dump_filterfile.pl',
-      't/006_pg_dumpall.pl',
       't/010_dump_connstr.pl',
     ],
   },
diff --git a/src/bin/pg_dump/parallel.c b/src/bin/pg_dump/parallel.c
index 5974d6706fd..086adcdc502 100644
--- a/src/bin/pg_dump/parallel.c
+++ b/src/bin/pg_dump/parallel.c
@@ -333,16 +333,6 @@ on_exit_close_archive(Archive *AHX)
 	on_exit_nicely(archive_close_connection, &shutdown_info);
 }
 
-/*
- * When pg_restore restores multiple databases, then update already added entry
- * into array for cleanup.
- */
-void
-replace_on_exit_close_archive(Archive *AHX)
-{
-	shutdown_info.AHX = AHX;
-}
-
 /*
  * on_exit_nicely handler for shutting down database connections and
  * worker processes cleanly.
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index af0007fb6d2..4ebef1e8644 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -308,7 +308,7 @@ extern void SetArchiveOptions(Archive *AH, DumpOptions *dopt, RestoreOptions *ro

-extern void RestoreArchive(Archive *AHX, bool append_data);
+extern void RestoreArchive(Archive *AHX);
 
 /* Open an existing archive */
 extern Archive *OpenArchive(const char *FileSpec, const ArchiveFormat fmt);
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 30e0da31aa3..dce88f040ac 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -87,7 +87,7 @@ static int	RestoringToDB(ArchiveHandle *AH);
 static void dump_lo_buf(ArchiveHandle *AH);
 static void dumpTimestamp(ArchiveHandle *AH, const char *msg, time_t tim);
 static void SetOutput(ArchiveHandle *AH, const char *filename,
-					  const pg_compress_specification compression_spec, bool append_data);
+					  const pg_compress_specification compression_spec);
 static CompressFileHandle *SaveOutput(ArchiveHandle *AH);
 static void RestoreOutput(ArchiveHandle *AH, CompressFileHandle *savedOutput);
 
@@ -339,14 +339,9 @@ ProcessArchiveRestoreOptions(Archive *AHX)
 		StrictNamesCheck(ropt);
 }
 
-/*
- * RestoreArchive
- *
- * If append_data is set, then append data into file as we are restoring dump
- * of multiple databases which was taken by pg_dumpall.
- */
+/* Public */
 void
-RestoreArchive(Archive *AHX, bool append_data)
+RestoreArchive(Archive *AHX)
 {
 	ArchiveHandle *AH = (ArchiveHandle *) AHX;
 	RestoreOptions *ropt = AH->public.ropt;
@@ -463,7 +458,7 @@ RestoreArchive(Archive *AHX, bool append_data)
 	 */
 	sav = SaveOutput(AH);
 	if (ropt->filename || ropt->compression_spec.algorithm != PG_COMPRESSION_NONE)
-		SetOutput(AH, ropt->filename, ropt->compression_spec, append_data);
+		SetOutput(AH, ropt->filename, ropt->compression_spec);

@@ -1302,7 +1297,7 @@ PrintTOCSummary(Archive *AHX)
 
 	sav = SaveOutput(AH);
 	if (ropt->filename)
-		SetOutput(AH, ropt->filename, out_compression_spec, false);
+		SetOutput(AH, ropt->filename, out_compression_spec);
 
 	if (strftime(stamp_str, sizeof(stamp_str), PGDUMP_STRFTIME_FMT,
 				 localtime(&AH->createDate)) == 0)
@@ -1681,8 +1676,7 @@ archprintf(Archive *AH, const char *fmt,...)
 
 static void
 SetOutput(ArchiveHandle *AH, const char *filename,
-		  const pg_compress_specification compression_spec,
-		  bool append_data)
+		  const pg_compress_specification compression_spec)
 {
 	CompressFileHandle *CFH;
 	const char *mode;
@@ -1702,7 +1696,7 @@ SetOutput(ArchiveHandle *AH, const char *filename,
 	else
 		fn = fileno(stdout);
 
-	if (append_data || AH->mode == archModeAppend)
+	if (AH->mode == archModeAppend)
 		mode = PG_BINARY_A;
 	else
 		mode = PG_BINARY_W;
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 365073b3eae..325b53fc9bd 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -394,7 +394,6 @@ struct _tocEntry
 
 extern int	parallel_restore(ArchiveHandle *AH, TocEntry *te);
 extern void on_exit_close_archive(Archive *AHX);
-extern void replace_on_exit_close_archive(Archive *AHX);

index d94d0de2a5d..b5ba3b46dd9 100644
 
-		RestoreArchive((Archive *) AH, false);
+		RestoreArchive((Archive *) AH);

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 6298edb26b5..f543d418e46 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1265,7 +1265,7 @@ main(int argc, char **argv)
 	 * right now.
 	 */
 	if (plainText)
-		RestoreArchive(fout, false);
+		RestoreArchive(fout);
 
 	CloseArchive(fout);
 
@@ -1276,7 +1276,7 @@ main(int argc, char **argv)
 static void
 help(const char *progname)
 {
-	printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
+	printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
 	printf(_("Usage:\n"));
 	printf(_("  %s [OPTION]... [DBNAME]\n"), progname);
 
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 100317b1aa9..f69f0260256 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -65,10 +65,9 @@ static void dropTablespaces(PGconn *conn);
 static void dumpTablespaces(PGconn *conn);
 static void dropDBs(PGconn *conn);
 static void dumpUserConfig(PGconn *conn, const char *username);
-static void dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat);
+static void dumpDatabases(PGconn *conn);
 static void dumpTimestamp(const char *msg);
-static int	runPgDump(const char *dbname, const char *create_opts,
-					  char *dbfile, ArchiveFormat archDumpFormat);
+static int	runPgDump(const char *dbname, const char *create_opts);
 static void buildShSecLabels(PGconn *conn,
 							 const char *catalog_name, Oid objectId,
 							 const char *objtype, const char *objname,
@@ -77,7 +76,6 @@ static void executeCommand(PGconn *conn, const char *query);
 static void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns,
 								   SimpleStringList *names);
 static void read_dumpall_filters(const char *filename, SimpleStringList *pattern);
-static ArchiveFormat parseDumpFormat(const char *format);
 
 static char pg_dump_bin[MAXPGPATH];
 static PQExpBuffer pgdumpopts;
@@ -150,7 +148,6 @@ main(int argc, char *argv[])
 		{"password", no_argument, NULL, 'W'},
 		{"no-privileges", no_argument, NULL, 'x'},
 		{"no-acl", no_argument, NULL, 'x'},
-		{"format", required_argument, NULL, 'F'},
 
 		/*
 		 * the following options don't have an equivalent short option letter
@@ -201,8 +198,6 @@ main(int argc, char *argv[])
 	char	   *pgdb = NULL;
 	char	   *use_role = NULL;
 	const char *dumpencoding = NULL;
-	ArchiveFormat archDumpFormat = archNull;
-	const char *formatName = "p";
 	trivalue	prompt_password = TRI_DEFAULT;
 	bool		data_only = false;
 	bool		globals_only = false;
@@ -252,7 +247,7 @@ main(int argc, char *argv[])

-	while ((c = getopt_long(argc, argv, "acd:E:f:F:gh:l:Op:rsS:tU:vwWx", long_options, &optindex)) != -1)
+	while ((c = getopt_long(argc, argv, "acd:E:f:gh:l:Op:rsS:tU:vwWx", long_options, &optindex)) != -1)
 	{
 		switch (c)
 		{
@@ -280,9 +275,7 @@ main(int argc, char *argv[])
 				appendPQExpBufferStr(pgdumpopts, " -f ");
 				appendShellString(pgdumpopts, filename);
 				break;
-			case 'F':
-				formatName = pg_strdup(optarg);
-				break;
+
 			case 'g':
 				globals_only = true;
 				break;
@@ -431,21 +424,6 @@ main(int argc, char *argv[])
 		exit_nicely(1);
 	}
 
-	/* Get format for dump. */
-	archDumpFormat = parseDumpFormat(formatName);
-
-	/*
-	 * If a non-plain format is specified, a file name is also required as the
-	 * path to the main directory.
-	 */
-	if (archDumpFormat != archNull &&
-		(!filename || strcmp(filename, "") == 0))
-	{
-		pg_log_error("option -F/--format=d|c|t requires option -f/--file");
-		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
-		exit_nicely(1);
-	}
-
 	/*
 	 * If password values are not required in the dump, switch to using
 	 * pg_roles which is equally useful, just more likely to have unrestricted
@@ -510,33 +488,6 @@ main(int argc, char *argv[])
 	if (sequence_data)
 		appendPQExpBufferStr(pgdumpopts, " --sequence-data");
 
-	/*
-	 * Open the output file if required, otherwise use stdout.  If required,
-	 * then create new directory and global.dat file.
-	 */
-	if (archDumpFormat != archNull)
-	{
-		char		global_path[MAXPGPATH];
-
-		/* Create new directory or accept the empty existing directory. */
-		create_or_open_dir(filename);
-
-		snprintf(global_path, MAXPGPATH, "%s/global.dat", filename);
-
-		OPF = fopen(global_path, PG_BINARY_W);
-		if (!OPF)
-			pg_fatal("could not open file \"%s\": %m", global_path);
-	}
-	else if (filename)
-	{
-		OPF = fopen(filename, PG_BINARY_W);
-		if (!OPF)
-			pg_fatal("could not open output file \"%s\": %m",
-					 filename);
-	}
-	else
-		OPF = stdout;
-
 	/*
 	 * If there was a database specified on the command line, use that,
 	 * otherwise try to connect to database "postgres", and failing that
@@ -576,6 +527,19 @@ main(int argc, char *argv[])
 	expand_dbname_patterns(conn, &database_exclude_patterns,
 						   &database_exclude_names);
 
+	/*
+	 * Open the output file if required, otherwise use stdout
+	 */
+	if (filename)
+	{
+		OPF = fopen(filename, PG_BINARY_W);
+		if (!OPF)
+			pg_fatal("could not open output file \"%s\": %m",
+					 filename);
+	}
+	else
+		OPF = stdout;
+
 	/*
 	 * Set the client encoding if requested.
 	 */
@@ -675,7 +639,7 @@ main(int argc, char *argv[])
 	}
 
 	if (!globals_only && !roles_only && !tablespaces_only)
-		dumpDatabases(conn, archDumpFormat);
+		dumpDatabases(conn);
 
 	PQfinish(conn);
 
@@ -688,7 +652,7 @@ main(int argc, char *argv[])
 		fclose(OPF);
 
 		/* sync the resulting file, errors are not fatal */
-		if (dosync && (archDumpFormat == archNull))
+		if (dosync)
 			(void) fsync_fname(filename, false);
 	}
 
@@ -699,14 +663,12 @@ main(int argc, char *argv[])
 static void
 help(void)
 {
-	printf(_("%s exports a PostgreSQL database cluster as an SQL script or to other formats.\n\n"), progname);
+	printf(_("%s extracts a PostgreSQL database cluster into an SQL script file.\n\n"), progname);
 	printf(_("Usage:\n"));
 	printf(_("  %s [OPTION]...\n"), progname);
 
 	printf(_("\nGeneral options:\n"));
 	printf(_("  -f, --file=FILENAME          output file name\n"));
-	printf(_("  -F, --format=c|d|t|p         output file format (custom, directory, tar,\n"
-			 "                               plain text (default))\n"));
 	printf(_("  -v, --verbose                verbose mode\n"));
 	printf(_("  -V, --version                output version information, then exit\n"));
 	printf(_("  --lock-wait-timeout=TIMEOUT  fail after waiting TIMEOUT for a table lock\n"));
@@ -1013,6 +975,9 @@ dumpRoles(PGconn *conn)
 	 * We do it this way because config settings for roles could mention the
 	 * names of other roles.
 	 */
+	if (PQntuples(res) > 0)
+		fprintf(OPF, "\n--\n-- User Configurations\n--\n");
+
 	for (i = 0; i < PQntuples(res); i++)
 		dumpUserConfig(conn, PQgetvalue(res, i, i_rolname));
 
@@ -1526,7 +1491,6 @@ dumpUserConfig(PGconn *conn, const char *username)
 {
 	PQExpBuffer buf = createPQExpBuffer();
 	PGresult   *res;
-	static bool header_done = false;
 
 	printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
 					  "WHERE setdatabase = 0 AND setrole = "
@@ -1538,13 +1502,7 @@ dumpUserConfig(PGconn *conn, const char *username)
 	res = executeQuery(conn, buf->data);
 
 	if (PQntuples(res) > 0)
-	{
-		if (!header_done)
-		header_done = true;
 
 	for (int i = 0; i < PQntuples(res); i++)
 	{
@@ -1618,13 +1576,10 @@ expand_dbname_patterns(PGconn *conn,
  * Dump contents of databases.
  */
 static void
-dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
+dumpDatabases(PGconn *conn)
 {
 	PGresult   *res;
 	int			i;
-	char		db_subdir[MAXPGPATH];
-	char		dbfilepath[MAXPGPATH];
-	FILE	   *map_file = NULL;
 
 	/*
@@ -1638,42 +1593,18 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
-					   "SELECT datname, oid "
+					   "SELECT datname "
 
-	if (archDumpFormat == archNull && PQntuples(res) > 0)
+	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Databases\n--\n\n");
 
-	/*
-	 * If directory/tar/custom format is specified, create a subdirectory
-	 * under the main directory and each database dump file or subdirectory
-	 * will be created in that subdirectory by pg_dump.
-	 */
-	if (archDumpFormat != archNull)
-	{
-		char		map_file_path[MAXPGPATH];
-
-		snprintf(db_subdir, MAXPGPATH, "%s/databases", filename);
-
-		/* Create a subdirectory with 'databases' name under main directory. */
-		if (mkdir(db_subdir, pg_dir_create_mode) != 0)
-			pg_fatal("could not create directory \"%s\": %m", db_subdir);
-
-		snprintf(map_file_path, MAXPGPATH, "%s/map.dat", filename);
-
-		/* Create a map file (to store dboid and dbname) */
-		map_file = fopen(map_file_path, PG_BINARY_W);
-		if (!map_file)
-			pg_fatal("could not open file \"%s\": %m", map_file_path);
-	}
-
 	for (i = 0; i < PQntuples(res); i++)
 	{
 		char	   *dbname = PQgetvalue(res, i, 0);
-		char	   *oid = PQgetvalue(res, i, 1);
-		const char *create_opts = "";
+		const char *create_opts;
 		int			ret;
 
 		/* Skip template0, even if it's not marked !datallowconn. */
@@ -1687,27 +1618,9 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 			continue;
 		}
 
-		/*
-		 * If this is not a plain format dump, then append dboid and dbname to
-		 * the map.dat file.
-		 */
-		if (archDumpFormat != archNull)
-		{
-			if (archDumpFormat == archCustom)
-				snprintf(dbfilepath, MAXPGPATH, "\"%s\"/\"%s\".dmp", db_subdir, oid);
-			else if (archDumpFormat == archTar)
-				snprintf(dbfilepath, MAXPGPATH, "\"%s\"/\"%s\".tar", db_subdir, oid);
-			else
-				snprintf(dbfilepath, MAXPGPATH, "\"%s\"/\"%s\"", db_subdir, oid);
-
-			/* Put one line entry for dboid and dbname in map file. */
-			fprintf(map_file, "%s %s\n", oid, dbname);
-		}
-
 		pg_log_info("dumping database \"%s\"", dbname);

 		/*
 		 * We assume that "template1" and "postgres" already exist in the
@@ -1721,9 +1634,12 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 		{
 			if (output_clean)
 				create_opts = "--clean --create";
-			/* Since pg_dump won't emit a \connect command, we must */
-			else if (archDumpFormat == archNull)
+			else
+			{
+				create_opts = "";
+				/* Since pg_dump won't emit a \connect command, we must */
 				fprintf(OPF, "\\connect %s\n\n", dbname);
+			}
 		}
 		else
 			create_opts = "--create";
@@ -1731,30 +1647,19 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 		if (filename)
 			fclose(OPF);
 
-		ret = runPgDump(dbname, create_opts, dbfilepath, archDumpFormat);
+		ret = runPgDump(dbname, create_opts);
 		if (ret != 0)
 			pg_fatal("pg_dump failed on database \"%s\", exiting", dbname);
 
 		if (filename)
 		{
-			char		global_path[MAXPGPATH];
-
-			if (archDumpFormat != archNull)
-				snprintf(global_path, MAXPGPATH, "%s/global.dat", filename);
-			else
-				snprintf(global_path, MAXPGPATH, "%s", filename);
-
-			OPF = fopen(global_path, PG_BINARY_A);
+			OPF = fopen(filename, PG_BINARY_A);
 			if (!OPF)
 				pg_fatal("could not re-open the output file \"%s\": %m",
-						 global_path);
+						 filename);
 		}
 	}
 
-	/* Close map file */
-	if (archDumpFormat != archNull)
-		fclose(map_file);
-
 	PQclear(res);
 }
 
@@ -1764,8 +1669,7 @@ dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
  * Run pg_dump on dbname, with specified options.
  */
 static int
-runPgDump(const char *dbname, const char *create_opts, char *dbfile,
-		  ArchiveFormat archDumpFormat)
+runPgDump(const char *dbname, const char *create_opts)
 {
 	PQExpBufferData connstrbuf;
 	PQExpBufferData cmd;
@@ -1774,36 +1678,17 @@ runPgDump(const char *dbname, const char *create_opts, char *dbfile,
 	initPQExpBuffer(&connstrbuf);
 	initPQExpBuffer(&cmd);
 
+	printfPQExpBuffer(&cmd, "\"%s\" %s %s", pg_dump_bin,
+					  pgdumpopts->data, create_opts);
+
 	/*
-	 * If this is not a plain format dump, then append file name and dump
-	 * format to the pg_dump command to get archive dump.
+	 * If we have a filename, use the undocumented plain-append pg_dump
+	 * format.
 	 */
-	if (archDumpFormat != archNull)
-	{
-		printfPQExpBuffer(&cmd, "\"%s\" -f %s %s", pg_dump_bin,
-						  dbfile, create_opts);
-
-		if (archDumpFormat == archDirectory)
-			appendPQExpBufferStr(&cmd, "  --format=directory ");
-		else if (archDumpFormat == archCustom)
-			appendPQExpBufferStr(&cmd, "  --format=custom ");
-		else if (archDumpFormat == archTar)
-			appendPQExpBufferStr(&cmd, "  --format=tar ");
-	}
+	if (filename)
+		appendPQExpBufferStr(&cmd, " -Fa ");
 	else
-	{
-		printfPQExpBuffer(&cmd, "\"%s\" %s %s", pg_dump_bin,
-						  pgdumpopts->data, create_opts);
-
-		/*
-		 * If we have a filename, use the undocumented plain-append pg_dump
-		 * format.
-		 */
-		if (filename)
-			appendPQExpBufferStr(&cmd, " -Fa ");
-		else
-			appendPQExpBufferStr(&cmd, " -Fp ");
-	}
+		appendPQExpBufferStr(&cmd, " -Fp ");
 
 	/*
 	 * Append the database name to the already-constructed stem of connection
@@ -1948,36 +1833,3 @@ read_dumpall_filters(const char *filename, SimpleStringList *pattern)
 
 	filter_free(&fstate);
 }
-
-/*
- * parseDumpFormat
- *
- * This will validate dump formats.
- */
-static ArchiveFormat
-parseDumpFormat(const char *format)
-{
-	ArchiveFormat archDumpFormat;
-
-	if (pg_strcasecmp(format, "c") == 0)
-		archDumpFormat = archCustom;
-	else if (pg_strcasecmp(format, "custom") == 0)
-		archDumpFormat = archCustom;
-	else if (pg_strcasecmp(format, "d") == 0)
-		archDumpFormat = archDirectory;
-	else if (pg_strcasecmp(format, "directory") == 0)
-		archDumpFormat = archDirectory;
-	else if (pg_strcasecmp(format, "p") == 0)
-		archDumpFormat = archNull;
-	else if (pg_strcasecmp(format, "plain") == 0)
-		archDumpFormat = archNull;
-	else if (pg_strcasecmp(format, "t") == 0)
-		archDumpFormat = archTar;
-	else if (pg_strcasecmp(format, "tar") == 0)
-		archDumpFormat = archTar;
-	else
-		pg_fatal("unrecognized output format \"%s\"; please specify \"c\", \"d\", \"p\", or \"t\"",
-				 format);
-
-	return archDumpFormat;
-}
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 6ef789cb06d..b4e1acdb63f 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -2,7 +2,7 @@
  *
  * pg_restore.c
  *	pg_restore is an utility extracting postgres database definitions
- *	from a backup archive created by pg_dump/pg_dumpall using the archiver
+ *	from a backup archive created by pg_dump using the archiver
  *	interface.
  *
  *	pg_restore will read the backup archive and
@@ -41,15 +41,11 @@
 #include "postgres_fe.h"
 
 #include <ctype.h>
-#include <sys/stat.h>
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
 
-#include "common/string.h"
-#include "connectdb.h"
 #include "fe_utils/option_utils.h"
-#include "fe_utils/string_utils.h"
 #include "filter.h"
 #include "getopt_long.h"
 #include "parallel.h"
@@ -57,43 +53,18 @@
 
 static void usage(const char *progname);
 static void read_restore_filters(const char *filename, RestoreOptions *opts);
-static bool file_exists_in_directory(const char *dir, const char *filename);
-static int	restore_one_database(const char *inputFileSpec, RestoreOptions *opts,
-								 int numWorkers, bool append_data, int num);
-static int	read_one_statement(StringInfo inBuf, FILE *pfile);
-static int	restore_all_databases(PGconn *conn, const char *dumpdirpath,
-								  SimpleStringList db_exclude_patterns, RestoreOptions *opts, int numWorkers);
-static int	process_global_sql_commands(PGconn *conn, const char *dumpdirpath,
-										const char *outfile);
-static void copy_or_print_global_file(const char *outfile, FILE *pfile);
-static int	get_dbnames_list_to_restore(PGconn *conn,
-										SimplePtrList *dbname_oid_list,
-										SimpleStringList db_exclude_patterns);
-static int	get_dbname_oid_list_from_mfile(const char *dumpdirpath,
-										   SimplePtrList *dbname_oid_list);
-
-/*
- * Stores a database OID and the corresponding name.
- */
-typedef struct DbOidName
-{
-	Oid			oid;
-	char		str[FLEXIBLE_ARRAY_MEMBER]; /* null-terminated string here */
-} DbOidName;
-
 
 int
 main(int argc, char **argv)
 {
 	RestoreOptions *opts;
 	int			c;
+	int			exit_code;
 	int			numWorkers = 1;
+	Archive    *AH;
 	char	   *inputFileSpec;
 	bool		data_only = false;
 	bool		schema_only = false;
-	int			n_errors = 0;
-	bool		globals_only = false;
-	SimpleStringList db_exclude_patterns = {NULL, NULL};
 	static int	disable_triggers = 0;
 	static int	enable_row_security = 0;
 	static int	if_exists = 0;
@@ -119,7 +90,6 @@ main(int argc, char **argv)
 		{"clean", 0, NULL, 'c'},
 		{"create", 0, NULL, 'C'},
 		{"data-only", 0, NULL, 'a'},
-		{"globals-only", 0, NULL, 'g'},
 		{"dbname", 1, NULL, 'd'},
 		{"exit-on-error", 0, NULL, 'e'},
 		{"exclude-schema", 1, NULL, 'N'},
@@ -174,7 +144,6 @@ main(int argc, char **argv)
 		{"with-statistics", no_argument, &with_statistics, 1},
 		{"statistics-only", no_argument, &statistics_only, 1},
 		{"filter", required_argument, NULL, 4},
-		{"exclude-database", required_argument, NULL, 6},
 
 		{NULL, 0, NULL, 0}
 	};
@@ -203,7 +172,7 @@ main(int argc, char **argv)
 		}
 	}
 
-	while ((c = getopt_long(argc, argv, "acCd:ef:F:gh:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1",
+	while ((c = getopt_long(argc, argv, "acCd:ef:F:h:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1",
 							cmdopts, NULL)) != -1)
 	{
 		switch (c)
@@ -230,14 +199,11 @@ main(int argc, char **argv)
 				if (strlen(optarg) != 0)
 					opts->formatName = pg_strdup(optarg);
 				break;
-			case 'g':
-				/* restore only global.dat file from directory */
-				globals_only = true;
-				break;
 			case 'h':
 				if (strlen(optarg) != 0)
 					opts->cparams.pghost = pg_strdup(optarg);
 				break;
+
 			case 'j':			/* number of restore jobs */
 				if (!option_parse_int(optarg, "-j/--jobs", 1,
 									  PG_MAX_JOBS,
@@ -352,9 +318,6 @@ main(int argc, char **argv)
 					exit(1);
 				opts->exit_on_error = true;
 				break;
-			case 6:				/* database patterns to skip */
-				simple_string_list_append(&db_exclude_patterns, optarg);
-				break;
 
 			default:
 				/* getopt_long already emitted a complaint */
@@ -382,13 +345,6 @@ main(int argc, char **argv)
 	if (!opts->cparams.dbname && !opts->filename && !opts->tocSummary)
 		pg_fatal("one of -d/--dbname and -f/--file must be specified");
 
-	if (db_exclude_patterns.head != NULL && globals_only)
-	{
-		pg_log_error("option --exclude-database cannot be used together with -g/--globals-only");
-		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
-		exit_nicely(1);
-	}
-
 	/* Should get at most one of -d and -f, else user is confused */
 	if (opts->cparams.dbname)
 	{
@@ -496,114 +452,6 @@ main(int argc, char **argv)
 					 opts->formatName);
 	}
 
-	/*
-	 * If toc.dat file is not present in the current path, then check for
-	 * global.dat.  If global.dat file is present, then restore all the
-	 * databases from map.dat (if it exists), but skip restoring those
-	 * matching --exclude-database patterns.
-	 */
-	if (inputFileSpec != NULL && !file_exists_in_directory(inputFileSpec, "toc.dat") &&
-		file_exists_in_directory(inputFileSpec, "global.dat"))
-	{
-		PGconn	   *conn = NULL;	/* Connection to restore global sql
-									 * commands. */
-
-		/*
-		 * Can only use --list or --use-list options with a single database
-		 * dump.
-		 */
-		if (opts->tocSummary)
-			pg_fatal("option -l/--list cannot be used when restoring an archive created by pg_dumpall");
-		else if (opts->tocFile)
-			pg_fatal("option -L/--use-list cannot be used when restoring an archive created by pg_dumpall");
-
-		/*
-		 * To restore from a pg_dumpall archive, -C (create database) option
-		 * must be specified unless we are only restoring globals.
-		 */
-		if (!globals_only && opts->createDB != 1)
-		{
-			pg_log_error("option -C/--create must be specified when restoring an archive created by pg_dumpall");
-			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
-			pg_log_error_hint("Individual databases can be restored using their specific archives.");
-			exit_nicely(1);
-		}
-
-		/*
-		 * Connect to the database to execute global sql commands from
-		 * global.dat file.
-		 */
-		if (opts->cparams.dbname)
-		{
-			conn = ConnectDatabase(opts->cparams.dbname, NULL, opts->cparams.pghost,
-								   opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT,
-								   false, progname, NULL, NULL, NULL, NULL);
-
-
-			if (!conn)
-				pg_fatal("could not connect to database \"%s\"", opts->cparams.dbname);
-		}
-
-		/* If globals-only, then return from here. */
-		if (globals_only)
-		{
-			/*
-			 * Open global.dat file and execute/append all the global sql
-			 * commands.
-			 */
-			n_errors = process_global_sql_commands(conn, inputFileSpec,
-												   opts->filename);
-
-			if (conn)
-				PQfinish(conn);
-
-			pg_log_info("database restoring skipped because option -g/--globals-only was specified");
-		}
-		else
-		{
-			/* Now restore all the databases from map.dat */
-			n_errors = restore_all_databases(conn, inputFileSpec, db_exclude_patterns,
-											 opts, numWorkers);
-		}
-
-		/* Free db pattern list. */
-		simple_string_list_destroy(&db_exclude_patterns);
-	}
-	else						/* process if global.dat file does not exist. */
-	{
-		if (db_exclude_patterns.head != NULL)
-			pg_fatal("option --exclude-database can be used only when restoring an archive created by pg_dumpall");
-
-		if (globals_only)
-			pg_fatal("option -g/--globals-only can be used only when restoring an archive created by pg_dumpall");
-
-		n_errors = restore_one_database(inputFileSpec, opts, numWorkers, false, 0);
-	}
-
-	/* Done, print a summary of ignored errors during restore. */
-	if (n_errors)
-	{
-		pg_log_warning("errors ignored on restore: %d", n_errors);
-		return 1;
-	}
-
-	return 0;
-}
-
-/*
- * restore_one_database
- *
- * This will restore one database using toc.dat file.
- *
- * returns the number of errors while doing restore.
- */
-static int
-restore_one_database(const char *inputFileSpec, RestoreOptions *opts,
-					 int numWorkers, bool append_data, int num)
-{
-	Archive    *AH;
-	int			n_errors;
-
 	AH = OpenArchive(inputFileSpec, opts->format);
 
 	SetArchiveOptions(AH, NULL, opts);
@@ -611,15 +459,9 @@ restore_one_database(const char *inputFileSpec, RestoreOptions *opts,
 	/*
 	 * We don't have a connection yet but that doesn't matter. The connection
 	 * is initialized to NULL and if we terminate through exit_nicely() while
-	 * it's still NULL, the cleanup function will just be a no-op. If we are
-	 * restoring multiple databases, then only update AX handle for cleanup as
-	 * the previous entry was already in the array and we had closed previous
-	 * connection, so we can use the same array slot.
+	 * it's still NULL, the cleanup function will just be a no-op.
 	 */
-	if (!append_data || num == 0)
-		on_exit_close_archive(AH);
-	else
-		replace_on_exit_close_archive(AH);
+	on_exit_close_archive(AH);
 
 	/* Let the archiver know how noisy to be */
 	AH->verbose = opts->verbose;
@@ -639,21 +481,25 @@ restore_one_database(const char *inputFileSpec, RestoreOptions *opts,
 	else
 	{
 		ProcessArchiveRestoreOptions(AH);
-		RestoreArchive(AH, append_data);
+		RestoreArchive(AH);
 	}
 
-	n_errors = AH->n_errors;
+	/* done, print a summary of ignored errors */
+	if (AH->n_errors)
+		pg_log_warning("errors ignored on restore: %d", AH->n_errors);
 
 	/* AH may be freed in CloseArchive? */
+	exit_code = AH->n_errors ? 1 : 0;
+
 	CloseArchive(AH);
 
-	return n_errors;
+	return exit_code;
 }
 
 static void
 usage(const char *progname)
 {
-	printf(_("%s restores PostgreSQL databases from archives created by pg_dump or pg_dumpall.\n\n"), progname);
+	printf(_("%s restores a PostgreSQL database from an archive created by pg_dump.\n\n"), progname);
 	printf(_("Usage:\n"));
 	printf(_("  %s [OPTION]... [FILE]\n"), progname);
 
@@ -671,7 +517,6 @@ usage(const char *progname)
 	printf(_("  -c, --clean                  clean (drop) database objects before recreating\n"));
 	printf(_("  -C, --create                 create the target database\n"));
 	printf(_("  -e, --exit-on-error          exit on error, default is to continue\n"));
-	printf(_("  -g, --globals-only           restore only global objects, no databases\n"));
 	printf(_("  -I, --index=NAME             restore named index\n"));
 	printf(_("  -j, --jobs=NUM               use this many parallel jobs to restore\n"));
 	printf(_("  -L, --use-list=FILENAME      use table of contents from this file for\n"
@@ -688,7 +533,6 @@ usage(const char *progname)
 	printf(_("  -1, --single-transaction     restore as a single transaction\n"));
 	printf(_("  --disable-triggers           disable triggers during data-only restore\n"));
 	printf(_("  --enable-row-security        enable row security\n"));
-	printf(_("  --exclude-database=PATTERN   do not restore the specified database(s)\n"));
 	printf(_("  --filter=FILENAME            restore or skip objects based on expressions\n"
 			 "                               in FILENAME\n"));
 	printf(_("  --if-exists                  use IF EXISTS when dropping objects\n"));
@@ -725,8 +569,8 @@ usage(const char *progname)
 	printf(_("  --role=ROLENAME          do SET ROLE before restore\n"));
 
 	printf(_("\n"
-			 "The options -I, -n, -N, -P, -t, -T, --section, and --exclude-database can be\n"
-			 "combined and specified multiple times to select multiple objects.\n"));
+			 "The options -I, -n, -N, -P, -t, -T, and --section can be combined and specified\n"
+			 "multiple times to select multiple objects.\n"));
 	printf(_("\nIf no input file name is supplied, then standard input is used.\n\n"));
 	printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
 	printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
@@ -831,585 +675,3 @@ read_restore_filters(const char *filename, RestoreOptions *opts)
 
 	filter_free(&fstate);
 }
-
-/*
- * file_exists_in_directory
- *
- * Returns true if the file exists in the given directory.
- */
-static bool
-file_exists_in_directory(const char *dir, const char *filename)
-{
-	struct stat st;
-	char		buf[MAXPGPATH];
-
-	if (snprintf(buf, MAXPGPATH, "%s/%s", dir, filename) >= MAXPGPATH)
-		pg_fatal("directory name too long: \"%s\"", dir);
-
-	return (stat(buf, &st) == 0 && S_ISREG(st.st_mode));
-}
-
-/*
- * read_one_statement
- *
- * This will start reading from passed file pointer using fgetc and read till
- * semicolon(sql statement terminator for global.dat file)
- *
- * EOF is returned if end-of-file input is seen; time to shut down.
- */
-
-static int
-read_one_statement(StringInfo inBuf, FILE *pfile)
-{
-	int			c;				/* character read from getc() */
-	int			m;
-
-	StringInfoData q;
-
-	initStringInfo(&q);
-
-	resetStringInfo(inBuf);
-
-	/*
-	 * Read characters until EOF or the appropriate delimiter is seen.
-	 */
-	while ((c = fgetc(pfile)) != EOF)
-	{
-		if (c != '\'' && c != '"' && c != '\n' && c != ';')
-		{
-			appendStringInfoChar(inBuf, (char) c);
-			while ((c = fgetc(pfile)) != EOF)
-			{
-				if (c != '\'' && c != '"' && c != ';' && c != '\n')
-					appendStringInfoChar(inBuf, (char) c);
-				else
-					break;
-			}
-		}
-
-		if (c == '\'' || c == '"')
-		{
-			appendStringInfoChar(&q, (char) c);
-			m = c;
-
-			while ((c = fgetc(pfile)) != EOF)
-			{
-				appendStringInfoChar(&q, (char) c);
-
-				if (c == m)
-				{
-					appendStringInfoString(inBuf, q.data);
-					resetStringInfo(&q);
-					break;
-				}
-			}
-		}
-
-		if (c == ';')
-		{
-			appendStringInfoChar(inBuf, (char) ';');
-			break;
-		}
-
-		if (c == '\n')
-			appendStringInfoChar(inBuf, (char) '\n');
-	}
-
-	pg_free(q.data);
-
-	/* No input before EOF signal means time to quit. */
-	if (c == EOF && inBuf->len == 0)
-		return EOF;
-
-	/* return something that's not EOF */
-	return 'Q';
-}
-
-/*
- * get_dbnames_list_to_restore
- *
- * This will mark for skipping any entries from dbname_oid_list that pattern match an
- * entry in the db_exclude_patterns list.
- *
- * Returns the number of database to be restored.
- *
- */
-static int
-get_dbnames_list_to_restore(PGconn *conn,
-							SimplePtrList *dbname_oid_list,
-							SimpleStringList db_exclude_patterns)
-{
-	int			count_db = 0;
-	PQExpBuffer query;
-	PGresult   *res;
-
-	query = createPQExpBuffer();
-
-	if (!conn)
-		pg_log_info("considering PATTERN as NAME for --exclude-database option as no database connection while doing pg_restore");
-
-	/*
-	 * Process one by one all dbnames and if specified to skip restoring, then
-	 * remove dbname from list.
-	 */
-	for (SimplePtrListCell *db_cell = dbname_oid_list->head;
-		 db_cell; db_cell = db_cell->next)
-	{
-		DbOidName  *dbidname = (DbOidName *) db_cell->ptr;
-		bool		skip_db_restore = false;
-		PQExpBuffer db_lit = createPQExpBuffer();
-
-		appendStringLiteralConn(db_lit, dbidname->str, conn);
-
-		for (SimpleStringListCell *pat_cell = db_exclude_patterns.head; pat_cell; pat_cell = pat_cell->next)
-		{
-			/*
-			 * If there is an exact match then we don't need to try a pattern
-			 * match
-			 */
-			if (pg_strcasecmp(dbidname->str, pat_cell->val) == 0)
-				skip_db_restore = true;
-			/* Otherwise, try a pattern match if there is a connection */
-			else if (conn)
-			{
-				int			dotcnt;
-
-				appendPQExpBufferStr(query, "SELECT 1 ");
-				processSQLNamePattern(conn, query, pat_cell->val, false,
-									  false, NULL, db_lit->data,
-									  NULL, NULL, NULL, &dotcnt);
-
-				if (dotcnt > 0)
-				{
-					pg_log_error("improper qualified name (too many dotted names): %s",
-								 dbidname->str);
-					PQfinish(conn);
-					exit_nicely(1);
-				}
-
-				res = executeQuery(conn, query->data);
-
-				if ((PQresultStatus(res) == PGRES_TUPLES_OK) && PQntuples(res))
-				{
-					skip_db_restore = true;
-					pg_log_info("database name \"%s\" matches exclude pattern \"%s\"", dbidname->str, pat_cell->val);
-				}
-
-				PQclear(res);
-				resetPQExpBuffer(query);
-			}
-
-			if (skip_db_restore)
-				break;
-		}
-
-		destroyPQExpBuffer(db_lit);
-
-		/*
-		 * Mark db to be skipped or increment the counter of dbs to be
-		 * restored
-		 */
-		if (skip_db_restore)
-		{
-			pg_log_info("excluding database \"%s\"", dbidname->str);
-			dbidname->oid = InvalidOid;
-		}
-		else
-		{
-			count_db++;
-		}
-	}
-
-	destroyPQExpBuffer(query);
-
-	return count_db;
-}
-
-/*
- * get_dbname_oid_list_from_mfile
- *
- * Open map.dat file and read line by line and then prepare a list of database
- * names and corresponding db_oid.
- *
- * Returns, total number of database names in map.dat file.
- */
-static int
-get_dbname_oid_list_from_mfile(const char *dumpdirpath, SimplePtrList *dbname_oid_list)
-{
-	StringInfoData linebuf;
-	FILE	   *pfile;
-	char		map_file_path[MAXPGPATH];
-	int			count = 0;
-
-
-	/*
-	 * If there is only global.dat file in dump, then return from here as
-	 * there is no database to restore.
-	 */
-	if (!file_exists_in_directory(dumpdirpath, "map.dat"))
-	{
-		pg_log_info("database restoring is skipped because file \"%s\" does not exist in directory \"%s\"", "map.dat", dumpdirpath);
-		return 0;
-	}
-
-	snprintf(map_file_path, MAXPGPATH, "%s/map.dat", dumpdirpath);
-
-	/* Open map.dat file. */
-	pfile = fopen(map_file_path, PG_BINARY_R);
-
-	if (pfile == NULL)
-		pg_fatal("could not open file \"%s\": %m", map_file_path);
-
-	initStringInfo(&linebuf);
-
-	/* Append all the dbname/db_oid combinations to the list. */
-	while (pg_get_line_buf(pfile, &linebuf))
-	{
-		Oid			db_oid = InvalidOid;
-		char	   *dbname;
-		DbOidName  *dbidname;
-		int			namelen;
-		char	   *p = linebuf.data;
-
-		/* Extract dboid. */
-		while (isdigit((unsigned char) *p))
-			p++;
-		if (p > linebuf.data && *p == ' ')
-		{
-			sscanf(linebuf.data, "%u", &db_oid);
-			p++;
-		}
-
-		/* dbname is the rest of the line */
-		dbname = p;
-		namelen = strlen(dbname);
-
-		/* Report error and exit if the file has any corrupted data. */
-		if (!OidIsValid(db_oid) || namelen <= 1)
-			pg_fatal("invalid entry in file \"%s\" on line %d", map_file_path,
-					 count + 1);
-
-		pg_log_info("found database \"%s\" (OID: %u) in file \"%s\"",
-					dbname, db_oid, map_file_path);
-
-		dbidname = pg_malloc(offsetof(DbOidName, str) + namelen + 1);
-		dbidname->oid = db_oid;
-		strlcpy(dbidname->str, dbname, namelen);
-
-		simple_ptr_list_append(dbname_oid_list, dbidname);
-		count++;
-	}
-
-	/* Close map.dat file. */
-	fclose(pfile);
-
-	return count;
-}
-
-/*
- * restore_all_databases
- *
- * This will restore databases those dumps are present in
- * directory based on map.dat file mapping.
- *
- * This will skip restoring for databases that are specified with
- * exclude-database option.
- *
- * returns, number of errors while doing restore.
- */
-static int
-restore_all_databases(PGconn *conn, const char *dumpdirpath,
-					  SimpleStringList db_exclude_patterns, RestoreOptions *opts,
-					  int numWorkers)
-{
-	SimplePtrList dbname_oid_list = {NULL, NULL};
-	int			num_db_restore = 0;
-	int			num_total_db;
-	int			n_errors_total;
-	int			count = 0;
-	char	   *connected_db = NULL;
-	bool		dumpData = opts->dumpData;
-	bool		dumpSchema = opts->dumpSchema;
-	bool		dumpStatistics = opts->dumpSchema;
-
-	/* Save db name to reuse it for all the database. */
-	if (opts->cparams.dbname)
-		connected_db = opts->cparams.dbname;
-
-	num_total_db = get_dbname_oid_list_from_mfile(dumpdirpath, &dbname_oid_list);
-
-	/* If map.dat has no entries, return after processing global.dat */
-	if (dbname_oid_list.head == NULL)
-		return process_global_sql_commands(conn, dumpdirpath, opts->filename);
-
-	pg_log_info(ngettext("found %d database name in \"%s\"",
-						 "found %d database names in \"%s\"",
-						 num_total_db),
-				num_total_db, "map.dat");
-
-	if (!conn)
-	{
-		pg_log_info("trying to connect to database \"%s\"", "postgres");
-
-		conn = ConnectDatabase("postgres", NULL, opts->cparams.pghost,
-							   opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT,
-							   false, progname, NULL, NULL, NULL, NULL);
-
-		/* Try with template1. */
-		if (!conn)
-		{
-			pg_log_info("trying to connect to database \"%s\"", "template1");
-
-			conn = ConnectDatabase("template1", NULL, opts->cparams.pghost,
-								   opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT,
-								   false, progname, NULL, NULL, NULL, NULL);
-		}
-	}
-
-	/*
-	 * filter the db list according to the exclude patterns
-	 */
-	num_db_restore = get_dbnames_list_to_restore(conn, &dbname_oid_list,
-												 db_exclude_patterns);
-
-	/* Open global.dat file and execute/append all the global sql commands. */
-	n_errors_total = process_global_sql_commands(conn, dumpdirpath, opts->filename);
-
-	/* Close the db connection as we are done with globals and patterns. */
-	if (conn)
-		PQfinish(conn);
-
-	/* Exit if no db needs to be restored. */
-	if (dbname_oid_list.head == NULL || num_db_restore == 0)
-	{
-		pg_log_info(ngettext("no database needs restoring out of %d database",
-							 "no database needs restoring out of %d databases", num_total_db),
-					num_total_db);
-		return n_errors_total;
-	}
-
-	pg_log_info("need to restore %d databases out of %d databases", num_db_restore, num_total_db);
-
-	/*
-	 * We have a list of databases to restore after processing the
-	 * exclude-database switch(es).  Now we can restore them one by one.
-	 */
-	for (SimplePtrListCell *db_cell = dbname_oid_list.head;
-		 db_cell; db_cell = db_cell->next)
-	{
-		DbOidName  *dbidname = (DbOidName *) db_cell->ptr;
-		char		subdirpath[MAXPGPATH];
-		char		subdirdbpath[MAXPGPATH];
-		char		dbfilename[MAXPGPATH];
-		int			n_errors;
-
-		/* ignore dbs marked for skipping */
-		if (dbidname->oid == InvalidOid)
-			continue;
-
-		/*
-		 * We need to reset override_dbname so that objects can be restored
-		 * into an already created database. (used with -d/--dbname option)
-		 */
-		if (opts->cparams.override_dbname)
-		{
-			pfree(opts->cparams.override_dbname);
-			opts->cparams.override_dbname = NULL;
-		}
-
-		snprintf(subdirdbpath, MAXPGPATH, "%s/databases", dumpdirpath);
-
-		/*
-		 * Look for the database dump file/dir. If there is an {oid}.tar or
-		 * {oid}.dmp file, use it. Otherwise try to use a directory called
-		 * {oid}
-		 */
-		snprintf(dbfilename, MAXPGPATH, "%u.tar", dbidname->oid);
-		if (file_exists_in_directory(subdirdbpath, dbfilename))
-			snprintf(subdirpath, MAXPGPATH, "%s/databases/%u.tar", dumpdirpath, dbidname->oid);
-		else
-		{
-			snprintf(dbfilename, MAXPGPATH, "%u.dmp", dbidname->oid);
-
-			if (file_exists_in_directory(subdirdbpath, dbfilename))
-				snprintf(subdirpath, MAXPGPATH, "%s/databases/%u.dmp", dumpdirpath, dbidname->oid);
-			else
-				snprintf(subdirpath, MAXPGPATH, "%s/databases/%u", dumpdirpath, dbidname->oid);
-		}
-
-		pg_log_info("restoring database \"%s\"", dbidname->str);
-
-		/* If database is already created, then don't set createDB flag. */
-		if (opts->cparams.dbname)
-		{
-			PGconn	   *test_conn;
-
-			test_conn = ConnectDatabase(dbidname->str, NULL, opts->cparams.pghost,
-										opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT,
-										false, progname, NULL, NULL, NULL, NULL);
-			if (test_conn)
-			{
-				PQfinish(test_conn);
-
-				/* Use already created database for connection. */
-				opts->createDB = 0;
-				opts->cparams.dbname = dbidname->str;
-			}
-			else
-			{
-				/* we'll have to create it */
-				opts->createDB = 1;
-				opts->cparams.dbname = connected_db;
-			}
-		}
-
-		/*
-		 * Reset flags - might have been reset in pg_backup_archiver.c by the
-		 * previous restore.
-		 */
-		opts->dumpData = dumpData;
-		opts->dumpSchema = dumpSchema;
-		opts->dumpStatistics = dumpStatistics;
-
-		/* Restore the single database. */
-		n_errors = restore_one_database(subdirpath, opts, numWorkers, true, count);
-
-		/* Print a summary of ignored errors during single database restore. */
-		if (n_errors)
-		{
-			n_errors_total += n_errors;
-			pg_log_warning("errors ignored on database \"%s\" restore: %d", dbidname->str, n_errors);
-		}
-
-		count++;
-	}
-
-	/* Log number of processed databases. */
-	pg_log_info("number of restored databases is %d", num_db_restore);
-
-	/* Free dbname and dboid list. */
-	simple_ptr_list_destroy(&dbname_oid_list);
-
-	return n_errors_total;
-}
-
-/*
- * process_global_sql_commands
- *
- * Open global.dat and execute or copy the sql commands one by one.
- *
- * If outfile is not NULL, copy all sql commands into outfile rather than
- * executing them.
- *
- * Returns the number of errors while processing global.dat
- */
-static int
-process_global_sql_commands(PGconn *conn, const char *dumpdirpath, const char *outfile)
-{
-	char		global_file_path[MAXPGPATH];
-	PGresult   *result;
-	StringInfoData sqlstatement,
-				user_create;
-	FILE	   *pfile;
-	int			n_errors = 0;
-
-	snprintf(global_file_path, MAXPGPATH, "%s/global.dat", dumpdirpath);
-
-	/* Open global.dat file. */
-	pfile = fopen(global_file_path, PG_BINARY_R);
-
-	if (pfile == NULL)
-		pg_fatal("could not open file \"%s\": %m", global_file_path);
-
-	/*
-	 * If outfile is given, then just copy all global.dat file data into
-	 * outfile.
-	 */
-	if (outfile)
-	{
-		copy_or_print_global_file(outfile, pfile);
-		return 0;
-	}
-
-	/* Init sqlstatement to append commands. */
-	initStringInfo(&sqlstatement);
-
-	/* creation statement for our current role */
-	initStringInfo(&user_create);
-	appendStringInfoString(&user_create, "CREATE ROLE ");
-	/* should use fmtId here, but we don't know the encoding */
-	appendStringInfoString(&user_create, PQuser(conn));
-	appendStringInfoChar(&user_create, ';');
-
-	/* Process file till EOF and execute sql statements. */
-	while (read_one_statement(&sqlstatement, pfile) != EOF)
-	{
-		/* don't try to create the role we are connected as */
-		if (strstr(sqlstatement.data, user_create.data))
-			continue;
-
-		pg_log_info("executing query: %s", sqlstatement.data);
-		result = PQexec(conn, sqlstatement.data);
-
-		switch (PQresultStatus(result))
-		{
-			case PGRES_COMMAND_OK:
-			case PGRES_TUPLES_OK:
-			case PGRES_EMPTY_QUERY:
-				break;
-			default:
-				n_errors++;
-				pg_log_error("could not execute query: %s", PQerrorMessage(conn));
-				pg_log_error_detail("Command was: %s", sqlstatement.data);
-		}
-		PQclear(result);
-	}
-
-	/* Print a summary of ignored errors during global.dat. */
-	if (n_errors)
-		pg_log_warning(ngettext("ignored %d error in file \"%s\"",
-								"ignored %d errors in file \"%s\"", n_errors),
-					   n_errors, global_file_path);
-	fclose(pfile);
-
-	return n_errors;
-}
-
-/*
- * copy_or_print_global_file
- *
- * Copy global.dat into the output file.  If "-" is used as outfile,
- * then print commands to stdout.
- */
-static void
-copy_or_print_global_file(const char *outfile, FILE *pfile)
-{
-	char		out_file_path[MAXPGPATH];
-	FILE	   *OPF;
-	int			c;
-
-	/* "-" is used for stdout. */
-	if (strcmp(outfile, "-") == 0)
-		OPF = stdout;
-	else
-	{
-		snprintf(out_file_path, MAXPGPATH, "%s", outfile);
-		OPF = fopen(out_file_path, PG_BINARY_W);
-
-		if (OPF == NULL)
-		{
-			fclose(pfile);
-			pg_fatal("could not open file: \"%s\"", outfile);
-		}
-	}
-
-	/* Append global.dat into output file or print to stdout. */
-	while ((c = fgetc(pfile)) != EOF)
-		fputc(c, OPF);
-
-	fclose(pfile);
-
-	/* Close output file. */
-	if (strcmp(outfile, "-") != 0)
-		fclose(OPF);
-}
diff --git a/src/bin/pg_dump/t/001_basic.pl b/src/bin/pg_dump/t/001_basic.pl
index c3c5fae11ea..37d893d5e6a 100644
--- a/src/bin/pg_dump/t/001_basic.pl
+++ b/src/bin/pg_dump/t/001_basic.pl
@@ -237,24 +237,6 @@ command_fails_like(
 	'pg_restore: options -C\/--create and -1\/--single-transaction cannot be used together'
 );
 
-command_fails_like(
-	[ 'pg_restore', '--exclude-database=foo', '--globals-only', '-d', 'xxx' ],
-	qr/\Qpg_restore: error: option --exclude-database cannot be used together with -g\/--globals-only\E/,
-	'pg_restore: option --exclude-database cannot be used together with -g/--globals-only'
-);
-
-command_fails_like(
-	[ 'pg_restore', '--exclude-database=foo', '-d', 'xxx', 'dumpdir' ],
-	qr/\Qpg_restore: error: option --exclude-database can be used only when restoring an archive created by pg_dumpall\E/,
-	'When option --exclude-database is used in pg_restore with dump of pg_dump'
-);
-
-command_fails_like(
-	[ 'pg_restore', '--globals-only', '-d', 'xxx', 'dumpdir' ],
-	qr/\Qpg_restore: error: option -g\/--globals-only can be used only when restoring an archive created by pg_dumpall\E/,
-	'When option --globals-only is not used in pg_restore with dump of pg_dump'
-);
-
 # also fails for -r and -t, but it seems pointless to add more tests for those.
 command_fails_like(
 	[ 'pg_dumpall', '--exclude-database=foo', '--globals-only' ],
@@ -262,8 +244,4 @@ command_fails_like(
 	'pg_dumpall: option --exclude-database cannot be used together with -g/--globals-only'
 );
 
-command_fails_like(
-	[ 'pg_dumpall', '--format', 'x' ],
-	qr/\Qpg_dumpall: error: unrecognized output format "x";\E/,
-	'pg_dumpall: unrecognized output format');
 done_testing();
diff --git a/src/bin/pg_dump/t/006_pg_dumpall.pl b/src/bin/pg_dump/t/006_pg_dumpall.pl
deleted file mode 100644
index c274b777586..00000000000
--- a/src/bin/pg_dump/t/006_pg_dumpall.pl
+++ /dev/null
@@ -1,400 +0,0 @@
-# Copyright (c) 2021-2025, PostgreSQL Global Development Group
-
-use strict;
-use warnings FATAL => 'all';
-
-use PostgreSQL::Test::Cluster;
-use PostgreSQL::Test::Utils;
-use Test::More;
-
-my $tempdir = PostgreSQL::Test::Utils::tempdir;
-my $run_db = 'postgres';
-my $sep = $windows_os ? "\\" : "/";
-
-# Tablespace locations used by "restore_tablespace" test case.
-my $tablespace1 = "${tempdir}${sep}tbl1";
-my $tablespace2 = "${tempdir}${sep}tbl2";
-mkdir($tablespace1) || die "mkdir $tablespace1 $!";
-mkdir($tablespace2) || die "mkdir $tablespace2 $!";
-
-# Scape tablespace locations on Windows.
-$tablespace1 = $windows_os ? ($tablespace1 =~ s/\\/\\\\/gr) : $tablespace1;
-$tablespace2 = $windows_os ? ($tablespace2 =~ s/\\/\\\\/gr) : $tablespace2;
-
-# Where pg_dumpall will be executed.
-my $node = PostgreSQL::Test::Cluster->new('node');
-$node->init;
-$node->start;
-
-
-###############################################################
-# Definition of the pg_dumpall test cases to run.
-#
-# Each of these test cases are named and those names are used for fail
-# reporting and also to save the dump and restore information needed for the
-# test to assert.
-#
-# The "setup_sql" is a psql valid script that contains SQL commands to execute
-# before of actually execute the tests. The setups are all executed before of
-# any test execution.
-#
-# The "dump_cmd" and "restore_cmd" are the commands that will be executed. The
-# "restore_cmd" must have the --file flag to save the restore output so that we
-# can assert on it.
-#
-# The "like" and "unlike" is a regexp that is used to match the pg_restore
-# output. It must have at least one of then filled per test cases but it also
-# can have both. See "excluding_databases" test case for example.
-my %pgdumpall_runs = (
-	restore_roles => {
-		setup_sql => '
-		CREATE ROLE dumpall WITH ENCRYPTED PASSWORD \'admin\' SUPERUSER;
-		CREATE ROLE dumpall2 WITH REPLICATION CONNECTION LIMIT 10;',
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_roles",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_roles.sql",
-			"$tempdir/restore_roles",
-		],
-		like => qr/
-			^\s*\QCREATE ROLE dumpall;\E\s*\n
-			\s*\QALTER ROLE dumpall WITH SUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS PASSWORD 'SCRAM-SHA-256\E
-			[^']+';\s*\n
-			\s*\QCREATE ROLE dumpall2;\E
-			\s*\QALTER ROLE dumpall2 WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN REPLICATION NOBYPASSRLS CONNECTION LIMIT 10;\E
-		/xm
-	},
-
-	restore_tablespace => {
-		setup_sql => "
-		CREATE ROLE tap;
-		CREATE TABLESPACE tbl1 OWNER tap LOCATION '$tablespace1';
-		CREATE TABLESPACE tbl2 OWNER tap LOCATION '$tablespace2' WITH (seq_page_cost=1.0);",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_tablespace",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_tablespace.sql",
-			"$tempdir/restore_tablespace",
-		],
-		# Match "E" as optional since it is added on LOCATION when running on
-		# Windows.
-		like => qr/^
-			\n\QCREATE TABLESPACE tbl1 OWNER tap LOCATION \E(?:E)?\Q'$tablespace1';\E
-			\n\QCREATE TABLESPACE tbl2 OWNER tap LOCATION \E(?:E)?\Q'$tablespace2';\E
-			\n\QALTER TABLESPACE tbl2 SET (seq_page_cost=1.0);\E
-		/xm,
-	},
-
-	restore_grants => {
-		setup_sql => "
-		CREATE DATABASE tapgrantsdb;
-		CREATE SCHEMA private;
-		CREATE SEQUENCE serial START 101;
-		CREATE FUNCTION fn() RETURNS void AS \$\$
-		BEGIN
-		END;
-		\$\$ LANGUAGE plpgsql;
-		CREATE ROLE super;
-		CREATE ROLE grant1;
-		CREATE ROLE grant2;
-		CREATE ROLE grant3;
-		CREATE ROLE grant4;
-		CREATE ROLE grant5;
-		CREATE ROLE grant6;
-		CREATE ROLE grant7;
-		CREATE ROLE grant8;
-
-		CREATE TABLE t (id int);
-		INSERT INTO t VALUES (1), (2), (3), (4);
-
-		GRANT SELECT ON TABLE t TO grant1;
-		GRANT INSERT ON TABLE t TO grant2;
-		GRANT ALL PRIVILEGES ON TABLE t to grant3;
-		GRANT CONNECT, CREATE ON DATABASE tapgrantsdb TO grant4;
-		GRANT USAGE, CREATE ON SCHEMA private TO grant5;
-		GRANT USAGE, SELECT, UPDATE ON SEQUENCE serial TO grant6;
-		GRANT super TO grant7;
-		GRANT EXECUTE ON FUNCTION fn() TO grant8;
-		",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_grants",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_grants.sql",
-			"$tempdir/restore_grants",
-		],
-		like => qr/^
-			\n\QGRANT super TO grant7 WITH INHERIT TRUE GRANTED BY\E
-			(.*\n)*
-			\n\QGRANT ALL ON SCHEMA private TO grant5;\E
-			(.*\n)*
-			\n\QGRANT ALL ON FUNCTION public.fn() TO grant8;\E
-			(.*\n)*
-			\n\QGRANT ALL ON SEQUENCE public.serial TO grant6;\E
-			(.*\n)*
-			\n\QGRANT SELECT ON TABLE public.t TO grant1;\E
-			\n\QGRANT INSERT ON TABLE public.t TO grant2;\E
-			\n\QGRANT ALL ON TABLE public.t TO grant3;\E
-			(.*\n)*
-			\n\QGRANT CREATE,CONNECT ON DATABASE tapgrantsdb TO grant4;\E
-		/xm,
-	},
-
-	excluding_databases => {
-		setup_sql => 'CREATE DATABASE db1;
-		\c db1
-		CREATE TABLE t1 (id int);
-		INSERT INTO t1 VALUES (1), (2), (3), (4);
-		CREATE TABLE t2 (id int);
-		INSERT INTO t2 VALUES (1), (2), (3), (4);
-
-		CREATE DATABASE db2;
-		\c db2
-		CREATE TABLE t3 (id int);
-		INSERT INTO t3 VALUES (1), (2), (3), (4);
-		CREATE TABLE t4 (id int);
-		INSERT INTO t4 VALUES (1), (2), (3), (4);
-
-		CREATE DATABASE dbex3;
-		\c dbex3
-		CREATE TABLE t5 (id int);
-		INSERT INTO t5 VALUES (1), (2), (3), (4);
-		CREATE TABLE t6 (id int);
-		INSERT INTO t6 VALUES (1), (2), (3), (4);
-
-		CREATE DATABASE dbex4;
-		\c dbex4
-		CREATE TABLE t7 (id int);
-		INSERT INTO t7 VALUES (1), (2), (3), (4);
-		CREATE TABLE t8 (id int);
-		INSERT INTO t8 VALUES (1), (2), (3), (4);
-
-		CREATE DATABASE db5;
-		\c db5
-		CREATE TABLE t9 (id int);
-		INSERT INTO t9 VALUES (1), (2), (3), (4);
-		CREATE TABLE t10 (id int);
-		INSERT INTO t10 VALUES (1), (2), (3), (4);
-		',
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/excluding_databases",
-			'--exclude-database' => 'dbex*',
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'directory',
-			'--file' => "$tempdir/excluding_databases.sql",
-			'--exclude-database' => 'db5',
-			"$tempdir/excluding_databases",
-		],
-		like => qr/^
-			\n\QCREATE DATABASE db1\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t1 (\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t2 (\E
-			(.*\n)*
-			\n\QCREATE DATABASE db2\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t3 (\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t4 (/xm,
-		unlike => qr/^
-			\n\QCREATE DATABASE db3\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t5 (\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t6 (\E
-			(.*\n)*
-			\n\QCREATE DATABASE db4\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t7 (\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t8 (\E
-			\n\QCREATE DATABASE db5\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t9 (\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t10 (\E
-		/xm,
-	},
-
-	format_directory => {
-		setup_sql => "CREATE TABLE format_directory(a int, b boolean, c text);
-		INSERT INTO format_directory VALUES (1, true, 'name1'), (2, false, 'name2');",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/format_directory",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'directory',
-			'--file' => "$tempdir/format_directory.sql",
-			"$tempdir/format_directory",
-		],
-		like => qr/^\n\QCOPY public.format_directory (a, b, c) FROM stdin;/xm
-	},
-
-	format_tar => {
-		setup_sql => "CREATE TABLE format_tar(a int, b boolean, c text);
-		INSERT INTO format_tar VALUES (1, false, 'name3'), (2, true, 'name4');",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'tar',
-			'--file' => "$tempdir/format_tar",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'tar',
-			'--file' => "$tempdir/format_tar.sql",
-			"$tempdir/format_tar",
-		],
-		like => qr/^\n\QCOPY public.format_tar (a, b, c) FROM stdin;/xm
-	},
-
-	format_custom => {
-		setup_sql => "CREATE TABLE format_custom(a int, b boolean, c text);
-		INSERT INTO format_custom VALUES (1, false, 'name5'), (2, true, 'name6');",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'custom',
-			'--file' => "$tempdir/format_custom",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'custom',
-			'--file' => "$tempdir/format_custom.sql",
-			"$tempdir/format_custom",
-		],
-		like => qr/^ \n\QCOPY public.format_custom (a, b, c) FROM stdin;/xm
-	},
-
-	dump_globals_only => {
-		setup_sql => "CREATE TABLE format_dir(a int, b boolean, c text);
-		INSERT INTO format_dir VALUES (1, false, 'name5'), (2, true, 'name6');",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--globals-only',
-			'--file' => "$tempdir/dump_globals_only",
-		],
-		restore_cmd => [
-			'pg_restore', '-C', '--globals-only',
-			'--format' => 'directory',
-			'--file' => "$tempdir/dump_globals_only.sql",
-			"$tempdir/dump_globals_only",
-		],
-		like => qr/
-            ^\s*\QCREATE ROLE dumpall;\E\s*\n
-			/xm
-	},);
-
-# First execute the setup_sql
-foreach my $run (sort keys %pgdumpall_runs)
-{
-	if ($pgdumpall_runs{$run}->{setup_sql})
-	{
-		$node->safe_psql($run_db, $pgdumpall_runs{$run}->{setup_sql});
-	}
-}
-
-# Execute the tests
-foreach my $run (sort keys %pgdumpall_runs)
-{
-	# Create a new target cluster to pg_restore each test case run so that we
-	# don't need to take care of the cleanup from the target cluster after each
-	# run.
-	my $target_node = PostgreSQL::Test::Cluster->new("target_$run");
-	$target_node->init;
-	$target_node->start;
-
-	# Dumpall from node cluster.
-	$node->command_ok(\@{ $pgdumpall_runs{$run}->{dump_cmd} },
-		"$run: pg_dumpall runs");
-
-	# Restore the dump on "target_node" cluster.
-	my @restore_cmd = (
-		@{ $pgdumpall_runs{$run}->{restore_cmd} },
-		'--host', $target_node->host, '--port', $target_node->port);
-
-	my ($stdout, $stderr) = run_command(\@restore_cmd);
-
-	# pg_restore --file output file.
-	my $output_file = slurp_file("$tempdir/${run}.sql");
-
-	if (   !($pgdumpall_runs{$run}->{like})
-		&& !($pgdumpall_runs{$run}->{unlike}))
-	{
-		die "missing \"like\" or \"unlike\" in test \"$run\"";
-	}
-
-	if ($pgdumpall_runs{$run}->{like})
-	{
-		like($output_file, $pgdumpall_runs{$run}->{like}, "should dump $run");
-	}
-
-	if ($pgdumpall_runs{$run}->{unlike})
-	{
-		unlike(
-			$output_file,
-			$pgdumpall_runs{$run}->{unlike},
-			"should not dump $run");
-	}
-}
-
-# Some negative test case with dump of pg_dumpall and restore using pg_restore
-# test case 1: when -C is not used in pg_restore with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom",
-		'--format' => 'custom',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: option -C\/--create must be specified when restoring an archive created by pg_dumpall\E/,
-	'When -C is not used in pg_restore with dump of pg_dumpall');
-
-# test case 2: When --list option is used with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--list',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: option -l\/--list cannot be used when restoring an archive created by pg_dumpall\E/,
-	'When --list is used in pg_restore with dump of pg_dumpall');
-
-# test case 3: When non-exist database is given with -d option
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'-d' => 'dbpq',
-	],
-	qr/\Qpg_restore: error: could not connect to database "dbpq"\E/,
-	'When non-existent database is given with -d option in pg_restore with dump of pg_dumpall'
-);
-
-$node->stop('fast');
-
-done_testing();

---

From: Noah Misch <[email-masked]>
Date: Tue, 29 Jul 2025 13:34:25 -0700

On Tue, Jul 29, 2025 at 04:09:13PM -0400, Andrew Dunstan wrote:
> here's a reversion patch for master.

>     This reverts parts or all of the following commits:

I briefly looked through this.  The biggest non-reverted part is, I think,
c1da728 "Move common pg_dump code related to connections to a new file".
Refraining from a revert of that one is defensible.

>  	printf(_("  %s [OPTION]... [DBNAME]\n"), progname);

I think commit dec6643487b, which e.g. decided to standardize on the term
"export" for these programs, was independent of $SUBJECT.

---

From: Andrew Dunstan <[email-masked]>
Date: Tue, 29 Jul 2025 21:07:10 -0400

> Refraining from a revert of that one is defensible.

Yes, that was deliberate, since we intend to use it in the same way when 
we redo this.

> "export" for these programs, was independent of $SUBJECT.

OK, thanks for looking. Will fix.

cheers

andrew

---

From: Andrew Dunstan <[email-masked]>
Date: Wed, 30 Jul 2025 14:51:59 -0400

On 2025-07-29 Tu 4:09 PM, Andrew Dunstan wrote:
> here's a reversion patch for master. It applies cleanly to release 18 
> as well. Thanks to Mahendra Singh Thalor for helping me sanity check 
> it (Any issues are of course my responsibility)
>
>

OK, now that's reverted we should discuss how to proceed. I had two 
thoughts - we could use invent a JSON format for the globals, or we 
could just use the existing archive format. I think the archive format 
is pretty flexible, and should be able to accommodate this. The downside 
is it's not humanly readable. The upside is that we don't need to do 
anything special either to write it or parse it.

There might also be other reasonable options. But I think we should stay 
out of the business of using custom code to parse text.

cheers

andrew

---

From: Christoph Berg <[email-masked]>
Date: Thu, 31 Jul 2025 11:44:57 +0200

The 18 branch is broken for apt.pg.o:

00:54:18 #   Failed test 'dump_globals_only: pg_dumpall runs'
00:54:18 #   at t/006_pg_dumpall.pl line 329.
00:54:18 # Tests were run but no plan was declared and done_testing() was not seen.
00:54:18 # Looks like your test exited with 2 just after 1.
00:54:18 t/006_pg_dumpall.pl ........... 
00:54:18 # initializing database system by copying initdb template
00:54:18 # initializing database system by copying initdb template
00:54:18 not ok 1 - dump_globals_only: pg_dumpall runs
00:54:18 Dubious, test returned 2 (wstat 512, 0x200)
00:54:18 Failed 1/1 subtests 

Devel is ok.

Christoph

---

From: Andrew Dunstan <[email-masked]>
Date: Thu, 31 Jul 2025 09:40:25 -0400

> 00:54:18 Failed 1/1 subtests

That file was deleted by the revert. Maybe you have a cache problem?

cheers

andrew

---

From: Christoph Berg <[email-masked]>
Date: Thu, 31 Jul 2025 15:52:36 +0200

Re: Andrew Dunstan
> That file was deleted by the revert. Maybe you have a cache problem?

Oh right. This was caused by our snapshot builds using the latest
tarball (if available) and putting a patch on top that. I've now
bumped the upstream version to 18~beta3, this should avoid the
problem.

Sorry for the noise, and thanks for the pointer!

Christoph

---

From: Nathan Bossart <[email-masked]>
Date: Thu, 31 Jul 2025 13:22:38 -0500

On Wed, Jul 30, 2025 at 02:51:59PM -0400, Andrew Dunstan wrote:
> OK, now that's reverted...

Can we close the open item for this one now?  Or is there something else
remaining?

-- 
nathan

---

From: Andrew Dunstan <[email-masked]>
Date: Thu, 31 Jul 2025 15:06:07 -0400

> Can we close the open item for this one now?  Or is there something else

Thanks for the reminder. I have marked the item as fixed.

cheers

andrew

---

From: Noah Misch <[email-masked]>
Date: Sat, 23 Aug 2025 18:08:11 -0700

On Wed, Jul 30, 2025 at 02:51:59PM -0400, Andrew Dunstan wrote:
> OK, now that's reverted we should discuss how to proceed. I had two thoughts
> - we could use invent a JSON format for the globals, or we could just use
> the existing archive format. I think the archive format is pretty flexible,
> and should be able to accommodate this. The downside is it's not humanly
> readable. The upside is that we don't need to do anything special either to
> write it or parse it.

I would first try to use the existing archiver API, because that makes it
harder to miss bugs.  Any tension between that API and pg_dumpall is likely to
have corresponding tension on the pg_restore side.  Resolving that tension
will reveal much of the project's scope that remained hidden during the v18
attempt.  Perhaps more important than that, using the archiver API means
future pg_dump and pg_restore options are more likely to cooperate properly
with $SUBJECT.  In other words, I want it to be hard to add pg_dump/pg_restore
features that malfunction only for $SUBJECT archives.  The strength of the
archiver architecture shows in how rarely new features need format-specific
logic and how rarely format-specific bugs get reported.  We've had little or
no trouble with e.g. bugs that appear in -Fd but not in -Fc.

If pg_backup_json.c emerged as a new backend to the archiver API, I'd not have
concerns about that.  But a JSON format specific to $SUBJECT sounds like a
recipe for bugs.

> There might also be other reasonable options. But I think we should stay out
> of the business of using custom code to parse text.

Agreed.

---

From: Andrew Dunstan <[email-masked]>
Date: Sun, 24 Aug 2025 12:42:56 -0400

> no trouble with e.g. bugs that appear in -Fd but not in -Fc.

Yeah, that's what we're going to try.

cheers

andrew

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Wed, 15 Oct 2025 23:05:58 +0530

> Yeah, that's what we're going to try.

Thanks Andrew, Noah and all others for feedback.

Based on the above suggestions and discussions, I removed sql commands
from the global.dat file. For global commands, now we are making
toc.dat/toc.dmp/toc.tar file based on format specified and based on
format specified, we are making archive entries for these global
commands. By this approach, we removed the hard-coded parsing part of
the global.dat file and we are able to skip DROP DATABASE with the
globals-only option.

Here, I am attaching a patch for review, testing and feedback. This is
a WIP patch. I will do some more code cleanup and will add some more
comments also. Please review this and let me know design level
feedback. Thanks Tushar Ahuja for some internal testing and feedback.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Thu, 16 Oct 2025 16:24:42 +0530

> feedback. Thanks Tushar Ahuja for some internal testing and feedback.

Hi,
Here, I am attaching an updated patch. In offline discussion, Andrew
reported some test-case failures(Thanks Andrew). I fixed those.
Please let me know feedback for the patch.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Tue, 28 Oct 2025 11:32:24 +0530

> Please let me know feedback for the patch.

Hi,
Here I am attaching a re-based patch as v02 was failing on head.
Thanks Tushar for the testing.
Please review this and let me know feedback.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Fri, 31 Oct 2025 14:50:52 +0530

> Please review this and let me know feedback.

Hi all,
Here I am attaching an updated patch for review and testing. Based on
some offline comments by Andrew, I did some code cleanup.
Please consider this patch for feedback.

---

From: Vaibhav Dalvi <[email-masked]>
Date: Mon, 3 Nov 2025 12:05:46 +0530

Hi Mahendra,

Thank you for your work on this feature.
I have just begun reviewing the latest patch and
encountered the following errors during the initial setup:

```
$ ./db/bin/pg_restore testdump_dir -C -d postgres -F d -p 5556
pg_restore: error: could not execute query: ERROR: syntax error at or near
"\\"
LINE 1: \restrict aO9K1gzVZTlafidF5fWx8ADGzUnIiAcguFz5qskGaFDygTCjCj...
^
Command was: \restrict
aO9K1gzVZTlafidF5fWx8ADGzUnIiAcguFz5qskGaFDygTCjCj9vg3Xxys1b3hb

pg_restore: error: could not execute query: ERROR: syntax error at or near
"\\"
LINE 1: \unrestrict aO9K1gzVZTlafidF5fWx8ADGzUnIiAcguFz5qskGaFDygTCj...
^
Command was: \unrestrict
aO9K1gzVZTlafidF5fWx8ADGzUnIiAcguFz5qskGaFDygTCjCj9vg3Xxys1b3hb

pg_restore: error: could not execute query: ERROR: syntax error at or near
"\\"
LINE 1: \connect template1
^
Command was: \connect template1

pg_restore: error: could not execute query: ERROR: syntax error at or near
"\\"
LINE 1: \connect postgres
^
Command was: \connect postgres
```
To cross-check tried with plain dump(with pg_dumpall) and
 restored(SQL file restore) without patch and didn't get above
connection errors.

It appears there might be an issue with the dump file itself.
Please note that this is my first observation as I have just
started the review. I will continue with my assessment.

Regards,
Vaibhav Dalvi
EnterpriseDB

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Mon, 3 Nov 2025 17:24:37 +0530

On Mon, 3 Nov 2025 at 12:06, Vaibhav Dalvi <[email-masked]>
wrote:
> pg_restore: error: could not execute query: ERROR: syntax error at or
near "\\"
>
> pg_restore: error: could not execute query: ERROR: syntax error at or
near "\\"
>
> pg_restore: error: could not execute query: ERROR: syntax error at or
near "\\"
> pg_restore: error: could not execute query: ERROR: syntax error at or
near "\\"
> started the review. I will continue with my assessment.

Thanks Vaibhav for the review.
This change was added by me in v04. Only in the case of a file, we should
restore these commands. Attached patch is fixing the same.

If we dump and restore the same file with the same user, then we will get
an error of ROLE CREATE as the same role is already created. I think,
either we can ignore this error, or we can keep it as a restore can be done
with different users.

> mst@localhost bin]$ ./pg_restore d1  -C -d postgres
> pg_restore: error: could not execute query: ERROR:  role "mst" already
> exists
> Command was: CREATE ROLE mst;
> ALTER ROLE mst WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN
> REPLICATION BYPASSRLS;
>
>
> pg_restore: warning: errors ignored on restore: 1

---

From: Vaibhav Dalvi <[email-masked]>
Date: Mon, 3 Nov 2025 17:29:30 +0530

Hi Mahendra,

I have a few more review comments regarding the patch:

1. Is the following change in `src/bin/pg_dump/connectdb.c` intentional?

```
--- a/src/bin/pg_dump/connectdb.c
+++ b/src/bin/pg_dump/connectdb.c
@@ -287,7 +287,6 @@ executeQuery(PGconn *conn, const char *query)
{
pg_log_error("query failed: %s", PQerrorMessage(conn));
pg_log_error_detail("Query was: %s", query);
- PQfinish(conn);
exit_nicely(1);
}
```

When I re-added `PQfinish(conn);`, the regression tests passed successfully.
The `git diff` shows:

```
diff --git a/src/bin/pg_dump/connectdb.c b/src/bin/pg_dump/connectdb.c
index f44a8a45fca..d55d53dbeea 100644
--- a/src/bin/pg_dump/connectdb.c
+++ b/src/bin/pg_dump/connectdb.c
@@ -287,6 +287,7 @@ executeQuery(PGconn *conn, const char *query)
{
pg_log_error("query failed: %s", PQerrorMessage(conn));
pg_log_error_detail("Query was: %s", query);
+ PQfinish(conn);
exit_nicely(1);
}
```

If this change is intentional, could you please add a test case to justify
or demonstrate the need for it?

2. Please remove the extra blank line before `static void usage(const char
*progname);`.

```
+

3. There is an unnecessary line deletion that does not appear to be related
to this feature:

```

Could this deletion be part of a separate cleanup?

Regards,
Vaibhav Dalvi

---

From: tushar <[email-masked]>
Date: Tue, 4 Nov 2025 18:23:30 +0530

On Mon, Nov 3, 2025 at 5:25 PM Mahendra Singh Thalor <[email-masked]>
wrote:

> Thanks Mahendra, I am getting a segmentation fault against v05 patch.

[edb@1a1c15437e7c bin]$ ./pg_dumpall -Ft   --file  a.3 -v
pg_dumpall: executing SELECT pg_catalog.set_config('search_path', '',
false);
Segmentation fault

Issue is coming with all output file formats -F[t/c/d] except plain

regards,

---

From: Andrew Dunstan <[email-masked]>
Date: Tue, 4 Nov 2025 11:55:28 -0500

On 2025-11-04 Tu 7:53 AM, tushar wrote:
>
>
> On Mon, Nov 3, 2025 at 5:25 PM Mahendra Singh Thalor 
>     On Mon, 3 Nov 2025 at 12:06, Vaibhav Dalvi
>     <[email-masked]> wrote:
>     >
>     > Hi Mahendra,
>     >
>     > pg_restore: error: could not execute query: ERROR: syntax error
>     at or near "\\"
>     > LINE 1: \restrict
>     aO9K1gzVZTlafidF5fWx8ADGzUnIiAcguFz5qskGaFDygTCjCj...
>     > ^
>     > Command was: \restrict
>     > pg_restore: error: could not execute query: ERROR: syntax error
>     at or near "\\"
>     > LINE 1: \unrestrict
>     aO9K1gzVZTlafidF5fWx8ADGzUnIiAcguFz5qskGaFDygTCj...
>     > ^
>     > Command was: \unrestrict
>     > pg_restore: error: could not execute query: ERROR: syntax error
>     at or near "\\"
>     > pg_restore: error: could not execute query: ERROR: syntax error
>     at or near "\\"
>     This change was added by me in v04. Only in the case of a file, we
>     should restore these commands. Attached patch is fixing the same.
>

Yeah, I don't think we need to dump the timestamp in non-text modes. 
This fix worked for me:

diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 601b9f9738e..f66cc26d9a2 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -638,7 +638,7 @@ main(int argc, char *argv[])
     if (quote_all_identifiers)
         executeCommand(conn, "SET quote_all_identifiers = true");

-   if (verbose)
+   if (verbose && archDumpFormat == archNull)
         dumpTimestamp("Started on");

     /* create a archive file for global commands. */
@@ -2258,6 +2258,7 @@ createDumpId(void)
  static void
  createOneArchiveEntry(const char *query, const char *tag)
  {
+   Assert(fout != NULL);
     ArchiveEntry(fout,
             nilCatalogId, /* catalog ID */
             createDumpId(), /* dump ID */

cheers

andrew

---

From: Vaibhav Dalvi <[email-masked]>
Date: Wed, 5 Nov 2025 12:29:53 +0530

Hi Mahendra,

Thank you for the fix. Please find my further review comments below.

### Restrict-Key Option

The `--restrict-key` option is currently being accepted by
`pg_dumpall` even when non-plain formats are specified,
which contradicts its intended use only with the plain format.

For example:

```
$ ./db/bin/pg_dump --format=d -f testdump_dir --restrict-key=RESTRICT_KEY
pg_dump: error: option --restrict-key can only be used with --format=plain
$ ./db/bin/pg_dumpall --format=d -f testdump_dir --restrict-key=RESTRICT_KEY
pg_dumpall: error: invalid restrict key
```

I have attached a delta patch that addresses the issue with the
`--restrict-key` option. It would be beneficial to include a dedicated
test case for this check.

### Use of Dump Options Structure (dopt)

Please ensure consistency by utilizing the main dump options
structure (`dopt`) instead of declaring and using individual variables
where the structure already provides fields. For example, the
`output_clean` variable seems redundant here:

```c
case 'c':
output_clean = true;
dopt.outputClean = 1;
break;
```

In my attached delta file, I have replaced the unnecessary
`restrict_key` variable with `dopt.restrict_key`.

### Cosmetic Issues

1. Please review the spacing around the pointer:
```c
+ ((ArchiveHandle * )fout) ->connection = conn;
+ ((ArchiveHandle * ) fout) -> public.numWorkers = 1;
```
2. Please be consistent with the punctuation of single-line comments;
    some end with a full stop (`.`) and others do not.
3. In the SGML documentation changes, some new statements start
    with one space, and others start with two. Please adhere to a single
    standard for indentation across the patch.

Regards,
Vaibhav
EnterpriseDB

On Mon, Nov 3, 2025 at 5:24 PM Mahendra Singh Thalor <[email-masked]>
wrote:

> > On Fri, Oct 31, 2025 at 2:51 PM Mahendra Singh Thalor <
> [email-masked]> wrote:
> >>

---

From: Vaibhav Dalvi <[email-masked]>
Date: Wed, 5 Nov 2025 18:46:38 +0530

Hi Mahendra,

Here are a few more comments following my review of the patch:

### 1\. Incorrect Comment for `-g` (globals-only) Option

The comment for the `-g` case in the code states that it restores the
`global.dat` file. However, in the non-text dump output, I only see the
following files: `databases`, `map.dat`, and `toc.dat`.

```c
+ case 'g':

Please update this comment to accurately reflect the file being restored
(e.g., `toc.dat` or the global objects within the archive).

### 2\. Incorrect Order of `case` Statements in `pg_restore.c`

The new `case 7` statement in `pg_restore.c` appears to be
inserted before `case 6`, disrupting the numerical order.

```c
+ case 7: /* database patterns to skip */
+ simple_string_list_append(&db_exclude_patterns, optarg);
+ break;

case 6:
opts->restrict_key = pg_strdup(optarg);
```

Please re-order the `case` statements so they follow ascending
numerical order.

### 3\. Missing Example in SGML Documentation

The SGML documentation for `pg_dumpall` is missing an explicit
example demonstrating its use with non-text formats (e.g., directory
format).
It would be beneficial to include a clear example for this new feature.

### 4\. Cosmetic Issues

Please address the following minor stylistic points:

Please ensure the function signatures
adhere to standard coding style, particularly for line wrapping.
The following lines seem to have inconsistent indentation:

```c
static int restore_global_objects(const char *inputFileSpec, RestoreOptions
*opts,
int numWorkers, bool append_data, int num, bool globals_only);
static int restore_all_databases(const char *inputFileSpec,
SimpleStringList db_exclude_patterns, RestoreOptions *opts, int numWorkers);
```

Please fix instances where the 80-character line limit is
crossed, such as in the example below:

```c
n_errors = restore_one_database(subdirpath, opts, numWorkers, true, 1,
false);
```

I believe this concludes my formal review.

Thanks,
Vaibhav Dalvi

On Wed, Nov 5, 2025 at 12:29 PM Vaibhav Dalvi <
[email-masked]> wrote:

>> On Mon, 3 Nov 2025 at 12:06, Vaibhav Dalvi <

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Thu, 6 Nov 2025 11:03:38 +0530

Thanks Vaibhav, Tushar and Andrew for the review and testing.

> +++ b/src/bin/pg_dump/connectdb.c

Yes, we need this. If there is any error, then we were trying to
disconnect the database in 2 places so we were getting a crash. I will
try to reproduce crashe without this patch and will respond.

On Tue, 4 Nov 2025 at 18:23, tushar <[email-masked]> wrote:
> pg_dumpall: executing SELECT pg_catalog.set_config('search_path', '', false);
> Segmentation fault
>

Thanks for the report. Fixed,

On Tue, 4 Nov 2025 at 22:25, Andrew Dunstan <[email-masked]> wrote:
> Yeah, I don't think we need to dump the timestamp in non-text modes. This fix worked for me:
>
>

Thanks Andrew. Yes, we should not dump timestamp in non-text modes.

Fixed.

Fixed.

Fixed.

Fixed.

> example demonstrating its use with non-text formats (e.g., directory format).

I think we don't add such cases in doc. We already added test cases in
code. If others also feel that we should add a test case in SGML, then
I will update the doc with the test case.

> static int restore_global_objects(const char *inputFileSpec, RestoreOptions *opts,

Fixed.

>
> ```c
> n_errors = restore_one_database(subdirpath, opts, numWorkers, true, 1, false);
> ```
>
> On Wed, Nov 5, 2025 at 12:29 PM Vaibhav Dalvi <[email-masked]> wrote:
>>
>> Hi Mahendra,
>>

We should dump restrict-key with all modes as we need to restore with
the "-f file" option in text mode.
Ex: pg_dumpall --format=d -f testdump_dir
and restore::: pg_restore testdump_dir -d dabasename -C -f testdumpfile
(In testdumpfile, we will generate commands from archive dump)

So I am not merging this delat patch.

>> `output_clean` variable seems redundant here:

output_clean is not added by this patch. I will analyse this comment
and will respond in the next update.

>> `restrict_key` variable with `dopt.restrict_key`.

This is also not part of this patch. If you feel to add this in DOPT,
please suggest in separate thread.

Fixed.

>>     some end with a full stop (`.`) and others do not.

Based on nearby code comments, I made changes. I will try to fix these
inconsistencies..

>>     standard for indentation across the patch.

Okay. I will fix these.

>>
>> Regards,
>> Vaibhav
>> EnterpriseDB
>>
>> On Mon, Nov 3, 2025 at 5:24 PM Mahendra Singh Thalor <[email-masked]> wrote:
>>>
>>> On Mon, 3 Nov 2025 at 12:06, Vaibhav Dalvi <[email-masked]> wrote:
>>> >
>>> > Hi Mahendra,
>>> >
>>> > pg_restore: error: could not execute query: ERROR: syntax error at or near "\\"
>>> > Command was: \restrict aO9K1gzVZTlafidF5fWx8ADGzUnIiAcguFz5qskGaFDygTCjCj9vg3Xxys1b3hb
>>> >
>>> > pg_restore: error: could not execute query: ERROR: syntax error at or near "\\"
>>> > Command was: \unrestrict aO9K1gzVZTlafidF5fWx8ADGzUnIiAcguFz5qskGaFDygTCjCj9vg3Xxys1b3hb
>>> >
>>> > pg_restore: error: could not execute query: ERROR: syntax error at or near "\\"
>>> > pg_restore: error: could not execute query: ERROR: syntax error at or near "\\"
>>> This change was added by me in v04. Only in the case of a file, we should restore these commands. Attached patch is fixing the same.
>>>
>>> If we dump and restore the same file with the same user, then we will get an error of ROLE CREATE as the same role is already created. I think, either we can ignore this error, or we can keep it as a restore can be done with different users.
>>>>
>>>> pg_restore: error: could not execute query: ERROR:  role "mst" already exists
>>>> ALTER ROLE mst WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION BYPASSRLS;
>>>>
>>>>
>>> > On Fri, Oct 31, 2025 at 2:51 PM Mahendra Singh Thalor <[email-masked]> wrote:
>>> >>

Here, I am attaching an updated patch for the review and testing.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Tue, 11 Nov 2025 11:29:04 +0530

Hi,

FIX: as suggested by Vaibhav, added error for --restrict-key option
with non-text format.

---

From: Andrew Dunstan <[email-masked]>
Date: Tue, 11 Nov 2025 10:11:52 -0500

> FIX: as suggested by Vaibhav, added error for --restrict-key option

Regarding the name and format of the globals toc file, I'm inclined to 
think we should always use custom format, regardless of whether the 
individual databases will be in custom, tar or directory formats, and 
that it should be called something distinguishable, e.g. toc.glo.

cheers

andrew

---

From: tushar <[email-masked]>
Date: Mon, 17 Nov 2025 19:38:32 +0530

> > Thanks Vaibhav, Tushar and Andrew for the review and testing.
Thanks Mahendra, getting this error against v07 series patch

 [edb@1a1c15437e7c bin]$ ./pg_dumpall -Ft -f tar.dumpc  -v
pg_dumpall: executing SELECT pg_catalog.set_config('search_path', '',
false);
pg_dumpall: pg_dumpall.c:2256: createOneArchiveEntry: Assertion `fout !=
((void *)0)' failed.
Aborted

regards,
Tushar Ahuja
EDB  https://www.enterprisedb.com/

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Mon, 17 Nov 2025 22:45:28 +0530

Thanks Andrew for the review.
> that it should be called something distinguishable, e.g. toc.glo.

I also agree with your point. Fixed.

On Mon, 17 Nov 2025 at 19:38, tushar <[email-masked]> wrote:
>
>
>
> On Tue, Nov 11, 2025 at 11:29 AM Mahendra Singh Thalor <[email-masked]> wrote:
>>
> pg_dumpall: pg_dumpall.c:2256: createOneArchiveEntry: Assertion `fout != ((void *)0)' failed.
> Aborted
>
> regards,

Thanks Tushar for the report. Fixed.

---

From: Vaibhav Dalvi <[email-masked]>
Date: Tue, 18 Nov 2025 16:04:22 +0530

Hi Mahendra,

Thanks Mahendra for working on this.

Looks like my previous comment below is not addressed:
1.

> `output_clean` variable seems redundant here:

I agree that the output_clean variable is not added by your patch
but the introduction of dopt by your patch makes it redundant because
dopt has dopt.outputClean. Please look at below code from pg_dump.c
for the reference:

 case 'c': /* clean (i.e., drop) schema prior to create */
dopt.outputClean = 1;
break;
 case 25:
dopt.restrict_key = pg_strdup(optarg);
break;

2.

> It would be beneficial to include a clear example for this new feature.

I think pg_dumpall should have separate examples similar to pg_dump
rather than referencing the pg_dump example because pg_dumpall
doesn't have to mention the database name without -l or --database
in the command.

3.

> Have you added a test case in the regression suite which fails if we remove
this particular change and works well with the change? or if possible could
you please demonstrate here at least.

4. The variable name *append_data* doesn't look meaningful to me.
Instead we can use *append_database/**append_databases*?
because if this variable is set then we dump the databases along with
global objects. In case of pg_dump, append_data or data_only does make
sense to differentiate between schema and data but in case of pg_dumpall
if this variable is set then we're dumping schema as well as data i.e.
in-short
the databases.

------------------------------------ pg_dumpall.c
----------------------------------------

5. The variable name formatName doesn't follow the naming convention of
variables available around it. I think use of format_name/formatname would
be better.

 char   *use_role = NULL;
>   bool globals_only = false;

------------------------------------ pg_restore.c
----------------------------------------

6. Fourth parameter (i.e. append_data) to function restore_global_objects()
is redundant.
All the time value provided by all callers to this parameter is false.

I would suggest removing this parameter and in the definition of this
function
call function restore_one_database() with false as 4th argument. Find diff
below:

--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -64,8 +64,7 @@ static int    restore_one_database(const char
*inputFileSpec, RestoreOptions *opts,
                                                                 int
numWorkers, bool append_data, int num,
                                                                 bool
globals_only);
 static int restore_global_objects(const char *inputFileSpec,
-               RestoreOptions *opts, int numWorkers, bool append_data,
-               int num, bool globals_only);
+               RestoreOptions *opts, int numWorkers, int num, bool
globals_only);
 static int     restore_all_databases(const char *inputFileSpec,
                SimpleStringList db_exclude_patterns, RestoreOptions *opts,
int numWorkers);
 static int     get_dbnames_list_to_restore(PGconn *conn,
@@ -554,7 +553,7 @@ main(int argc, char **argv)

                        /* Set path for toc.glo file. */
                        snprintf(global_path, MAXPGPATH, "%s/toc.glo",
inputFileSpec);
-                       n_errors = restore_global_objects(global_path,
opts, numWorkers, false, 0, globals_only);
+                       n_errors = restore_global_objects(global_path,
opts, numWorkers, 0, globals_only);

                        pg_log_info("database restoring skipped because
option -g/--globals-only was specified");
                }
@@ -602,7 +601,7 @@ main(int argc, char **argv)
  * If globals_only is set, then skip DROP DATABASE commands from restore.
  */
 static int restore_global_objects(const char *inputFileSpec,
RestoreOptions *opts,
-               int numWorkers, bool append_data, int num, bool
globals_only)
+               int numWorkers, int num, bool globals_only)
 {
        int     nerror;
        int     format = opts->format;
@@ -610,8 +609,8 @@ static int restore_global_objects(const char
*inputFileSpec, RestoreOptions *opt
        /* Set format as custom so that toc.glo file can be read. */
        opts->format = archCustom;

-       nerror = restore_one_database(inputFileSpec, opts, numWorkers,
-                       append_data, num, globals_only);
+       nerror = restore_one_database(inputFileSpec, opts, numWorkers,
false, num,
+
globals_only);

        /* Reset format value. */
        opts->format = format;
@@ -1097,7 +1096,7 @@ restore_all_databases(const char *inputFileSpec,

        /* If map.dat has no entries, return after processing global
commands. */
        if (dbname_oid_list.head == NULL)
-               return restore_global_objects(global_path, opts,
numWorkers, false, 0, false);
+               return restore_global_objects(global_path, opts,
numWorkers, 0, false);

        pg_log_info(ngettext("found %d database name in \"%s\"",
                                                 "found %d database names
in \"%s\"",
@@ -1151,7 +1150,7 @@ restore_all_databases(const char *inputFileSpec,
                PQfinish(conn);

        /* Open toc.dat file and execute/append all the global sql
commands. */
-       n_errors_total =  restore_global_objects(global_path, opts,
numWorkers, false, 0, false);
+       n_errors_total =  restore_global_objects(global_path, opts,
numWorkers, 0, false);

Regression is successful with these changes.

7. Fix indentation:

> static int restore_global_objects(const char *inputFileSpec,
> RestoreOptions *opts, int numWorkers, bool append_data,
> int num, bool globals_only);

9. Remove extra space after map.dat and before comma:

> + * databases from map.dat , but skip restoring those matching

10. Fix 80 char limits:

+ n_errors = restore_one_database(subdirpath, opts, numWorkers, true, 1,
false);

+ num_total_db = get_dbname_oid_list_from_mfile(inputFileSpec,
&dbname_oid_list);

+ return restore_global_objects(global_path, opts, numWorkers, false, 0,
false);

+ n_errors_total =  restore_global_objects(global_path, opts, numWorkers,
false, 0, false);

+ pg_log_warning("errors ignored on database \"%s\" restore: %d",
dbidname->str, n_errors);

Regards,
Vaibhav

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Thu, 27 Nov 2025 13:45:12 +0530

>> `output_clean` variable seems redundant here:

Fixed. output_clean was a global variable because it was used in 2
functions. Now I am passing dopt. output_clean as function argument
for another function.

> doesn't have to mention the database name without -l or --database

Fixed. Added some examples.

> you please demonstrate here at least.

Fixed. With AH(archive), we should not free pointers by this exec call
as we free this by exit_nicely hook. (we register AH by
on_exit_close_archive).

>
> 4. The variable name append_data doesn't look meaningful to me.
> Instead we can use append_database/append_databases?
> if this variable is set then we're dumping schema as well as data i.e. in-short
> the databases.
>

As of now, I am keeping this append_data as this was from an already
committed patch.

> ------------------------------------ pg_dumpall.c ----------------------------------------
>

Fixed.

>
> ------------------------------------ pg_restore.c ----------------------------------------
>
> 6. Fourth parameter (i.e. append_data) to function restore_global_objects() is redundant.
> I would suggest removing this parameter and in the definition of this function
> call function restore_one_database() with false as 4th argument. Find diff below:
>

Fixed.

> @@ -64,8 +64,7 @@ static int    restore_one_database(const char *inputFileSpec, RestoreOptions *opts,
> +               RestoreOptions *opts, int numWorkers, int num, bool globals_only);
>                         snprintf(global_path, MAXPGPATH, "%s/toc.glo", inputFileSpec);
> -                       n_errors = restore_global_objects(global_path, opts, numWorkers, false, 0, globals_only);
> +                       n_errors = restore_global_objects(global_path, opts, numWorkers, 0, globals_only);
>
>                         pg_log_info("database restoring skipped because option -g/--globals-only was specified");
>                 }
> -               int numWorkers, bool append_data, int num, bool globals_only)
> @@ -610,8 +609,8 @@ static int restore_global_objects(const char *inputFileSpec, RestoreOptions *opt
> +       nerror = restore_one_database(inputFileSpec, opts, numWorkers, false, num,
> +                                                                 globals_only);
>
>         /* If map.dat has no entries, return after processing global commands. */
> -               return restore_global_objects(global_path, opts, numWorkers, false, 0, false);
> +               return restore_global_objects(global_path, opts, numWorkers, 0, false);
>
>                                                  "found %d database names in \"%s\"",
>         /* Open toc.dat file and execute/append all the global sql commands. */
> -       n_errors_total =  restore_global_objects(global_path, opts, numWorkers, false, 0, false);
> +       n_errors_total =  restore_global_objects(global_path, opts, numWorkers, 0, false);
>

Fixed some.

Fixed.

Fixed.

>
> 10. Fix 80 char limits:
>
> + n_errors = restore_one_database(subdirpath, opts, numWorkers, true, 1, false);
>
> + num_total_db = get_dbname_oid_list_from_mfile(inputFileSpec, &dbname_oid_list);
>
> + return restore_global_objects(global_path, opts, numWorkers, false, 0, false);
>
> + n_errors_total =  restore_global_objects(global_path, opts, numWorkers, false, 0, false);
>
> + pg_log_warning("errors ignored on database \"%s\" restore: %d", dbidname->str, n_errors);
>

Fixed some.
I will do some more cleanup in the coming versions.

>
> Regards,
> Vaibhav
>
> On Mon, Nov 17, 2025 at 10:45 PM Mahendra Singh Thalor <[email-masked]> wrote:
>>

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Thu, 27 Nov 2025 14:49:14 +0530

> >> Here, I am attaching an updated patch for the review and testing.

Hi,
CI was reporting an error for an unused variable.

[08:37:07.338] user 0m14.312s
[08:37:07.338] sys 0m9.155s
[08:37:07.338] make -s -j${BUILD_JOBS} clean
[08:37:07.850] time make -s -j${BUILD_JOBS} world-bin
[08:37:17.443] pg_restore.c:1080:8: error: variable 'count' set but
not used [-Werror,-Wunused-but-set-variable]
[08:37:17.443] 1080 | int count = 0;
[08:37:17.443] | ^
[08:37:17.443] 1 error generated.
[08:37:17.443] make[3]: *** [<builtin>: pg_restore.o] Error 1
[08:37:17.443] make[3]: *** Waiting for unfinished jobs....
[08:37:17.708] make[2]: *** [Makefile:45: all-pg_dump-recurse] Error 2
[08:37:17.709] make[1]: *** [Makefile:42: all-bin-recurse] Error 2
[08:37:17.709] mak

Fixed. Here, I am attaching an updated patch for the review and testing.

---

From: tushar <[email-masked]>
Date: Mon, 1 Dec 2025 18:36:48 +0530

> Fixed. Here, I am attaching an updated patch for the review and testing.

Thanks Mahendra, please refer this scenario where restoring the
dump(database contain tablespace) throwing an error

*Steps to reproduce *
initdb (./initdb -D data) , start the server ( ./pg_ctl -D data start) ,
connect to psql terminal ( ./psql postgres)
create a directory ( \! mkdir /tmp/abc) , create a tablespace ( create
tablespace a location '/tmp/abc'); )
create a table ( create table t(n int) tablespace a; ) , insert data (
insert into t values ('a');  )
perform pg_dumpall with option -c  ( ./pg_dumpall -Fc -f my.d)
try to perform pg_restore with option --no-tablespaces  ( ./pg_restore
--no-tablespaces -Fc my.d  -d postgres -C)
Getting this error :
"
pg_restore: error: could not execute query: ERROR:  role "edb" already
exists
Command was: CREATE ROLE edb;
ALTER ROLE edb WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION
BYPASSRLS;

pg_restore: error: could not execute query: ERROR:  directory
"/tmp/abc/PG_19_202511281" already in use as a tablespace
Command was: CREATE TABLESPACE a OWNER edb LOCATION '/tmp/abc';

pg_restore: warning: errors ignored on restore: 2
"

regards,
Tushar
https://www.enterprisedb.com/

---

From: tushar <[email-masked]>
Date: Mon, 1 Dec 2025 22:47:17 +0530

On Mon, Dec 1, 2025 at 6:36 PM tushar <[email-masked]> wrote:

> ALTER ROLE edb WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN
> REPLICATION BYPASSRLS;
>
I have observed that when combining the --globals-only option with certain
other switches during a pg_restore - operation fails silently.
The attempted restore does not execute, but no error message or warning is
displayed unless the --verbose option is also used.

--this will just run without any message but objects also not going to
create
./pg_restore -Fc ok31. -C -d postgres  -t mytable  --globals-only
./pg_restore -Fc ok31. -C -d postgres  -no-tablespace     --globals-only
./pg_restore -Fc ok31. -C -d postgres  -no-data  --globals-only

with --verbose
[edb@1a1c15437e7c bin]$ ./pg_restore -Fc ok31. -C -d postgres  -t myable
--globals-only -v
pg_restore: connecting to database for restore
pg_restore: executing SELECT pg_catalog.set_config('search_path', '',
false);
pg_restore: implied no-schema restore
pg_restore: database restoring skipped because option -g/--globals-only was
specified

we should probably add some message there.

regards,

---

From: tushar <[email-masked]>
Date: Tue, 2 Dec 2025 18:45:06 +0530

On Mon, Dec 1, 2025 at 10:47 PM tushar <[email-masked]>
wrote:

> pg_restore: database restoring skipped because option -g/--globals-only
> was specified
>
Please refer this scenario where "--no-comments"  switch  is ignoring when
used with -Ft/c option of pg_dumpall

*Test Case to reproduce:*
--Connect to psql terminal , create a table and comment :
postgres=# create table t(n int);
CREATE TABLE
postgres=# insert into t values (1);
INSERT 0 1
postgres=# comment on table t is 'testing...';
COMMENT
postgres=# SELECT obj_description('public.t'::regclass, 'pg_class') AS
table_comment ;
table_comment
---------------
 testing...
(1 row)

--perform pg_dumpall with
(a) -Fp      (./pg_dumpall -Fp --no-comments -f dump.plain)
(b) -Ft       (./pg_dumpall -Ft  --no-comments -f dump.tar)

Case 1:  restore (a) , just run the file (dump.plain) on psql terminal ,
fire this query :
postgres=# SELECT
    obj_description('public.t'::regclass, 'pg_class') AS table_comment;
 table_comment
---------------

(1 row)
Seems expected .

Case 2: restore (b) via command ( ./pg_restore -Ft dump.tar -d postgres -p
5806  -C )
fire this query :
postgres=# SELECT obj_description('public.t'::regclass, 'pg_class') AS
table_comment ;
table_comment
---------------
 testing...
(1 row)

Seems not expected i.e pg_dumpall with option -Ft still taking table
comments and ignoring --no-comments switch.

regards,

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Mon, 8 Dec 2025 12:14:06 +0530

Thanks Tushar for the testing and reports.

On Tue, 2 Dec 2025 at 18:45, tushar <[email-masked]> wrote:
>
>
>
> On Mon, Dec 1, 2025 at 10:47 PM tushar <[email-masked]> wrote:
>>>
>>>
>>
>> I have observed that when combining the --globals-only option with certain other switches during a pg_restore - operation fails silently.
>> The attempted restore does not execute, but no error message or warning is displayed unless the --verbose option is also used.
>>
>> --this will just run without any message but objects also not going to create
>> [edb@1a1c15437e7c bin]$ ./pg_restore -Fc ok31. -C -d postgres  -t myable  --globals-only -v
>> pg_restore: executing SELECT pg_catalog.set_config('search_path', '', false);
>> pg_restore: database restoring skipped because option -g/--globals-only was specified
>>
> Please refer this scenario where "--no-comments"  switch  is ignoring when used with -Ft/c option of pg_dumpall
>
> Test Case to reproduce:
> postgres=# SELECT obj_description('public.t'::regclass, 'pg_class') AS table_comment ;
> table_comment
> ---------------
>  testing...
> (1 row)
>
> Case 1:  restore (a) , just run the file (dump.plain) on psql terminal , fire this query :
> postgres=# SELECT
> Case 2: restore (b) via command ( ./pg_restore -Ft dump.tar -d postgres -p 5806  -C )
> fire this query :
> postgres=# SELECT obj_description('public.t'::regclass, 'pg_class') AS table_comment ;
> table_comment
> ---------------
>  testing...
> (1 row)
>
> Seems not expected i.e pg_dumpall with option -Ft still taking table comments and ignoring --no-comments switch.
>
> regards,

I tried to fix these issues in the attached patch.

---

From: tushar <[email-masked]>
Date: Mon, 8 Dec 2025 22:39:00 +0530

> Here, I am attaching an updated patch for the review and testing.

Thanks Mahendra, I am not able to apply the patche against the latest
sources, seems like you need to rebase it

[edb@1a1c15437e7c pg]$ git apply
/tmp/v11_08122025-Non-text-modes-for-pg_dumpall-correspondingly-change.patch

error: patch failed: src/bin/pg_dump/pg_dumpall.c:419
error: src/bin/pg_dump/pg_dumpall.c: patch does not apply
error: patch failed: src/bin/pg_dump/pg_restore.c:409
error: src/bin/pg_dump/pg_restore.c: patch does not apply
[edb@1a1c15437e7c pg]$

regards,

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Tue, 9 Dec 2025 00:18:46 +0530

On Mon, 8 Dec 2025 at 22:39, tushar <[email-masked]> wrote:
>
>
>
> On Mon, Dec 8, 2025 at 12:14 PM Mahendra Singh Thalor <[email-masked]> wrote:
>>
>>
> Thanks Mahendra, I am not able to apply the patche against the latest sources, seems like you need to rebase it
>
> [edb@1a1c15437e7c pg]$ git apply /tmp/v11_08122025-Non-text-modes-for-pg_dumpall-correspondingly-change.patch

Thanks Tushar for the report.

In the last commit, there were some changes for error messages so this
was not applying cleanly.

> we should probably add some message there.

All these are good to me. In a successful case, we don't receive any
error message.(expected)

Here, I am attaching an updated patch for the review and testing. This
can be applied on commit d0d0ba6cf66c4043501f6f7.

---

From: tushar <[email-masked]>
Date: Wed, 10 Dec 2025 19:08:21 +0530

> can be applied on commit d0d0ba6cf66c4043501f6f7.
Thanks, Mahendra, please refer to this scenario where  if
"--transaction-size" switch  is used with pg_dumpall/pg_restore, then the
table creation fails (or the table is not created)

Steps to reproduce:
1. Connect to the psql terminal, create a table/insert rows { create table
t(n int); insert into t values (generate_series(1,15)); }
2. Perform pg_dump operation  { ./pg_dumpall -Ft -f tar.dump }
3. new cluster:
try to restore with  --transaction-size switch { ./pg_restore -Ft  tar.dump
 -C -d postgres   --transaction-size=10 } =  Table failed to create

I have checked via pg_dump/pg_restore using --transaction-size, and it is
working fine, i.e, table is created successfully

./pg_dump -Ft -f tar.d postgres
./pg_restore  --transaction-size=10 -Ft -d new_database  tar.d

regards,

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Thu, 11 Dec 2025 21:39:39 +0530

On Wed, 10 Dec 2025 at 19:08, tushar <[email-masked]> wrote:
>
>
>
> On Tue, Dec 9, 2025 at 12:18 AM Mahendra Singh Thalor <[email-masked]> wrote:
>>
> Thanks, Mahendra, please refer to this scenario where  if "--transaction-size" switch  is used with pg_dumpall/pg_restore, then the table creation fails (or the table is not created)
>
> Steps to reproduce:
> 1. Connect to the psql terminal, create a table/insert rows { create table t(n int); insert into t values (generate_series(1,15)); }
> try to restore with  --transaction-size switch { ./pg_restore -Ft  tar.dump  -C -d postgres   --transaction-size=10 } =  Table failed to create
>
> I have checked via pg_dump/pg_restore using --transaction-size, and it is working fine, i.e, table is created successfully
>

If transaction-size is given as non-zero, then pg_restore behaves like
"-e/--exit-on-error". means if there is any error in restore, then
exit without restoring the full cluster.

Here, in our case, as the cluster already has a role with the current
user in restore, we are reporting error  "pg_restore: error: could not
execute query: ERROR:  role "role" already exists" and after this
error, restore is exiting.

If you restore using a different role, then you will not get any error
and the full cluster will be restored. I will add some handling to
ignore the "CREATE ROLE current_user" command in pg_restore.

---

From: tushar <[email-masked]>
Date: Fri, 12 Dec 2025 19:10:02 +0530

On Thu, Dec 11, 2025 at 9:39 PM Mahendra Singh Thalor <[email-masked]>
wrote:

> Thanks Mahendra, Could you please also add some error message  for this
below
pg_restore command:
postgres=# create table t(n int);
CREATE TABLE
postgres=# insert into t values (1),(10),(100);
INSERT 0 3
Perform pg_dump: ./pg_dump -Ft   -f a.a1  postgres
Perform pg_restore: /pg_restore  -Ft  a.a1 -f  -C -v
pg_restore: creating TABLE "public.t"
pg_restore: processing data for table "public.t"
[edb@1a1c15437e7c bin]$ ./psql postgres
psql (19devel)
Type "help" for help.
postgres=# \dt
Did not find any tables.
postgres=#

regards,

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Fri, 12 Dec 2025 21:47:00 +0530

On Fri, 12 Dec 2025 at 19:10, tushar <[email-masked]> wrote:
>
>
>
> On Thu, Dec 11, 2025 at 9:39 PM Mahendra Singh Thalor <[email-masked]> wrote:
>>
>>
>>
> Thanks Mahendra, Could you please also add some error message  for this below
> pg_restore command:

Hi Tushar,
This is the handling of command line arguments.
In code, after "-f", we expect file name, but here you are using "-C"
which will be considered as file name. This is the case for all the
command line arguments.

If pg_restore has the "-f" option, then the "-d database" name can't
be given and data will be copied into "-f filename" (it will not be
restored in the cluster).

Please let me know if you still have some doubts.

---

From: Mahendra Singh Thalor <[email-masked]>
Date: Tue, 3 Mar 2026 16:47:52 +0530

On Tue, 3 Mar 2026 at 14:55, Peter Eisentraut <[email-masked]> wrote:
>
> I noticed this cast in the committed code:
>
> > + num_total_db = get_dbname_oid_list_from_mfile((char *) inputFileSpec,
> > &dbname_oid_list);
>
> The cast drops the const qualifier from inputFileSpec.
> get_dbname_oid_list_from_mfile() writes into the space pointed to by its
> argument, so it's really not "const".  (And inputFileSpec points into
> argv, so this ends up writing directly into argv.)
>
> Please see if you can clean this up.  It might be best if
> get_dbname_oid_list_from_mfile() made a copy of its argument that it can
> write into, and then the argument can be "const".
>

Thanks Peter.

Here, I am attaching a patch to fix this issue.

-- 
Thanks and Regards
Mahendra Singh Thalor
EnterpriseDB: http://www.enterprisedb.com
From cb744c481ff045a95f11d2a0217aba928ede22db Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <[email-masked]>
Date: Tue, 3 Mar 2026 16:43:33 +0530
Subject: [PATCH] pg_restore: don't edit inputfile name, instead use local copy

In get_dbname_oid_list_from_mfile, we are editing filename by passing is
char *, instead of const char*. Fixed this by adding local copy.
---
 src/bin/pg_dump/pg_restore.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 14d886fc86e..54c8b9d48b0 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -68,7 +68,7 @@ static int	restore_all_databases(const char *inputFileSpec,
 static int	get_dbnames_list_to_restore(PGconn *conn,
 										SimplePtrList *dbname_oid_list,
 										SimpleStringList db_exclude_patterns);
-static int	get_dbname_oid_list_from_mfile(char *dumpdirpath,
+static int	get_dbname_oid_list_from_mfile(const char *dumpdirpatharg,
 										   SimplePtrList *dbname_oid_list);
 
 /*
@@ -1082,14 +1082,18 @@ get_dbnames_list_to_restore(PGconn *conn,
  * Returns, total number of database names in map.dat file.
  */
 static int
-get_dbname_oid_list_from_mfile(char *dumpdirpath, SimplePtrList *dbname_oid_list)
+get_dbname_oid_list_from_mfile(const char *dumpdirpatharg, SimplePtrList *dbname_oid_list)
 {
 	StringInfoData linebuf;
 	FILE	   *pfile;
 	char		map_file_path[MAXPGPATH];
 	int			count = 0;
 	int			len;
+	char			*dumpdirpath;
 
+	len = strlen(dumpdirpatharg);
+	dumpdirpath = pg_malloc0(len + 1);
+	memcpy(dumpdirpath, dumpdirpatharg, len);
 
 	/*
 	 * If there is no map.dat file in dump, then return from here as there is
@@ -1206,7 +1210,7 @@ restore_all_databases(const char *inputFileSpec,
 	if (opts->cparams.dbname)
 		connected_db = opts->cparams.dbname;
 
-	num_total_db = get_dbname_oid_list_from_mfile((char *) inputFileSpec, &dbname_oid_list);
+	num_total_db = get_dbname_oid_list_from_mfile(inputFileSpec, &dbname_oid_list);
 
2.52.0

---

From: Andrew Dunstan <[email-masked]>
Date: Wed, 4 Mar 2026 15:54:48 -0500

> Here, I am attaching a patch to fix this issue.

Pushed with a slight tweak.

cheers

andrew

---

From: Andrew Dunstan <[email-masked]>
Date: Sun, 22 Mar 2026 04:50:31 -0400

On Sat, Mar 21, 2026 at 5:10 PM Tom Lane <[email-masked]> wrote:

> The CF entry for this is still open:
>
> https://commitfest.postgresql.org/patch/5495/
>
> Shouldn't it get closed now?
>
>
>

Yes, sorry, done.

cheers

andrew

---

From: Noah Misch <[email-masked]>
Date: Sat, 6 Jun 2026 17:02:18 -0700

On Thu, Feb 26, 2026 at 09:02:48AM -0500, Andrew Dunstan wrote:
> pushed with a slight tweak.

Having now reviewed commit 763aaa0, I don't think it's ready to remain part of
v19.  While some points from my v18 review are now resolved, other points
still seem unresolved.  I didn't find discussion of the unresolved points.  I
also see new issues.

Wider issues:

> @@ -796,24 +964,45 @@ dropRoles(PGconn *conn)

> +			appendPQExpBuffer(delQry, "DROP ROLE %s%s;\n",
> +							  if_exists ? "IF EXISTS " : "",
> +							  fmtId(rolename));
> +			fprintf(OPF, "%s", delQry->data);
> +		}
> +		else
> +		{
> +			appendPQExpBuffer(delQry, "DROP ROLE IF EXISTS %s;\n",
> +							  fmtId(rolename));
> +
> +			ArchiveEntry(fout,
> +						 nilCatalogId,	/* catalog ID */
> +						 createDumpId(),	/* dump ID */
> +						 ARCHIVE_OPTS(.tag = psprintf("ROLE %s", fmtId(rolename)),
> +									  .description = "DROP_GLOBAL",
> +									  .section = SECTION_PRE_DATA,
> +									  .createStmt = delQry->data));
> +		}

pg_dumpall.c should call ArchiveEntry() in the same way pg_dump.c does.  In
pg_dump.c, per-object-class support code calls ArchiveEntry() unconditionally,
and the object-independent infra of pg_backup_archiver.c deals with the
difference between plain and non-plain formats.

There should be one appendPQExpBuffer(delQry, "DROP ROLE ..."), not one for
plain format and another for non-plain formats.  Having two creates excess
risk of format-specific bugs, something pg_dump.c has long avoided well.  (I'm
echoing my postgr.es/m/[email-masked] review.  I wrote,
"The strength of the archiver architecture shows in how rarely new features
need format-specific logic and how rarely format-specific bugs get reported."
That holds for the way pg_dump.c uses the archiver, but it doesn't hold for
the way pg_dumpall.c now uses the archiver.)

Separately, DROP should be in dropStmt, not in createStmt.  This likely
entails refactoring to merge dumpRoles() and dropRoles() into one function,
per the style of pg_dump.c.

> +		/*
> +		 * For pg_dumpall archives, --clean implies --if-exists since global
> +		 * objects may not exist in the target cluster.
> +		 */
> +		if (opts->dropSchema && !opts->if_exists)
> +		{
> +			opts->if_exists = 1;
> +			pg_log_info("--if-exists is implied by --clean for pg_dumpall archives");
> +		}

The last comment of postgr.es/m/[email-masked] disagreed
with this decision, so that remains unresolved.

> +		if (tmpopts->cparams.dbname)
> +		{
> +			PGconn	   *test_conn;
> +
> +			test_conn = ConnectDatabase(dbidname->str, NULL, tmpopts->cparams.pghost,
> +										tmpopts->cparams.pgport, tmpopts->cparams.username, TRI_DEFAULT,
> +				tmpopts->createDB = 0;
> +				tmpopts->cparams.dbname = dbidname->str;
> +			}
> +			else
> +			{
> +				/* We'll have to create it */
> +				tmpopts->createDB = 1;
> +				tmpopts->cparams.dbname = connected_db;
> +			}

postgr.es/m/[email-masked] called for this to change.

None of this is meant to say the feature is impossible. But I don’t think this
commit is at the point where post-commit fixups are the right workflow.  I
recommend reverting, posting a new version, and letting commitfest review
finish.

Localized issues:

There's new code to write map and globals files, but I don't see corresponding
code to fsync those files, like we fsync a plain-format dump.

> @@ -3027,6 +3049,16 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
>  			return 0;
>  	}
>  
> +	/*
> +	 * Global object TOC entries (e.g., ROLEs or TABLESPACEs) must not be
> +	 * ignored.
> +	 */
> +	if (strcmp(te->desc, "ROLE") == 0 ||
> +		strcmp(te->desc, "ROLE PROPERTIES") == 0 ||
> +		strcmp(te->desc, "TABLESPACE") == 0 ||
> +		strcmp(te->desc, "DROP_GLOBAL") == 0)
> +		return REQ_SCHEMA;
> +

If I delete this addition, no src/bin/pg_dump test fails.  Would you explain
the rationale for this addition?

> +			if (comment_buf->data[0] != '\0')
> +				ArchiveEntry(fout,
> +							 nilCatalogId,	/* catalog ID */
> +							 createDumpId(),	/* dump ID */
> +							 ARCHIVE_OPTS(.tag = tag,
> +										  .description = "COMMENT",
> +										  .section = SECTION_PRE_DATA,
> +										  .createStmt = comment_buf->data));
> +
> +			if (seclabel_buf->data[0] != '\0')
> +				ArchiveEntry(fout,
> +							 nilCatalogId,	/* catalog ID */
> +							 createDumpId(),	/* dump ID */
> +							 ARCHIVE_OPTS(.tag = tag,
> +										  .description = "SECURITY LABEL",
> +										  .section = SECTION_PRE_DATA,
> +										  .createStmt = seclabel_buf->data));

COMMENT and SECURITY LABEL should use .deps and .nDeps to record a dependency
on the main object.  Representative example from pg_dump.c:

		ArchiveEntry(fout, nilCatalogId, createDumpId(),
					 ARCHIVE_OPTS(.tag = target->data,
								  .namespace = tbinfo->dobj.namespace->dobj.name,
								  .owner = tbinfo->rolname,
								  .description = "SECURITY LABEL",
								  .section = SECTION_NONE,
								  .createStmt = query->data,
								  .deps = &(tbinfo->dobj.dumpId),
								  .nDeps = 1));

This probably has no user-visible consequences, since "/* Parallel execution
is not supported for global object restoration. */".  Still, best to follow
the standard.

> +		 * If this is not a plain format dump, then append dboid and dbname to

The first six lines inside the block aren't for the purpose described here.
The one line that is for this purpose has its own comment.  Please delete this
comment.

> +				snprintf(dbfilepath, MAXPGPATH, "\"%s\"/\"%s\"", db_subdir, oid);

My review in postgr.es/m/[email-masked] called for this
to change.  I'm finding no discussion of the rationale for not changing it.

> +runPgDump(const char *dbname, const char *create_opts, char *dbfile)

> +		printfPQExpBuffer(&cmd, "\"%s\" %s -f %s %s", pg_dump_bin,
> +						  pgdumpopts->data, dbfile, create_opts);

A different "-f" argument is already in pgdumpopts.  That works, but it's
untidy.

> +			pg_fatal("option %s cannot exclude %s when restoring a pg_dumpall archive",
> +					 "--section", "--pre-data");

s/--pre-data/pre-data/

> +		/*
> +		 * Always restore global objects, even if --exclude-database results
> +		 * in zero databases to process. If 'globals-only' is set, exit
> +		 * immediately.

I think this comment is out of date, because the code doesn't have an exit:

> +		 */
> +		snprintf(global_path, MAXPGPATH, "%s/toc.glo", inputFileSpec);
> +
> +		n_errors = restore_global_objects(global_path, tmpopts);
> +
> +		if (globals_only)
> +			pg_log_info("database restoring skipped because option %s was specified",
> +						"-g/--globals-only");
> +		else
> +		{
> +			/* Now restore all the databases from map.dat */
> +			n_errors = n_errors + restore_all_databases(inputFileSpec, db_exclude_patterns,
> +														opts, numWorkers);
> +		}
> +

> +	/* Don't output TOC entry comments when restoring globals */
> +	((ArchiveHandle *) AH)->noTocComments = 1;

Why not?

> +							SimplePtrList *dbname_oid_list,
...
> +			if (pg_strcasecmp(dbidname->str, pat_cell->val) == 0)
> +			/* Otherwise, try a pattern match if there is a connection */

The code assumes a connection unconditionally (which seems right to me):

> +									  false, NULL, db_lit->data,
> +								 dbidname->str);

---

From: Andrew Dunstan <[email-masked]>
Date: Mon, 15 Jun 2026 16:11:16 -0400

> still seem unresolved.  I didn't find discussion of the unresolved points.  I

OK, here's a reversion path. It's a bit messy, and I didn't touch the 
release notes, but apart from that I think it does the right thing.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com
From fe6da46afb016d9dcdf33051051856ae630bb74d Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <[email-masked]>
Date: Mon, 15 Jun 2026 15:58:04 -0400
Subject: [PATCH] Revert non-text output formats for pg_dumpall

This reverts the non-text (custom/directory/tar) output format support
for pg_dumpall added by 763aaa06f03 and its feature-specific follow-ups,
in line with Noah Misch's post-commit review which recommends reverting
and finishing the work through the commitfest.

Scope is deliberately minimal: only the feature itself is removed.
Independent improvements that merely touched the same files, or that were
committed alongside the feature but do not depend on its design, are
preserved.

Reverted (the feature):

  763aaa06f03  Add non-text output formats to pg_dumpall
  d6d9b96b404  Clean up nodes that are no longer of use in 007_pgdumpall.pl
  01c729e0c7a  Fix casting away const-ness in pg_restore.c
  c7572cd48d3  Improve writing map.dat preamble
  3c19983cc08  pg_restore: add --no-globals option to skip globals
  abff4492d02  Fix options listing of pg_restore --no-globals
  bb53b8d359d  Fix small memory leak in get_dbname_oid_list_from_mfile()
  a793677e57b  pg_restore: Remove dead code in restore_all_databases()
  a198c26dede  pg_dumpall: simplify coding of dropDBs()
  ec80215c033  pg_restore: Remove unnecessary strlen() calls in options parsing

Preserved (independent of the feature):

  b2898baaf7e  the check_mut_excl_opts() helper in src/fe_utils/option_utils.c
               and its use in pg_dump
  7c8280eeb58  pg_dump's conflicting-option refactor (and tests 002/005)
  be0d0b457cb  pg_dumpall's rejection of --clean together with --data-only
               (re-expressed directly, since pg_dumpall.c is otherwise
               returned to its pre-feature state)
  74b4438a70b  the dangling-grantor-OID GRANT fix (back-patched through 16)
  273d26b75e7, d4cb9c37765  independent pg_restore.sgml clarifications

Because the feature restructured pg_dumpall.c and pg_restore.c (pg_restore's
main() was split into restore_one_database() plus a dispatcher) and
interleaved its option checks with the conflicting-option refactor in the
same regions, the cosmetic check_mut_excl_opts() reflow of those two files'
option blocks is inseparable from the feature and comes out with it; the
behavior is unchanged.  The reusable helper and pg_dump's use of it are
unaffected.

Discussion: https://postgr.es/m/[email-masked]
---
 doc/src/sgml/ref/pg_dumpall.sgml            | 135 +--
 doc/src/sgml/ref/pg_restore.sgml            | 129 +--
 src/bin/pg_dump/meson.build                 |   1 -
 src/bin/pg_dump/parallel.c                  |  14 -
 src/bin/pg_dump/pg_backup.h                 |   2 +-
 src/bin/pg_dump/pg_backup_archiver.c        |  67 +-
 src/bin/pg_dump/pg_backup_archiver.h        |   1 -
 src/bin/pg_dump/pg_backup_tar.c             |   2 +-
 src/bin/pg_dump/pg_dump.c                   |   2 +-
 src/bin/pg_dump/pg_dumpall.c                | 863 ++++----------------
 src/bin/pg_dump/pg_restore.c                | 776 ++----------------
 src/bin/pg_dump/t/001_basic.pl              | 112 +--
 src/bin/pg_dump/t/005_pg_dump_filterfile.pl |   4 +-
 src/bin/pg_dump/t/007_pg_dumpall.pl         | 661 ---------------
 src/tools/pgindent/typedefs.list            |   1 -
 15 files changed, 275 insertions(+), 2495 deletions(-)
 delete mode 100644 src/bin/pg_dump/t/007_pg_dumpall.pl

index 51c70198091..8834b7ec141 100644
 
  <refnamediv>
 
  <refsynopsisdiv>
@@ -36,33 +33,14 @@ PostgreSQL documentation
   <para>
    <application>pg_dumpall</application> is a utility for writing out
    (<quote>dumping</quote>) all <productname>PostgreSQL</productname> databases
-   of a cluster into an SQL script file or an archive.  It does this by
+   of a cluster into one script file.  The script file contains
+   <acronym>SQL</acronym> commands that can be used as input to <xref
+   linkend="app-psql"/> to restore the databases.  It does this by
    calling <xref linkend="app-pgdump"/> for each database in the cluster.
-   The output contains <acronym>SQL</acronym> commands that can be used
-   as input to <xref linkend="app-psql"/> or <xref linkend="app-pgrestore"/>
-   to restore the databases.
    <application>pg_dumpall</application> also dumps global objects
    that are common to all databases, namely database roles, tablespaces,
    and privilege grants for configuration parameters.
    (<application>pg_dump</application> does not save these objects.)
-   The only parts of a database cluster's state that
-   are <emphasis>not</emphasis> included in the default output
-   of <application>pg_dumpall</application> are the configuration files
-   and any database parameter setting changes made with
-   <xref linkend="sql-altersystem"/>.
-  </para>
-
-  <para>
-   If the output format is a
-   plain text SQL script, it will be written to the standard output.  Use the
-   <option>-f</option>/<option>--file</option> option or shell operators to
-   redirect it into a file.
-  </para>
-
-  <para>
-   If another output format is selected, the archive will be placed in a
-   directory named using the <option>-f</option>/<option>--file</option>
-   option, which is required in this case.
   </para>
 
   <para>
@@ -73,6 +51,12 @@ PostgreSQL documentation
    allowed to add roles and create databases.
   </para>
 
+  <para>
+   The SQL script will be written to the standard output.  Use the
+   <option>-f</option>/<option>--file</option> option or shell operators to
+   redirect it into a file.
+  </para>
+
   <para>
   <application>pg_dumpall</application> needs to connect several
   times to the <productname>PostgreSQL</productname> server (once per
@@ -147,93 +131,16 @@ PostgreSQL documentation
        <para>
         Send output to the specified file.  If this is omitted, the
         standard output is used.
-        This option can only be omitted when <option>--format</option> is plain.
        </para>
       </listitem>
      </varlistentry>
 
-     <varlistentry>
-      <term><option>-F <replaceable class="parameter">format</replaceable></option></term>
-      <term><option>--format=<replaceable class="parameter">format</replaceable></option></term>
-      <listitem>
-       <para>
-        Specify the format of dump files.  In plain format, all the dump data is
-        sent in a single text stream. This is the default.
-
-        In all other modes, <application>pg_dumpall</application> first creates two files,
-        <filename>toc.glo</filename> and <filename>map.dat</filename>, in the directory
-        specified by <option>--file</option>.
-        The first file contains global data (roles and tablespaces) in custom format. The second
-        contains a mapping between database OIDs and names. These files are used by
-        <application>pg_restore</application>. Data for individual databases is placed in
-        the <filename>databases</filename> subdirectory, named using the database's OID.
-
-       <variablelist>
-        <varlistentry>
-         <term><literal>d</literal></term>
-         <term><literal>directory</literal></term>
-         <listitem>
-          <para>
-           Output directory-format archives for each database,
-           suitable for input into pg_restore. The directory
-           will have database <type>oid</type> as its name.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry>
-         <term><literal>p</literal></term>
-         <term><literal>plain</literal></term>
-         <listitem>
-          <para>
-           Output a plain-text SQL script file (the default).
-          </para>
-         </listitem>
-        </varlistentry>
-
-        <varlistentry>
-         <term><literal>c</literal></term>
-         <term><literal>custom</literal></term>
-         <listitem>
-          <para>
-           Output a custom-format archive for each database,
-           suitable for input into pg_restore. The archive
-           will be named <filename>dboid.dmp</filename> where <type>dboid</type> is the
-           <type>oid</type> of the database.
-          </para>
-         </listitem>
-        </varlistentry>
-
-         <varlistentry>
-         <term><literal>t</literal></term>
-         <term><literal>tar</literal></term>
-         <listitem>
-          <para>
-           Output a tar-format archive for each database,
-           suitable for input into pg_restore. The archive
-           will be named <filename>dboid.tar</filename> where <type>dboid</type> is the
-           <type>oid</type> of the database.
-          </para>
-         </listitem>
-        </varlistentry>
-
-        </variablelist>
-
-       See <xref linkend="app-pgdump"/> for details on how the
-       various non-plain-text archive formats work.
-
-        </para>
-      </listitem>
-     </varlistentry>
-
      <varlistentry>
       <term><option>-g</option></term>
       <term><option>--globals-only</option></term>
       <listitem>
        <para>
         Dump only global objects (roles and tablespaces), no databases.
-        Note: <option>--globals-only</option> cannot be used with
-        <option>--clean</option> with non-text dump format.
        </para>
       </listitem>
      </varlistentry>
@@ -1029,21 +936,13 @@ exclude database <replaceable class="parameter">PATTERN</replaceable>
  <refsect1 id="app-pg-dumpall-ex">
   <title>Examples</title>
   <para>
-   To dump all databases in plain text format (the default):
+   To dump all databases:
+
 <screen>
 <prompt>$</prompt> <userinput>pg_dumpall &gt; db.out</userinput>
 </screen>
   </para>
 
-  <para>
-   To dump all databases using other formats:
-<screen>
-<prompt>$</prompt> <userinput>pg_dumpall --format=directory -f db.out</userinput>
-<prompt>$</prompt> <userinput>pg_dumpall --format=custom -f db.out</userinput>
-<prompt>$</prompt> <userinput>pg_dumpall --format=tar -f db.out</userinput>
-</screen>
-  </para>
-
   <para>
    To restore database(s) from this file, you can use:
 <screen>
@@ -1057,16 +956,6 @@ exclude database <replaceable class="parameter">PATTERN</replaceable>
    the script will attempt to drop other databases immediately, and that
    will fail for the database you are connected to.
   </para>
-
-  <para>
-   If the dump was taken in a non-plain-text format, use
-   <application>pg_restore</application> to restore the databases:
-<screen>
-<prompt>$</prompt> <userinput>pg_restore db.out -d postgres -C</userinput>
-</screen>
-   This will restore all databases.  To restore only some databases, use
-   the <option>--exclude-database</option> option to skip those not wanted.
-  </para>
  </refsect1>
 
  <refsect1>
index 5e77ddd556f..b6c5299c36e 100644
 
   <refpurpose>

   <para>
 
@@ -54,34 +52,14 @@ PostgreSQL documentation
 
-  <para>
-   A non-plain-text archive made using <application>pg_dumpall</application>
-   is a directory containing a <filename>toc.glo</filename> file with global
-   objects (roles and tablespaces), a <filename>map.dat</filename> file
-   listing the databases, and a subdirectory for each database containing
-   its archive.  When restoring such an archive,
-   <application>pg_restore</application> first restores global objects from
-   <filename>toc.glo</filename>, then processes each database listed in
-   <filename>map.dat</filename>.  Lines in <filename>map.dat</filename> can
-   be commented out with <literal>#</literal> to skip restoring specific
-   databases.
-  </para>
-
   <para>
    Obviously, <application>pg_restore</application> cannot restore information
    that is not present in the archive file.  For instance, if the
@@ -152,12 +130,6 @@ PostgreSQL documentation
         ignorable error messages will be reported,
         unless <option>--if-exists</option> is also specified.
        </para>
-       <para>
-        When restoring a <application>pg_dumpall</application> archive,
-        <option>--if-exists</option> is implied by <option>--clean</option>,
-        since global objects such as roles and tablespaces may not exist
-        in the target cluster.
-       </para>
       </listitem>
      </varlistentry>
 
@@ -180,8 +152,6 @@ PostgreSQL documentation
         commands that mention this database.
         Access privileges for the database itself are also restored,
         unless <option>--no-acl</option> is specified.
-        <option>--create</option> is required when restoring multiple databases
-        from a non-plain-text archive made using <application>pg_dumpall</application>.
        </para>
 
        <para>
@@ -282,29 +252,6 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
-     <varlistentry>
-      <term><option>-g</option></term>
-      <term><option>--globals-only</option></term>
-      <listitem>
-       <para>
-        Restore only global objects (roles and tablespaces), no databases.
-       </para>
-       <para>
-        This option is only relevant when restoring from a non-plain-text archive made using <application>pg_dumpall</application>.
-        Note: <option>--globals-only</option> cannot be used with
-        <option>--data-only</option>,
-        <option>--schema-only</option>,
-        <option>--statistics-only</option>,
-        <option>--statistics</option>,
-        <option>--no-globals</option>,
-        <option>--exit-on-error</option>,
-        <option>--single-transaction</option>,
-        <option>--clean</option>, or
-        <option>--transaction-size</option>.
-       </para>
-      </listitem>
-     </varlistentry>
-
      <varlistentry>
       <term><option>-I <replaceable class="parameter">index</replaceable></option></term>
       <term><option>--index=<replaceable class="parameter">index</replaceable></option></term>
@@ -641,28 +588,6 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
-     <varlistentry>
-      <term><option>--exclude-database=<replaceable class="parameter">pattern</replaceable></option></term>
-      <listitem>
-       <para>
-        Do not restore databases whose name matches
-        <replaceable class="parameter">pattern</replaceable>.
-        Multiple patterns can be excluded by writing multiple
-        <option>--exclude-database</option> switches.  The
-        <replaceable class="parameter">pattern</replaceable> parameter is
-        interpreted as a pattern according to the same rules used by
-        <application>psql</application>'s <literal>\d</literal>
-        commands (see <xref linkend="app-psql-patterns"/>),
-        so multiple databases can also be excluded by writing wildcard
-        characters in the pattern.  When using wildcards, be careful to
-        quote the pattern if needed to prevent shell wildcard expansion.
-       </para>
-       <para>
-        This option is only relevant when restoring from a non-plain-text archive made using <application>pg_dumpall</application>.
-       </para>
-      </listitem>
-     </varlistentry>
-
      <varlistentry>
       <term><option>--filter=<replaceable class="parameter">filename</replaceable></option></term>
       <listitem>
@@ -751,9 +676,7 @@ PostgreSQL documentation
         in <option>--clean</option> mode.  This suppresses <quote>does not
         exist</quote> errors that might otherwise be reported.  This
         option is not valid unless <option>--clean</option> is also
-        specified.  This option is implied when restoring a
-        <application>pg_dumpall</application> archive with
-        <option>--clean</option>.
+        specified.
        </para>
       </listitem>
      </varlistentry>
@@ -800,21 +723,6 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
-     <varlistentry>
-      <term><option>--no-globals</option></term>
-      <listitem>
-       <para>
-        Do not restore global objects (roles and tablespaces).  When
-        <option>-C</option>/<option>--create</option> is not specified,
-        databases that do not already exist on the target server are skipped.
-       </para>
-       <para>
-        This option is only relevant when restoring from a non-plain-text
-        archive made using <application>pg_dumpall</application>.
-       </para>
-      </listitem>
-     </varlistentry>
-
      <varlistentry>
       <term><option>--no-policies</option></term>
       <listitem>
@@ -1240,21 +1148,6 @@ CREATE DATABASE foo WITH TEMPLATE template0;
      </para>
     </listitem>
 
-    <listitem>
-     <para>
-      The following options cannot be used when restoring from a non-plain-text
-      archive made using <application>pg_dumpall</application>:
-      <option>-a/--data-only</option>,
-      <option>-l/--list</option>,
-      <option>-L/--use-list</option>,
-      <option>--no-schema</option>,
-      <option>--statistics-only</option>, and
-      <option>--strict-names</option>.
-      Also, if the <option>--section</option> option is used, it must
-      include <option>pre-data</option>.
-     </para>
-    </listitem>
-
    </itemizedlist>
   </para>
 
diff --git a/src/bin/pg_dump/meson.build b/src/bin/pg_dump/meson.build
index 7c9a475963b..79bd5036841 100644
--- a/src/bin/pg_dump/meson.build
+++ b/src/bin/pg_dump/meson.build
@@ -103,7 +103,6 @@ tests += {
       't/004_pg_dump_parallel.pl',
       't/005_pg_dump_filterfile.pl',
       't/006_pg_dump_compress.pl',
-	  't/007_pg_dumpall.pl',
       't/010_dump_connstr.pl',
     ],
   },
diff --git a/src/bin/pg_dump/parallel.c b/src/bin/pg_dump/parallel.c
index a7bed5ecccf..7e2e9f958ea 100644
--- a/src/bin/pg_dump/parallel.c
+++ b/src/bin/pg_dump/parallel.c
@@ -333,20 +333,6 @@ on_exit_close_archive(Archive *AHX)
 	on_exit_nicely(archive_close_connection, &shutdown_info);
 }
 
-/*
- * Update the archive handle in the on_exit callback registered by
- * on_exit_close_archive(). When pg_restore processes a pg_dumpall archive
- * containing multiple databases, each database is restored from a separate
- * archive. After closing one archive and opening the next, we update the
- * shutdown_info to reference the new archive handle so the cleanup callback
- * will close the correct archive on exit.
- */
-void
-replace_on_exit_close_archive(Archive *AHX)
-{
-	shutdown_info.AHX = AHX;
-}
-
 /*
  * on_exit_nicely handler for shutting down database connections and
  * worker processes cleanly.
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 28e7ff6fa16..c7bdda1deed 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -313,7 +313,7 @@ extern void SetArchiveOptions(Archive *AH, DumpOptions *dopt, RestoreOptions *ro

 
index 2fd773ad84f..6d2f2ea8b69 100644
 
@@ -340,14 +340,9 @@ ProcessArchiveRestoreOptions(Archive *AHX)
 		StrictNamesCheck(ropt);
 }
 
-/*
- * RestoreArchive
- *
@@ -464,7 +459,7 @@ RestoreArchive(Archive *AHX, bool append_data)

@@ -767,19 +762,6 @@ RestoreArchive(Archive *AHX, bool append_data)
 			if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) == 0)
 				continue;		/* ignore if not to be dumped at all */
 
-			/* Skip if no-tablespace is given. */
-			if (ropt->noTablespace && te && te->desc &&
-				(strcmp(te->desc, "TABLESPACE") == 0))
-				continue;
-
-			/*
-			 * Skip DROP DATABASE/ROLES/TABLESPACE if we didn't specify
-			 * --clean
-			 */
-			if (!ropt->dropSchema && te && te->desc &&
-				strcmp(te->desc, "DROP_GLOBAL") == 0)
-				continue;
-
 			switch (_tocEntryRestorePass(te))
 			{
 				case RESTORE_PASS_MAIN:
@@ -1335,7 +1317,7 @@ PrintTOCSummary(Archive *AHX)
 
 	sav = SaveOutput(AH);
 	if (ropt->filename)
 
 	if (strftime(stamp_str, sizeof(stamp_str), PGDUMP_STRFTIME_FMT,
 				 localtime(&AH->createDate)) == 0)
@@ -1710,15 +1692,11 @@ archprintf(Archive *AH, const char *fmt, ...)
 
 /*******************************
  * Stuff below here should be 'private' to the archiver routines
- *
- * If append_data is set, then append data into file as we are restoring dump
- * of multiple databases which was taken by pg_dumpall.
  *******************************/
 
 static void
 SetOutput(ArchiveHandle *AH, const char *filename,
-		  const pg_compress_specification compression_spec,
-		  bool append_data)
+		  const pg_compress_specification compression_spec)
 {
 	CompressFileHandle *CFH;
 	const char *mode;
@@ -1738,7 +1716,7 @@ SetOutput(ArchiveHandle *AH, const char *filename,
 	else
 		fn = fileno(stdout);
 
-	if (append_data || AH->mode == archModeAppend)
+	if (AH->mode == archModeAppend)
 		mode = PG_BINARY_A;
 	else
 		mode = PG_BINARY_W;
@@ -2414,7 +2392,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
 
 	/* initialize for backwards compatible string processing */
 	AH->public.encoding = 0;	/* PG_SQL_ASCII */
-	AH->public.std_strings = true;
+	AH->public.std_strings = false;
 
 	/* sql error handling */
 	AH->public.exit_on_error = true;
@@ -3050,16 +3028,6 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
 			return 0;
 	}
 
-	/*
-	 * Global object TOC entries (e.g., ROLEs or TABLESPACEs) must not be
-	 * ignored.
-	 */
-	if (strcmp(te->desc, "ROLE") == 0 ||
-		strcmp(te->desc, "ROLE PROPERTIES") == 0 ||
-		strcmp(te->desc, "TABLESPACE") == 0 ||
-		strcmp(te->desc, "DROP_GLOBAL") == 0)
-		return REQ_SCHEMA;
-
 	/*
 	 * Process exclusions that affect certain classes of TOC entries.
 	 */
@@ -3095,14 +3063,6 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
 		if (ropt->no_subscriptions &&
 			strncmp(te->tag, "SUBSCRIPTION", strlen("SUBSCRIPTION")) == 0)
 			return 0;
-
-		/*
-		 * Comments on global objects (ROLEs or TABLESPACEs) should not be
-		 * skipped, since global objects themselves are never skipped.
-		 */
-		if (strncmp(te->tag, "ROLE", strlen("ROLE")) == 0 ||
-			strncmp(te->tag, "TABLESPACE", strlen("TABLESPACE")) == 0)
-			return REQ_SCHEMA;
 	}
 
 	/*
@@ -3132,14 +3092,6 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
 		if (ropt->no_subscriptions &&
 			strncmp(te->tag, "SUBSCRIPTION", strlen("SUBSCRIPTION")) == 0)
 			return 0;
-
-		/*
-		 * Security labels on global objects (ROLEs or TABLESPACEs) should not
-		 * be skipped, since global objects themselves are never skipped.
-		 */
-		if (strncmp(te->tag, "ROLE", strlen("ROLE")) == 0 ||
-			strncmp(te->tag, "TABLESPACE", strlen("TABLESPACE")) == 0)
-			return REQ_SCHEMA;
 	}
 
 	/* If it's a subscription, maybe ignore it */
@@ -3915,9 +3867,6 @@ _getObjectDescription(PQExpBuffer buf, const TocEntry *te)
 	else if (strcmp(type, "CAST") == 0 ||
 			 strcmp(type, "CHECK CONSTRAINT") == 0 ||
 			 strcmp(type, "CONSTRAINT") == 0 ||
-			 strcmp(type, "DROP_GLOBAL") == 0 ||
-			 strcmp(type, "ROLE PROPERTIES") == 0 ||
-			 strcmp(type, "ROLE") == 0 ||
 			 strcmp(type, "DATABASE PROPERTIES") == 0 ||
 			 strcmp(type, "DEFAULT") == 0 ||
 			 strcmp(type, "FK CONSTRAINT") == 0 ||
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 1218bf6a6a1..c1528d78853 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -394,7 +394,6 @@ struct _tocEntry

 extern void warn_or_exit_horribly(ArchiveHandle *AH, const char *fmt, ...) pg_attribute_printf(2, 3);
 
index ae3a7c80cbf..55b1e4e85d0 100644

 
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index a0f7f8e2168..c56437d6057 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1279,7 +1279,7 @@ main(int argc, char **argv)
 	 * right now.
 	 */
 	if (plainText)
-		RestoreArchive(fout, false);
+		RestoreArchive(fout);
 
 	CloseArchive(fout);
 
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index c1f43113c53..b9653f0aefe 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -1,20 +1,13 @@
 /*-------------------------------------------------------------------------
  *
  * pg_dumpall.c
- *	  pg_dumpall dumps all databases and global objects (roles and
- *	  tablespaces) from a PostgreSQL cluster.
- *
- *	  For text format output, globals are written directly and pg_dump is
- *	  invoked for each database, with all output going to stdout or a file.
- *
- *	  For non-text formats (custom, directory, tar), a directory is created
- *	  containing a toc.glo file with global objects, a map.dat file mapping
- *	  database OIDs to names, and a databases/ subdirectory with individual
- *	  pg_dump archives for each database.
  *
  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
+ * pg_dumpall forces all pg_dump output to be text, since it also outputs
+ * text into the same output stream.
+ *
  * src/bin/pg_dump/pg_dumpall.c
  *
  *-------------------------------------------------------------------------
@@ -34,11 +27,9 @@
 #include "common/string.h"
 #include "connectdb.h"
 #include "dumputils.h"
-#include "fe_utils/option_utils.h"
 #include "fe_utils/string_utils.h"
 #include "filter.h"
 #include "getopt_long.h"
-#include "pg_backup_archiver.h"
 
 /* version string we expect back from pg_dump */
 #define PGDUMP_VERSIONSTR "pg_dump (PostgreSQL) " PG_VERSION "\n"
@@ -76,19 +67,15 @@ static void dropDBs(PGconn *conn);
 static void dumpUserConfig(PGconn *conn, const char *username);
 static void dumpDatabases(PGconn *conn);
 static void dumpTimestamp(const char *msg);
-static int	runPgDump(const char *dbname, const char *create_opts, char *dbfile);
+static int	runPgDump(const char *dbname, const char *create_opts);
 static void buildShSecLabels(PGconn *conn,
 							 const char *catalog_name, Oid objectId,
 							 const char *objtype, const char *objname,
 							 PQExpBuffer buffer);
 static void executeCommand(PGconn *conn, const char *query);
-static void check_for_invalid_global_names(PGconn *conn,
-										   SimpleStringList *excluded_names);
 static void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns,
 								   SimpleStringList *names);
 static void read_dumpall_filters(const char *filename, SimpleStringList *pattern);
-static ArchiveFormat parseDumpFormat(const char *format);
-static int	createDumpId(void);
 
 static char pg_dump_bin[MAXPGPATH];
 static PQExpBuffer pgdumpopts;
@@ -136,10 +123,6 @@ static SimpleStringList database_exclude_patterns = {NULL, NULL};
 static SimpleStringList database_exclude_names = {NULL, NULL};
 
 static char *restrict_key;
-static Archive *fout = NULL;
-static int	dumpIdVal = 0;
-static ArchiveFormat archDumpFormat = archNull;
-static const CatalogId nilCatalogId = {0, 0};
 
 int
@@ -165,7 +148,6 @@ main(int argc, char *argv[])
 
 		/*
 		 * the following options don't have an equivalent short option letter
@@ -215,19 +197,16 @@ main(int argc, char *argv[])
 	char	   *pgdb = NULL;
 	char	   *use_role = NULL;
 	const char *dumpencoding = NULL;
-	const char *format_name = "p";
 	trivalue	prompt_password = TRI_DEFAULT;
 	bool		data_only = false;
 	bool		globals_only = false;
 	bool		roles_only = false;
-	bool		schema_only = false;
 	bool		tablespaces_only = false;
 	PGconn	   *conn;
 	int			encoding;
 	int			c,
 				ret;
 	int			optindex;
-	DumpOptions dopt;
 
 	pg_logging_init(argv[0]);
 	pg_logging_set_level(PG_LOG_WARNING);
@@ -265,9 +244,8 @@ main(int argc, char *argv[])
 	}
 
 	pgdumpopts = createPQExpBuffer();
-	InitDumpOptions(&dopt);
 
-	while ((c = getopt_long(argc, argv, "acd:E:f:F:gh:l:Op:rsS:tU:vwWx", long_options, &optindex)) != -1)
+	while ((c = getopt_long(argc, argv, "acd:E:f:gh:l:Op:rsS:tU:vwWx", long_options, &optindex)) != -1)
 	{
 		switch (c)
 		{
@@ -295,9 +273,7 @@ main(int argc, char *argv[])
 				appendPQExpBufferStr(pgdumpopts, " -f ");
 				appendShellString(pgdumpopts, filename);
 				break;
-			case 'F':
-				format_name = pg_strdup(optarg);
-				break;
+
 			case 'g':
 				globals_only = true;
 				break;
@@ -323,7 +299,6 @@ main(int argc, char *argv[])
 				break;
 
 			case 's':
-				schema_only = true;
 				appendPQExpBufferStr(pgdumpopts, " -s");
 				break;
 
@@ -338,7 +313,6 @@ main(int argc, char *argv[])
 
 			case 'U':
 				pguser = pg_strdup(optarg);
-				dopt.cparams.username = pg_strdup(optarg);
 				break;
 
 			case 'v':
@@ -421,74 +395,50 @@ main(int argc, char *argv[])
 		exit_nicely(1);
 	}
 
-	/* --exclude-database is incompatible with global *-only options */
-	check_mut_excl_opts(database_exclude_patterns.head, "--exclude-database",
-						globals_only, "-g/--globals-only",
-						roles_only, "-r/--roles-only",
-						tablespaces_only, "-t/--tablespaces-only");
+	if (database_exclude_patterns.head != NULL &&
+		(globals_only || roles_only || tablespaces_only))
+	{
+		pg_log_error("option %s cannot be used together with %s, %s, or %s",
+					 "--exclude-database",
+					 "-g/--globals-only", "-r/--roles-only", "-t/--tablespaces-only");
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+		exit_nicely(1);
+	}
 
-	/* *-only options are incompatible with each other */
-	check_mut_excl_opts(data_only, "-a/--data-only",
-						globals_only, "-g/--globals-only",
-						roles_only, "-r/--roles-only",
-						schema_only, "-s/--schema-only",
-						statistics_only, "--statistics-only",
-						tablespaces_only, "-t/--tablespaces-only");
+	/* Make sure the user hasn't specified a mix of globals-only options */
+	if (globals_only && roles_only)
+	{
+		pg_log_error("options %s and %s cannot be used together",
+					 "-g/--globals-only", "-r/--roles-only");
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+		exit_nicely(1);
+	}
 
-	/* --no-* and *-only for same thing are incompatible */
-	check_mut_excl_opts(data_only, "-a/--data-only",
-						no_data, "--no-data");
-	check_mut_excl_opts(schema_only, "-s/--schema-only",
-						no_schema, "--no-schema");
-	check_mut_excl_opts(statistics_only, "--statistics-only",
-						no_statistics, "--no-statistics");
-
-	/* --statistics and --no-statistics are incompatible */
-	check_mut_excl_opts(with_statistics, "--statistics",
-						no_statistics, "--no-statistics");
-
-	/* --statistics is incompatible with *-only (except --statistics-only) */
-	check_mut_excl_opts(with_statistics, "--statistics",
-						data_only, "-a/--data-only",
-						globals_only, "-g/--globals-only",
-						roles_only, "-r/--roles-only",
-						schema_only, "-s/--schema-only",
-						tablespaces_only, "-t/--tablespaces-only");
-
-	/* --clean and --data-only are incompatible */
-	check_mut_excl_opts(output_clean, "-c/--clean",
-						data_only, "-a/--data-only");
+	if (globals_only && tablespaces_only)
+	{
+		pg_log_error("options %s and %s cannot be used together",
+					 "-g/--globals-only", "-t/--tablespaces-only");
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+		exit_nicely(1);
+	}
 
 	if (if_exists && !output_clean)
 		pg_fatal("option %s requires option %s",
 				 "--if-exists", "-c/--clean");
 
-	/* Get format for dump. */
-	archDumpFormat = parseDumpFormat(format_name);
+	/* --clean and --data-only are incompatible */
+	if (output_clean && data_only)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-c/--clean", "-a/--data-only");
 
-	/*
-	 * If a non-plain format is specified, a file name is also required as the
-	 * path to the main directory.
-	 */
-	if (archDumpFormat != archNull &&
-		(!filename || strcmp(filename, "") == 0))
+	if (roles_only && tablespaces_only)
 	{
-		pg_log_error("option %s=d|c|t requires option %s",
-					 "-F/--format", "-f/--file");
+		pg_log_error("options %s and %s cannot be used together",
+					 "-r/--roles-only", "-t/--tablespaces-only");
 		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit_nicely(1);
 	}
 
-	/* restrict-key is only supported with --format=plain */
-	if (archDumpFormat != archNull && restrict_key)
-		pg_fatal("option %s can only be used with %s=plain",
-				 "--restrict-key", "--format");
-
-	/* --clean and -g/--globals-only cannot be used together in non-text dump */
-	if (archDumpFormat != archNull && output_clean && globals_only)
-		pg_fatal("options %s and %s cannot be used together in non-text dump",
-				 "--clean", "-g/--globals-only");
-
 	/*
 	 * If password values are not required in the dump, switch to using
 	 * pg_roles which is equally useful, just more likely to have unrestricted
@@ -549,27 +499,6 @@ main(int argc, char *argv[])
 	if (sequence_data)
 		appendPQExpBufferStr(pgdumpopts, " --sequence-data");
 
-	/*
-	 * Open the output file if required, otherwise use stdout.  If required,
-	 * then create new directory.
-	 */
-	if (archDumpFormat != archNull)
-	{
-		Assert(filename);
-
-		/* Create new directory or accept the empty existing directory. */
-		create_or_open_dir(filename);
-	}
-	else if (filename)
-	{
-		OPF = fopen(filename, PG_BINARY_W);
-		if (!OPF)
-			pg_fatal("could not open output file \"%s\": %m",
-					 filename);
-	}
-	else
-		OPF = stdout;
-
 	/*
 	 * If you don't provide a restrict key, one will be appointed for you.
 	 */
@@ -619,6 +548,19 @@ main(int argc, char *argv[])
 	expand_dbname_patterns(conn, &database_exclude_patterns,
 						   &database_exclude_names);
 
+	/*
@@ -656,121 +598,35 @@ main(int argc, char *argv[])
 
-	/* create a archive file for global commands. */
-	if (archDumpFormat != archNull)
-	{
-		PQExpBuffer qry = createPQExpBuffer();
-		char		global_path[MAXPGPATH];
-		const char *encname;
-		pg_compress_specification compression_spec = {0};
-
-		/*
-		 * Check that no global object names contain newlines or carriage
-		 * returns, which would break the map.dat file format.  This is only
-		 * needed for servers older than v19, which started prohibiting such
-		 * names.
-		 */
-		if (server_version < 190000)
-			check_for_invalid_global_names(conn, &database_exclude_names);
-
-		/* Set file path for global sql commands. */
-		snprintf(global_path, MAXPGPATH, "%s/toc.glo", filename);
-
-		/* Open the output file */
-		fout = CreateArchive(global_path, archCustom, compression_spec,
-							 dosync, archModeWrite, NULL, DATA_DIR_SYNC_METHOD_FSYNC);
-
-		/* Make dump options accessible right away */
-		SetArchiveOptions(fout, &dopt, NULL);
-
-		((ArchiveHandle *) fout)->connection = conn;
-		((ArchiveHandle *) fout)->public.numWorkers = 1;
-
-		/* Register the cleanup hook */
-		on_exit_close_archive(fout);
-
-		/* Let the archiver know how noisy to be */
-		fout->verbose = verbose;
-
-		/*
-		 * We allow the server to be back to 9.2, and up to any minor release
-		 * of our own major version.  (See also version check in
-		 * pg_dumpall.c.)
-		 */
-		fout->minRemoteVersion = 90200;
-		fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
-		fout->numWorkers = 1;
-
-		/* Dump default_transaction_read_only. */
-		appendPQExpBufferStr(qry, "SET default_transaction_read_only = off;\n\n");
-		ArchiveEntry(fout,
-					 nilCatalogId,	/* catalog ID */
-					 createDumpId(),	/* dump ID */
-					 ARCHIVE_OPTS(.tag = "default_transaction_read_only",
-								  .description = "default_transaction_read_only",
-								  .section = SECTION_PRE_DATA,
-								  .createStmt = qry->data));
-		resetPQExpBuffer(qry);
-
-		/* Put the correct encoding into the archive */
-		encname = pg_encoding_to_char(encoding);
-
-		appendPQExpBufferStr(qry, "SET client_encoding = ");
-		appendStringLiteralAH(qry, encname, fout);
-		appendPQExpBufferStr(qry, ";\n");
-		ArchiveEntry(fout,
-					 nilCatalogId,	/* catalog ID */
-					 createDumpId(),	/* dump ID */
-					 ARCHIVE_OPTS(.tag = "client_encoding",
-								  .description = "client_encoding",
-								  .section = SECTION_PRE_DATA,
-								  .createStmt = qry->data));
-		resetPQExpBuffer(qry);
-
-		/* Put the correct escape string behavior into the archive. */
-		appendPQExpBufferStr(qry, "SET standard_conforming_strings = 'on';\n");
-		ArchiveEntry(fout,
-					 nilCatalogId,	/* catalog ID */
-					 createDumpId(),	/* dump ID */
-					 ARCHIVE_OPTS(.tag = "standard_conforming_strings",
-								  .description = "standard_conforming_strings",
-								  .section = SECTION_PRE_DATA,
-								  .createStmt = qry->data));
-		destroyPQExpBuffer(qry);
-	}
-	else
-	{
-		fprintf(OPF, "--\n-- PostgreSQL database cluster dump\n--\n\n");
-
-		if (verbose)
-			dumpTimestamp("Started on");
-
-		/*
-		 * Enter restricted mode to block any unexpected psql meta-commands. A
-		 * malicious source might try to inject a variety of things via bogus
-		 * responses to queries.  While we cannot prevent such sources from
-		 * affecting the destination at restore time, we can block psql
-		 * meta-commands so that the client machine that runs psql with the
-		 * dump output remains unaffected.
-		 */
-		fprintf(OPF, "\\restrict %s\n\n", restrict_key);
-
-		/*
-		 * We used to emit \connect postgres here, but that served no purpose
-		 * other than to break things for installations without a postgres
-		 * database.  Everything we're restoring here is a global, so
-		 * whichever database we're connected to at the moment is fine.
-		 */
-
-		/* Restore will need to write to the target cluster */
-		fprintf(OPF, "SET default_transaction_read_only = off;\n\n");
-
-		/* Replicate encoding and standard_conforming_strings in output */
-		fprintf(OPF, "SET client_encoding = '%s';\n",
-				pg_encoding_to_char(encoding));
-		fprintf(OPF, "SET standard_conforming_strings = on;\n");
-		fprintf(OPF, "\n");
-	}
+	fprintf(OPF, "--\n-- PostgreSQL database cluster dump\n--\n\n");
+	if (verbose)
+		dumpTimestamp("Started on");
+
+	/*
+	 * Enter restricted mode to block any unexpected psql meta-commands.  A
+	 * malicious source might try to inject a variety of things via bogus
+	 * responses to queries.  While we cannot prevent such sources from
+	 * affecting the destination at restore time, we can block psql
+	 * meta-commands so that the client machine that runs psql with the dump
+	 * output remains unaffected.
+	 */
+	fprintf(OPF, "\\restrict %s\n\n", restrict_key);
+
+	/*
+	 * We used to emit \connect postgres here, but that served no purpose
+	 * other than to break things for installations without a postgres
+	 * database.  Everything we're restoring here is a global, so whichever
+	 * database we're connected to at the moment is fine.
+	 */
+
+	/* Restore will need to write to the target cluster */
+	fprintf(OPF, "SET default_transaction_read_only = off;\n\n");
+
+	/* Replicate encoding and standard_conforming_strings in output */
+	fprintf(OPF, "SET client_encoding = '%s';\n",
+			pg_encoding_to_char(encoding));
+	fprintf(OPF, "SET standard_conforming_strings = on;\n");
+	fprintf(OPF, "\n");
 
 	if (!data_only && !statistics_only && !no_schema)
 	{
@@ -779,14 +635,8 @@ main(int argc, char *argv[])
 		 * dependency analysis because databases never depend on each other,
 		 * and tablespaces never depend on each other.  Roles could have
 		 * grants to each other, but DROP ROLE will clean those up silently.
-		 *
-		 * For non-text formats, pg_dumpall unconditionally process --clean
-		 * option. In contrast, pg_restore only applies it if the user
-		 * explicitly provides the flag.  This discrepancy resolves corner
-		 * cases where pg_restore requires cleanup instructions that may be
-		 * missing from a standard pg_dumpall output.
 		 */
-		if (output_clean || archDumpFormat != archNull)
+		if (output_clean)
 		{
 			if (!globals_only && !roles_only && !tablespaces_only)
 				dropDBs(conn);
@@ -820,45 +670,28 @@ main(int argc, char *argv[])
 			dumpTablespaces(conn);
 	}
 
-	if (archDumpFormat == archNull)
-	{
-		/*
-		 * Exit restricted mode just before dumping the databases.  pg_dump
-		 * will handle entering restricted mode again as appropriate.
-		 */
-		fprintf(OPF, "\\unrestrict %s\n\n", restrict_key);
-	}
+	/*
+	 * Exit restricted mode just before dumping the databases.  pg_dump will
+	 * handle entering restricted mode again as appropriate.
+	 */
+	fprintf(OPF, "\\unrestrict %s\n\n", restrict_key);
 
 		dumpDatabases(conn);
 
-	if (archDumpFormat == archNull)
-	{
-		PQfinish(conn);
-
-		if (verbose)
-			dumpTimestamp("Completed on");
-		fprintf(OPF, "--\n-- PostgreSQL database cluster dump complete\n--\n\n");
+	PQfinish(conn);
 
-		if (filename)
-		{
-			fclose(OPF);
+	if (verbose)
+		dumpTimestamp("Completed on");
+	fprintf(OPF, "--\n-- PostgreSQL database cluster dump complete\n--\n\n");
 
-			/* sync the resulting file, errors are not fatal */
-			if (dosync)
-				(void) fsync_fname(filename, false);
-		}
-	}
-	else
+	if (filename)
 	{
-		RestoreOptions *ropt;
-
-		ropt = NewRestoreOptions();
-		SetArchiveOptions(fout, &dopt, ropt);
+		fclose(OPF);
 
-		/* Mark which entries should be output */
-		ProcessArchiveRestoreOptions(fout);
-		CloseArchive(fout);
+		/* sync the resulting file, errors are not fatal */
+		if (dosync)
+			(void) fsync_fname(filename, false);
 	}
 
 	exit_nicely(0);
@@ -868,14 +701,12 @@ main(int argc, char *argv[])
 static void
 help(void)
 {
-	printf(_("%s exports a PostgreSQL database cluster as an SQL script or to other formats.\n\n"), progname);
+	printf(_("%s exports a PostgreSQL database cluster as an SQL script.\n\n"), progname);
 	printf(_("Usage:\n"));
 	printf(_("  %s [OPTION]...\n"), progname);
 
@@ -970,45 +801,24 @@ dropRoles(PGconn *conn)
 
 	i_rolname = PQfnumber(res, "rolname");
 
-	if (PQntuples(res) > 0 && archDumpFormat == archNull)
+	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Drop roles\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
 	{
 		const char *rolename;
-		PQExpBuffer delQry = createPQExpBuffer();
 
 		rolename = PQgetvalue(res, i, i_rolname);
 
-		if (archDumpFormat == archNull)
-		{
-			appendPQExpBuffer(delQry, "DROP ROLE %s%s;\n",
-							  if_exists ? "IF EXISTS " : "",
-							  fmtId(rolename));
-			fprintf(OPF, "%s", delQry->data);
-		}
-		else
-		{
-			appendPQExpBuffer(delQry, "DROP ROLE IF EXISTS %s;\n",
-							  fmtId(rolename));
-
-			ArchiveEntry(fout,
-						 nilCatalogId,	/* catalog ID */
-						 createDumpId(),	/* dump ID */
-						 ARCHIVE_OPTS(.tag = psprintf("ROLE %s", fmtId(rolename)),
-									  .description = "DROP_GLOBAL",
-									  .section = SECTION_PRE_DATA,
-									  .createStmt = delQry->data));
-		}
-
-		destroyPQExpBuffer(delQry);
+		fprintf(OPF, "DROP ROLE %s%s;\n",
+				if_exists ? "IF EXISTS " : "",
+				fmtId(rolename));
 	}
 
 	PQclear(res);
 	destroyPQExpBuffer(buf);
 
-		fprintf(OPF, "\n\n");
+	fprintf(OPF, "\n\n");
 }
 
 /*
@@ -1018,8 +828,6 @@ static void
 dumpRoles(PGconn *conn)
 {
 	PQExpBuffer buf = createPQExpBuffer();
-	PQExpBuffer comment_buf = createPQExpBuffer();
-	PQExpBuffer seclabel_buf = createPQExpBuffer();
 	PGresult   *res;
 	int			i_oid,
 				i_rolname,
@@ -1091,7 +899,7 @@ dumpRoles(PGconn *conn)
 	i_rolcomment = PQfnumber(res, "rolcomment");
 	i_is_current_user = PQfnumber(res, "is_current_user");
 
-	if (PQntuples(res) > 0 && archDumpFormat == archNull)
+	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Roles\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
@@ -1110,8 +918,6 @@ dumpRoles(PGconn *conn)
 		}
 
 		resetPQExpBuffer(buf);
-		resetPQExpBuffer(comment_buf);
-		resetPQExpBuffer(seclabel_buf);
 
 		if (binary_upgrade)
 		{
@@ -1188,53 +994,17 @@ dumpRoles(PGconn *conn)
 
 		if (!no_comments && !PQgetisnull(res, i, i_rolcomment))
 		{
-			appendPQExpBuffer(comment_buf, "COMMENT ON ROLE %s IS ", fmtId(rolename));
-			appendStringLiteralConn(comment_buf, PQgetvalue(res, i, i_rolcomment), conn);
-			appendPQExpBufferStr(comment_buf, ";\n");
+			appendPQExpBuffer(buf, "COMMENT ON ROLE %s IS ", fmtId(rolename));
+			appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolcomment), conn);
+			appendPQExpBufferStr(buf, ";\n");
 		}
 
 		if (!no_security_labels)
 			buildShSecLabels(conn, "pg_authid", auth_oid,
 							 "ROLE", rolename,
-							 seclabel_buf);
+							 buf);
 
-		if (archDumpFormat == archNull)
-		{
-			fprintf(OPF, "%s", buf->data);
-			fprintf(OPF, "%s", comment_buf->data);
-
-			if (seclabel_buf->data[0] != '\0')
-				fprintf(OPF, "%s", seclabel_buf->data);
-		}
-		else
-		{
-			char	   *tag = psprintf("ROLE %s", fmtId(rolename));
-
-			ArchiveEntry(fout,
-						 nilCatalogId,	/* catalog ID */
-						 createDumpId(),	/* dump ID */
-						 ARCHIVE_OPTS(.tag = tag,
-									  .description = "ROLE",
-									  .section = SECTION_PRE_DATA,
-									  .createStmt = buf->data));
-			if (comment_buf->data[0] != '\0')
-				ArchiveEntry(fout,
-							 nilCatalogId,	/* catalog ID */
-							 createDumpId(),	/* dump ID */
-							 ARCHIVE_OPTS(.tag = tag,
-										  .description = "COMMENT",
-										  .section = SECTION_PRE_DATA,
-										  .createStmt = comment_buf->data));
-
-			if (seclabel_buf->data[0] != '\0')
-				ArchiveEntry(fout,
-							 nilCatalogId,	/* catalog ID */
-							 createDumpId(),	/* dump ID */
-							 ARCHIVE_OPTS(.tag = tag,
-										  .description = "SECURITY LABEL",
-										  .section = SECTION_PRE_DATA,
-										  .createStmt = seclabel_buf->data));
-		}
+		fprintf(OPF, "%s", buf->data);
 	}
 
 	/*
@@ -1242,7 +1012,7 @@ dumpRoles(PGconn *conn)
 	 * We do it this way because config settings for roles could mention the
 	 * names of other roles.
 	 */
-	if (PQntuples(res) > 0 && archDumpFormat == archNull)
+	if (PQntuples(res) > 0)
 		fprintf(OPF, "\n--\n-- User Configurations\n--\n");
 
 	for (i = 0; i < PQntuples(res); i++)
@@ -1250,12 +1020,9 @@ dumpRoles(PGconn *conn)
 
 	PQclear(res);
 
-		fprintf(OPF, "\n\n");
+	fprintf(OPF, "\n\n");
 
 	destroyPQExpBuffer(buf);
-	destroyPQExpBuffer(comment_buf);
-	destroyPQExpBuffer(seclabel_buf);
 }

@@ -1269,7 +1036,6 @@ static void
 dumpRoleMembership(PGconn *conn)
 {
 	PQExpBuffer buf = createPQExpBuffer();
-	PQExpBuffer querybuf = createPQExpBuffer();
 	PQExpBuffer optbuf = createPQExpBuffer();
 	PGresult   *res;
 	int			start = 0,
@@ -1332,7 +1098,7 @@ dumpRoleMembership(PGconn *conn)
 	i_inherit_option = PQfnumber(res, "inherit_option");
 	i_set_option = PQfnumber(res, "set_option");
 
-	if (PQntuples(res) > 0 && archDumpFormat == archNull)
+	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Role memberships\n--\n\n");
 
 	/*
@@ -1479,9 +1245,8 @@ dumpRoleMembership(PGconn *conn)
 
 				/* Generate the actual GRANT statement. */
 				resetPQExpBuffer(optbuf);
-				resetPQExpBuffer(querybuf);
-				appendPQExpBuffer(querybuf, "GRANT %s", fmtId(role));
-				appendPQExpBuffer(querybuf, " TO %s", fmtId(member));
+				fprintf(OPF, "GRANT %s", fmtId(role));
+				fprintf(OPF, " TO %s", fmtId(member));
 				if (*admin_option == 't')
 					appendPQExpBufferStr(optbuf, "ADMIN OPTION");
 				if (dump_grant_options)
@@ -1502,21 +1267,10 @@ dumpRoleMembership(PGconn *conn)
 					appendPQExpBufferStr(optbuf, "SET FALSE");
 				}
 				if (optbuf->data[0] != '\0')
-					appendPQExpBuffer(querybuf, " WITH %s", optbuf->data);
+					fprintf(OPF, " WITH %s", optbuf->data);
 				if (dump_this_grantor)
-					appendPQExpBuffer(querybuf, " GRANTED BY %s", fmtId(grantor));
-				appendPQExpBufferStr(querybuf, ";\n");
-
-				if (archDumpFormat == archNull)
-					fprintf(OPF, "%s", querybuf->data);
-				else
-					ArchiveEntry(fout,
-								 nilCatalogId,	/* catalog ID */
-								 createDumpId(),	/* dump ID */
-								 ARCHIVE_OPTS(.tag = psprintf("ROLE %s", fmtId(role)),
-											  .description = "ROLE PROPERTIES",
-											  .section = SECTION_PRE_DATA,
-											  .createStmt = querybuf->data));
+					fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
+				fprintf(OPF, ";\n");
 			}
 		}
 
@@ -1527,11 +1281,8 @@ dumpRoleMembership(PGconn *conn)
 
 	PQclear(res);
 	destroyPQExpBuffer(buf);
-	destroyPQExpBuffer(querybuf);
-	destroyPQExpBuffer(optbuf);
 
-		fprintf(OPF, "\n\n");
+	fprintf(OPF, "\n\n");
 }

@@ -1558,7 +1309,7 @@ dumpRoleGUCPrivs(PGconn *conn)
 					   "FROM pg_catalog.pg_parameter_acl "
 					   "ORDER BY 1");
 
-	if (PQntuples(res) > 0 && archDumpFormat == archNull)
+	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
@@ -1583,25 +1334,14 @@ dumpRoleGUCPrivs(PGconn *conn)
 			exit_nicely(1);
 		}
 
-		if (archDumpFormat == archNull)
-			fprintf(OPF, "%s", buf->data);
-		else
-			ArchiveEntry(fout,
-						 nilCatalogId,	/* catalog ID */
-						 createDumpId(),	/* dump ID */
-						 ARCHIVE_OPTS(.tag = psprintf("ROLE %s", fmtId(parowner)),
-									  .description = "ROLE PROPERTIES",
-									  .section = SECTION_PRE_DATA,
-									  .createStmt = buf->data));
+		fprintf(OPF, "%s", buf->data);
 
 		free(fparname);
 		destroyPQExpBuffer(buf);
 	}
 
 	PQclear(res);
-
-		fprintf(OPF, "\n\n");
+	fprintf(OPF, "\n\n");
 }

@@ -1623,41 +1363,21 @@ dropTablespaces(PGconn *conn)
 					   "WHERE spcname !~ '^pg_' "
 					   "ORDER BY 1");
 
-	if (PQntuples(res) > 0 && archDumpFormat == archNull)
+	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Drop tablespaces\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
 	{
 		char	   *spcname = PQgetvalue(res, i, 0);
-		PQExpBuffer delQry = createPQExpBuffer();
 
-		if (archDumpFormat == archNull)
-		{
-			appendPQExpBuffer(delQry, "DROP TABLESPACE %s%s;\n",
-							  if_exists ? "IF EXISTS " : "",
-							  fmtId(spcname));
-			fprintf(OPF, "%s", delQry->data);
-		}
-		else
-		{
-			appendPQExpBuffer(delQry, "DROP TABLESPACE IF EXISTS %s;\n",
-							  fmtId(spcname));
-			ArchiveEntry(fout,
-						 nilCatalogId,	/* catalog ID */
-						 createDumpId(),	/* dump ID */
-						 ARCHIVE_OPTS(.tag = psprintf("TABLESPACE %s", fmtId(spcname)),
-									  .description = "DROP_GLOBAL",
-									  .section = SECTION_PRE_DATA,
-									  .createStmt = delQry->data));
-		}
-
-		destroyPQExpBuffer(delQry);
+		fprintf(OPF, "DROP TABLESPACE %s%s;\n",
+				if_exists ? "IF EXISTS " : "",
+				fmtId(spcname));
 	}
 
 	PQclear(res);
 
-		fprintf(OPF, "\n\n");
+	fprintf(OPF, "\n\n");
 }
 
 /*
@@ -1667,8 +1387,6 @@ static void
 dumpTablespaces(PGconn *conn)
 {
 	PGresult   *res;
-	PQExpBuffer comment_buf = createPQExpBuffer();
-	PQExpBuffer seclabel_buf = createPQExpBuffer();
 	int			i;
 
 	/*
@@ -1685,7 +1403,7 @@ dumpTablespaces(PGconn *conn)
 					   "WHERE spcname !~ '^pg_' "
 					   "ORDER BY 1");
 
-	if (PQntuples(res) > 0 && archDumpFormat == archNull)
+	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Tablespaces\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
@@ -1704,9 +1422,6 @@ dumpTablespaces(PGconn *conn)
 		/* needed for buildACLCommands() */
 		fspcname = pg_strdup(fmtId(spcname));
 
-		resetPQExpBuffer(comment_buf);
-		resetPQExpBuffer(seclabel_buf);
-
 		if (binary_upgrade)
 		{
 			appendPQExpBufferStr(buf, "\n-- For binary upgrade, must preserve pg_tablespace oid\n");
@@ -1748,67 +1463,24 @@ dumpTablespaces(PGconn *conn)
 
 		if (!no_comments && spccomment && spccomment[0] != '\0')
 		{
-			appendPQExpBuffer(comment_buf, "COMMENT ON TABLESPACE %s IS ", fspcname);
-			appendStringLiteralConn(comment_buf, spccomment, conn);
-			appendPQExpBufferStr(comment_buf, ";\n");
+			appendPQExpBuffer(buf, "COMMENT ON TABLESPACE %s IS ", fspcname);
+			appendStringLiteralConn(buf, spccomment, conn);
+			appendPQExpBufferStr(buf, ";\n");
 		}
 
 		if (!no_security_labels)
 			buildShSecLabels(conn, "pg_tablespace", spcoid,
 							 "TABLESPACE", spcname,
-							 seclabel_buf);
+							 buf);
 
-		if (archDumpFormat == archNull)
-		{
-			fprintf(OPF, "%s", buf->data);
-
-			if (comment_buf->data[0] != '\0')
-				fprintf(OPF, "%s", comment_buf->data);
-
-			if (seclabel_buf->data[0] != '\0')
-				fprintf(OPF, "%s", seclabel_buf->data);
-		}
-		else
-		{
-			char	   *tag = psprintf("TABLESPACE %s", fmtId(fspcname));
-
-			ArchiveEntry(fout,
-						 nilCatalogId,	/* catalog ID */
-						 createDumpId(),	/* dump ID */
-						 ARCHIVE_OPTS(.tag = tag,
-									  .description = "TABLESPACE",
-									  .section = SECTION_PRE_DATA,
-									  .createStmt = buf->data));
-
-			if (comment_buf->data[0] != '\0')
-				ArchiveEntry(fout,
-							 nilCatalogId,	/* catalog ID */
-							 createDumpId(),	/* dump ID */
-							 ARCHIVE_OPTS(.tag = tag,
-										  .description = "COMMENT",
-										  .section = SECTION_PRE_DATA,
-										  .createStmt = comment_buf->data));
-
-			if (seclabel_buf->data[0] != '\0')
-				ArchiveEntry(fout,
-							 nilCatalogId,	/* catalog ID */
-							 createDumpId(),	/* dump ID */
-							 ARCHIVE_OPTS(.tag = tag,
-										  .description = "SECURITY LABEL",
-										  .section = SECTION_PRE_DATA,
-										  .createStmt = seclabel_buf->data));
-		}
+		fprintf(OPF, "%s", buf->data);
 
 		free(fspcname);
 		destroyPQExpBuffer(buf);
 	}
 
 	PQclear(res);
-	destroyPQExpBuffer(comment_buf);
-	destroyPQExpBuffer(seclabel_buf);
-
-	if (archDumpFormat == archNull)
-		fprintf(OPF, "\n\n");
+	fprintf(OPF, "\n\n");
 }

@@ -1831,7 +1503,7 @@ dropDBs(PGconn *conn)
 					   "WHERE datallowconn AND datconnlimit != -2 "
 					   "ORDER BY datname");
 
-	if (PQntuples(res) > 0 && archDumpFormat == archNull)
+	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Drop databases (except postgres and template1)\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
@@ -1847,33 +1519,15 @@ dropDBs(PGconn *conn)
 			strcmp(dbname, "template0") != 0 &&
 			strcmp(dbname, "postgres") != 0)
 		{
-			if (archDumpFormat == archNull)
-			{
-				fprintf(OPF, "DROP DATABASE %s%s;\n",
-						if_exists ? "IF EXISTS " : "",
-						fmtId(dbname));
-			}
-			else
-			{
-				char	   *stmt = psprintf("DROP DATABASE IF EXISTS %s;\n",
-											fmtId(dbname));
-
-				ArchiveEntry(fout,
-							 nilCatalogId,	/* catalog ID */
-							 createDumpId(),	/* dump ID */
-							 ARCHIVE_OPTS(.tag = psprintf("DATABASE %s", fmtId(dbname)),
-										  .description = "DROP_GLOBAL",
-										  .section = SECTION_PRE_DATA,
-										  .createStmt = stmt));
-				pg_free(stmt);
-			}
+			fprintf(OPF, "DROP DATABASE %s%s;\n",
+					if_exists ? "IF EXISTS " : "",
+					fmtId(dbname));
 		}
 	}
 
 	PQclear(res);
 
-		fprintf(OPF, "\n\n");
+	fprintf(OPF, "\n\n");
 }

@@ -1895,7 +1549,7 @@ dumpUserConfig(PGconn *conn, const char *username)

-	if (PQntuples(res) > 0 && archDumpFormat == archNull)
+	if (PQntuples(res) > 0)
 	{
 		char	   *sanitized;
 
@@ -1910,17 +1564,7 @@ dumpUserConfig(PGconn *conn, const char *username)
 		makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
 							   "ROLE", username, NULL, NULL,
 							   buf);
-
-		if (archDumpFormat == archNull)
-			fprintf(OPF, "%s", buf->data);
-		else
-			ArchiveEntry(fout,
-						 nilCatalogId,	/* catalog ID */
-						 createDumpId(),	/* dump ID */
-						 ARCHIVE_OPTS(.tag = psprintf("ROLE %s", fmtId(username)),
-									  .description = "ROLE PROPERTIES",
-									  .section = SECTION_PRE_DATA,
-									  .createStmt = buf->data));
+		fprintf(OPF, "%s", buf->data);
 	}
 
 	PQclear(res);
@@ -1990,9 +1634,6 @@ dumpDatabases(PGconn *conn)
 {
 	PGresult   *res;
 	int			i;
-	char		db_subdir[MAXPGPATH];
-	char		dbfilepath[MAXPGPATH];
-	FILE	   *map_file = NULL;
 
 	/*
@@ -2006,55 +1647,19 @@ dumpDatabases(PGconn *conn)
 
-	if (PQntuples(res) > 0 && archDumpFormat == archNull)
+	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Databases\n--\n\n");
 
-	/*
-	 * If directory/tar/custom format is specified, create a subdirectory
-	 * under the main directory and each database dump file or subdirectory
-	 * will be created in that subdirectory by pg_dump.
-	 */
-	if (archDumpFormat != archNull)
-	{
-		char		map_file_path[MAXPGPATH];
-
-		snprintf(db_subdir, MAXPGPATH, "%s/databases", filename);
-
-		/* Create a subdirectory with 'databases' name under main directory. */
-		if (mkdir(db_subdir, pg_dir_create_mode) != 0)
-			pg_fatal("could not create directory \"%s\": %m", db_subdir);
-
-		snprintf(map_file_path, MAXPGPATH, "%s/map.dat", filename);
-
-		/* Create a map file (to store dboid and dbname) */
-		map_file = fopen(map_file_path, PG_BINARY_W);
-		if (!map_file)
-			pg_fatal("could not open file \"%s\": %m", map_file_path);
-
-		fprintf(map_file,
-				"#################################################################\n"
-				"# map.dat\n"
-				"#\n"
-				"# This file maps oids to database names\n"
-				"#\n"
-				"# pg_restore will restore all the databases listed here, unless\n"
-				"# otherwise excluded. You can also inhibit restoration of a\n"
-				"# database by removing the line or commenting out the line with\n"
-				"# a # mark.\n"
-				"#################################################################\n");
-	}
-
 	for (i = 0; i < PQntuples(res); i++)
 	{
 		char	   *dbname = PQgetvalue(res, i, 0);
 		char	   *sanitized;
-		char	   *oid = PQgetvalue(res, i, 1);
-		const char *create_opts = "";
+		const char *create_opts;
 		int			ret;
 
 		/* Skip template0, even if it's not marked !datallowconn. */
@@ -2071,10 +1676,7 @@ dumpDatabases(PGconn *conn)
 		pg_log_info("dumping database \"%s\"", dbname);
 
 		sanitized = sanitize_line(dbname, true);
-
-		if (archDumpFormat == archNull)
-			fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n", sanitized);
-
+		fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n", sanitized);
 		free(sanitized);
 
 		/*
@@ -2089,40 +1691,24 @@ dumpDatabases(PGconn *conn)
 		{
 			if (output_clean)
 				create_opts = "";
 
-		if (filename && archDumpFormat == archNull)
+		if (filename)
 			fclose(OPF);
 
-		/*
-		ret = runPgDump(dbname, create_opts, dbfilepath);
 
-		if (filename && archDumpFormat == archNull)
+		if (filename)
 		{
 			OPF = fopen(filename, PG_BINARY_A);
 			if (!OPF)
@@ -2131,10 +1717,6 @@ dumpDatabases(PGconn *conn)
 		}
 	}
 
-	/* Close map file */
 
@@ -2144,7 +1726,7 @@ dumpDatabases(PGconn *conn)
  * Run pg_dump on dbname, with specified options.
  */
 static int
-runPgDump(const char *dbname, const char *create_opts, char *dbfile)
+runPgDump(const char *dbname, const char *create_opts)
 {
 	PQExpBufferData connstrbuf;
 	PQExpBufferData cmd;
@@ -2153,36 +1735,17 @@ runPgDump(const char *dbname, const char *create_opts, char *dbfile)
 	initPQExpBuffer(&connstrbuf);
 	initPQExpBuffer(&cmd);
 
-		printfPQExpBuffer(&cmd, "\"%s\" %s -f %s %s", pg_dump_bin,
-						  pgdumpopts->data, dbfile, create_opts);
 
 	/*
 	 * Append the database name to the already-constructed stem of connection
@@ -2256,76 +1819,6 @@ executeCommand(PGconn *conn, const char *query)
 }

-/*
- * check_for_invalid_global_names
- *
- * Check that no database, role, or tablespace name contains a newline or
- * carriage return character.  Such characters in database names would break
- * the map.dat file format used for non-plain-text dumps.  Role and tablespace
- * names are also checked because such characters were forbidden starting in
- * v19.
- *
- * Excluded databases are skipped since they won't appear in map.dat.
- */
-static void
-check_for_invalid_global_names(PGconn *conn,
-							   SimpleStringList *excluded_names)
-{
-	PGresult   *res;
-	int			i;
-	PQExpBuffer names;
-	int			count = 0;
-
-	res = executeQuery(conn,
-					   "SELECT datname AS objname, 'database' AS objtype "
-					   "FROM pg_catalog.pg_database "
-					   "WHERE datallowconn AND datconnlimit != -2 "
-					   "UNION ALL "
-					   "SELECT rolname AS objname, 'role' AS objtype "
-					   "FROM pg_catalog.pg_roles "
-					   "UNION ALL "
-					   "SELECT spcname AS objname, 'tablespace' AS objtype "
-					   "FROM pg_catalog.pg_tablespace");
-
-	names = createPQExpBuffer();
-
-	for (i = 0; i < PQntuples(res); i++)
-	{
-		char	   *objname = PQgetvalue(res, i, 0);
-		char	   *objtype = PQgetvalue(res, i, 1);
-
-		/* Skip excluded databases since they won't be in map.dat */
-		if (strcmp(objtype, "database") == 0 &&
-			simple_string_list_member(excluded_names, objname))
-			continue;
-
-		if (strpbrk(objname, "\n\r"))
-		{
-			appendPQExpBuffer(names, "  %s: \"", objtype);
-			for (char *p = objname; *p; p++)
-			{
-				if (*p == '\n')
-					appendPQExpBufferStr(names, "\\n");
-				else if (*p == '\r')
-					appendPQExpBufferStr(names, "\\r");
-				else
-					appendPQExpBufferChar(names, *p);
-			}
-			appendPQExpBufferStr(names, "\"\n");
-			count++;
-		}
-	}
-
-	PQclear(res);
-
-	if (count > 0)
-		pg_fatal("database, role, or tablespace names contain a newline or carriage return character, which is not supported in non-plain-text dumps:\n%s",
-				 names->data);
-
-	destroyPQExpBuffer(names);
-}
-
-
 /*
  * dumpTimestamp
  */
@@ -2397,45 +1890,3 @@ read_dumpall_filters(const char *filename, SimpleStringList *pattern)
 
 	filter_free(&fstate);
 }
-
-/*
- * parseDumpFormat
- *
-		return archCustom;
-		return archCustom;
-		return archDirectory;
-		return archDirectory;
-		return archNull;
-		return archNull;
-		return archTar;
-		return archTar;
-	return archUnknown;
- * createDumpId
- * Return the next dumpId.
-createDumpId(void)
-	return ++dumpIdVal;
index 48fdcb0fae1..84b8d410c9e 100644
@@ -41,16 +41,12 @@
 #include "postgres_fe.h"
 
 #include <ctype.h>
-#include <sys/stat.h>
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
 
@@ -58,41 +54,18 @@
 
-								 int numWorkers, bool append_data);
-static int	restore_global_objects(const char *inputFileSpec, RestoreOptions *opts);
-static int	restore_all_databases(const char *inputFileSpec,
 
 int
 main(int argc, char **argv)
 {
 	RestoreOptions *opts;
 	int			c;
+	int			exit_code;
 	int			numWorkers = 1;
+	Archive    *AH;
 	char	   *inputFileSpec;
 	bool		data_only = false;
 	bool		schema_only = false;
-	int			n_errors = 0;
-	bool		globals_only = false;
-	SimpleStringList db_exclude_patterns = {NULL, NULL};
 	static int	disable_triggers = 0;
 	static int	enable_row_security = 0;
 	static int	if_exists = 0;
@@ -107,7 +80,6 @@ main(int argc, char **argv)
 	static int	no_schema = 0;
 	static int	no_security_labels = 0;
 	static int	no_statistics = 0;
-	static int	no_globals = 0;
 	static int	no_subscriptions = 0;
 	static int	strict_names = 0;
 	static int	statistics_only = 0;
@@ -117,7 +89,6 @@ main(int argc, char **argv)
 		{"clean", 0, NULL, 'c'},
 		{"create", 0, NULL, 'C'},
 		{"data-only", 0, NULL, 'a'},
-		{"globals-only", 0, NULL, 'g'},
 		{"dbname", 1, NULL, 'd'},
 		{"exit-on-error", 0, NULL, 'e'},
 		{"exclude-schema", 1, NULL, 'N'},
@@ -165,14 +136,12 @@ main(int argc, char **argv)
 		{"no-publications", no_argument, &no_publications, 1},
 		{"no-schema", no_argument, &no_schema, 1},
 		{"no-security-labels", no_argument, &no_security_labels, 1},
-		{"no-globals", no_argument, &no_globals, 1},
 		{"no-subscriptions", no_argument, &no_subscriptions, 1},
 		{"no-statistics", no_argument, &no_statistics, 1},
 		{"statistics", no_argument, &with_statistics, 1},
 		{"statistics-only", no_argument, &statistics_only, 1},
 		{"filter", required_argument, NULL, 4},
 		{"restrict-key", required_argument, NULL, 6},
-		{"exclude-database", required_argument, NULL, 7},
 
 		{NULL, 0, NULL, 0}
 	};
@@ -201,7 +170,7 @@ main(int argc, char **argv)
 		}
 	}
 
-	while ((c = getopt_long(argc, argv, "acCd:ef:F:gh:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1",
+	while ((c = getopt_long(argc, argv, "acCd:ef:F:h:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1",
 							cmdopts, NULL)) != -1)
 	{
 		switch (c)
@@ -225,15 +194,14 @@ main(int argc, char **argv)
 				opts->filename = pg_strdup(optarg);
 				break;
 			case 'F':
-				opts->formatName = pg_strdup(optarg);
-				break;
-			case 'g':
-				/* restore only global sql commands. */
-				globals_only = true;
+				if (strlen(optarg) != 0)
+					opts->formatName = pg_strdup(optarg);
 				break;
 			case 'h':
-				opts->cparams.pghost = pg_strdup(optarg);
+				if (strlen(optarg) != 0)
+					opts->cparams.pghost = pg_strdup(optarg);
 				break;
+
 			case 'j':			/* number of restore jobs */
 				if (!option_parse_int(optarg, "-j/--jobs", 1,
 									  PG_MAX_JOBS,
@@ -262,7 +230,8 @@ main(int argc, char **argv)
 				break;
 
 			case 'p':
-				opts->cparams.pgport = pg_strdup(optarg);
+				if (strlen(optarg) != 0)
+					opts->cparams.pgport = pg_strdup(optarg);
 				break;
 			case 'R':
 				/* no-op, still accepted for backwards compatibility */
@@ -352,10 +321,6 @@ main(int argc, char **argv)
 				opts->restrict_key = pg_strdup(optarg);
 				break;
 
-			case 7:				/* database patterns to skip */
-				simple_string_list_append(&db_exclude_patterns, optarg);
-				break;
-
 			default:
 				/* getopt_long already emitted a complaint */
 				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
@@ -382,20 +347,23 @@ main(int argc, char **argv)
 	if (!opts->cparams.dbname && !opts->filename && !opts->tocSummary)
 		pg_fatal("one of -d/--dbname and -f/--file must be specified");
 
-	/* --exclude-database and --globals-only are incompatible */
-	check_mut_excl_opts(db_exclude_patterns.head, "--exclude-database",
-						globals_only, "-g/--globals-only");
-
 	/* Should get at most one of -d and -f, else user is confused */
-	check_mut_excl_opts(opts->cparams.dbname, "-d/--dbname",
-						opts->filename, "-f/--file");
-
-	/* --dbname and --restrict-key are incompatible */
-	check_mut_excl_opts(opts->cparams.dbname, "-d/--dbname",
-						opts->restrict_key, "--restrict-key");
-
 	if (opts->cparams.dbname)
+	{
+		if (opts->filename)
+		{
+			pg_log_error("options %s and %s cannot be used together",
+						 "-d/--dbname", "-f/--file");
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+			exit_nicely(1);
+		}
+
+		if (opts->restrict_key)
+			pg_fatal("options %s and %s cannot be used together",
+					 "-d/--dbname", "--restrict-key");
+
 		opts->useDB = 1;
+	}
 	else
 	{
 		/*
@@ -409,54 +377,56 @@ main(int argc, char **argv)
 			pg_fatal("invalid restrict key");
 	}
 
-	/* *-only options are incompatible with each other */
-	check_mut_excl_opts(data_only, "-a/--data-only",
-						globals_only, "-g/--globals-only",
-						schema_only, "-s/--schema-only",
-						statistics_only, "--statistics-only");
+	/* reject conflicting "-only" options */
+	if (data_only && schema_only)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-s/--schema-only", "-a/--data-only");
+	if (schema_only && statistics_only)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-s/--schema-only", "--statistics-only");
+	if (data_only && statistics_only)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-a/--data-only", "--statistics-only");
 
-	/* --no-* and *-only for same thing are incompatible */
-	check_mut_excl_opts(data_only, "-a/--data-only",
-						no_data, "--no-data");
-	check_mut_excl_opts(globals_only, "-g/--globals-only",
-						no_globals, "--no-globals");
-	check_mut_excl_opts(schema_only, "-s/--schema-only",
-						no_schema, "--no-schema");
-	check_mut_excl_opts(statistics_only, "--statistics-only",
-						no_statistics, "--no-statistics");
+	/* reject conflicting "-only" and "no-" options */
+	if (data_only && no_data)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-a/--data-only", "--no-data");
+	if (schema_only && no_schema)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-s/--schema-only", "--no-schema");
+	if (statistics_only && no_statistics)
+		pg_fatal("options %s and %s cannot be used together",
+				 "--statistics-only", "--no-statistics");
 
-	/* --statistics and --no-statistics are incompatible */
-	check_mut_excl_opts(with_statistics, "--statistics",
-						no_statistics, "--no-statistics");
+	/* reject conflicting "no-" options */
+	if (with_statistics && no_statistics)
+		pg_fatal("options %s and %s cannot be used together",
+				 "--statistics", "--no-statistics");
 
-	/* --statistics is incompatible with *-only (except --statistics-only) */
-	check_mut_excl_opts(with_statistics, "--statistics",
-						data_only, "-a/--data-only",
-						globals_only, "-g/--globals-only",
-						schema_only, "-s/--schema-only");
+	/* reject conflicting "only-" options */
+	if (data_only && with_statistics)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-a/--data-only", "--statistics");
+	if (schema_only && with_statistics)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-s/--schema-only", "--statistics");
 
-	/* --clean and --data-only are incompatible */
-	check_mut_excl_opts(opts->dropSchema, "-c/--clean",
-						data_only, "-a/--data-only");
+	if (data_only && opts->dropSchema)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-c/--clean", "-a/--data-only");
 
-	/*
-	 * --globals-only, --single-transaction, and --transaction-size are
-	 * incompatible.
-	 */
-	check_mut_excl_opts(globals_only, "-g/--globals-only",
-						opts->single_txn, "-1/--single-transaction",
-						opts->txn_size, "--transaction-size");
-
-	/* --exit-on-error and --globals-only are incompatible */
-	check_mut_excl_opts(opts->exit_on_error, "--exit-on-error",
-						globals_only, "-g/--globals-only");
+	if (opts->single_txn && opts->txn_size > 0)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-1/--single-transaction", "--transaction-size");
 
 	/*
 	 * -C is not compatible with -1, because we can't create a database inside
 	 * a transaction block.
 	 */
-	check_mut_excl_opts(opts->createDB, "-C/--create",
-						opts->single_txn, "-1/--single-transaction");
+	if (opts->createDB && opts->single_txn)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-C/--create", "-1/--single-transaction");
 
 	/* Can't do single-txn mode with multiple connections */
 	if (opts->single_txn && numWorkers > 1)
@@ -515,187 +485,6 @@ main(int argc, char **argv)
 					 opts->formatName);
 	}
 
-	/*
-	 * If toc.glo file is present, then restore all the databases from
-	 * map.dat, but skip restoring those matching --exclude-database patterns.
-	 */
-	if (inputFileSpec != NULL &&
-		(file_exists_in_directory(inputFileSpec, "toc.glo")))
-	{
-		char		global_path[MAXPGPATH];
-		RestoreOptions *tmpopts = pg_malloc0_object(RestoreOptions);
-
-		opts->format = archUnknown;
-
-		memcpy(tmpopts, opts, sizeof(RestoreOptions));
-
-		/*
-		 * Can only use --list or --use-list options with a single database
-		 * dump.
-		 */
-		if (opts->tocSummary)
-			pg_fatal("option %s cannot be used when restoring an archive created by pg_dumpall",
-					 "-l/--list");
-		if (opts->tocFile)
-			pg_fatal("option %s cannot be used when restoring an archive created by pg_dumpall",
-					 "-L/--use-list");
-
-		if (opts->strict_names)
-			pg_fatal("option %s cannot be used when restoring an archive created by pg_dumpall",
-					 "--strict-names");
-		if (globals_only && opts->dropSchema)
-			pg_fatal("options %s and %s cannot be used together when restoring an archive created by pg_dumpall",
-					 "--clean", "-g/--globals-only");
-
-		/*
-		 * For pg_dumpall archives, --clean implies --if-exists since global
-		 * objects may not exist in the target cluster.
-		 */
-		if (opts->dropSchema && !opts->if_exists)
-		{
-			opts->if_exists = 1;
-			pg_log_info("--if-exists is implied by --clean for pg_dumpall archives");
-		}
-
-		if (no_schema)
-			pg_fatal("option %s cannot be used when restoring an archive created by pg_dumpall",
-					 "--no-schema");
-
-		if (data_only)
-			pg_fatal("option %s cannot be used when restoring an archive created by pg_dumpall",
-					 "-a/--data-only");
-
-		if (statistics_only)
-			pg_fatal("option %s cannot be used when restoring an archive created by pg_dumpall",
-					 "--statistics-only");
-
-		if (!(opts->dumpSections & DUMP_PRE_DATA))
-			pg_fatal("option %s cannot exclude %s when restoring a pg_dumpall archive",
-					 "--section", "--pre-data");
-
-		/*
-		 * To restore from a pg_dumpall archive, -C (create database) option
-		 * must be specified unless we are only restoring globals or we are
-		 * skipping globals.
-		 */
-		if (!no_globals && !globals_only && opts->createDB != 1)
-		{
-			pg_log_error("option %s must be specified when restoring an archive created by pg_dumpall",
-						 "-C/--create");
-			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
-			pg_log_error_hint("Individual databases can be restored using their specific archives.");
-			exit_nicely(1);
-		}
-
-		/*
-		 * Restore global objects, even if --exclude-database results in zero
-		 * databases to process. If 'globals-only' is set, exit immediately.
-		 */
-		snprintf(global_path, MAXPGPATH, "%s/toc.glo", inputFileSpec);
-
-		if (!no_globals)
-			n_errors = restore_global_objects(global_path, tmpopts);
-		else
-			pg_log_info("skipping restore of global objects because %s was specified",
-						"--no-globals");
-
-		if (globals_only)
-			pg_log_info("database restoring skipped because option %s was specified",
-						"-g/--globals-only");
-		else
-		{
-			/* Now restore all the databases from map.dat */
-			n_errors = n_errors + restore_all_databases(inputFileSpec, db_exclude_patterns,
-														opts, numWorkers);
-		}
-
-		/* Free db pattern list. */
-		simple_string_list_destroy(&db_exclude_patterns);
-	}
-	else
-	{
-		if (db_exclude_patterns.head != NULL)
-		{
-			simple_string_list_destroy(&db_exclude_patterns);
-			pg_fatal("option %s can be used only when restoring an archive created by pg_dumpall",
-					 "--exclude-database");
-		}
-
-		if (globals_only)
-			pg_fatal("option %s can be used only when restoring an archive created by pg_dumpall",
-					 "-g/--globals-only");
-
-		/* Process if toc.glo file does not exist. */
-		n_errors = restore_one_database(inputFileSpec, opts, numWorkers, false);
-	}
-
-	/* Done, print a summary of ignored errors during restore. */
-	if (n_errors)
-	{
-		pg_log_warning("errors ignored on restore: %d", n_errors);
-		return 1;
-	}
-
-	return 0;
-}
-
-/*
- * restore_global_objects
- *
- * This restore all global objects.
- */
-static int
-restore_global_objects(const char *inputFileSpec, RestoreOptions *opts)
-{
-	Archive    *AH;
-	int			nerror = 0;
-
-	/* Set format as custom so that toc.glo file can be read. */
-	opts->format = archCustom;
-	opts->txn_size = 0;
-
-	AH = OpenArchive(inputFileSpec, opts->format);
-
-	SetArchiveOptions(AH, NULL, opts);
-
-	on_exit_close_archive(AH);
-
-	/* Let the archiver know how noisy to be */
-	AH->verbose = opts->verbose;
-
-	/* Don't output TOC entry comments when restoring globals */
-	((ArchiveHandle *) AH)->noTocComments = 1;
-
-	AH->exit_on_error = false;
-
-	/* Parallel execution is not supported for global object restoration. */
-	AH->numWorkers = 1;
-
-	ProcessArchiveRestoreOptions(AH);
-	RestoreArchive(AH, false);
-
-	nerror = AH->n_errors;
-
-	/* AH may be freed in CloseArchive? */
-	CloseArchive(AH);
-
-	return nerror;
-}
-
-/*
- * restore_one_database
- *
- * This will restore one database using toc.dat file.
- *
- * returns the number of errors while doing restore.
- */
-static int
-restore_one_database(const char *inputFileSpec, RestoreOptions *opts,
-					 int numWorkers, bool append_data)
-{
-	Archive    *AH;
-	int			n_errors;
-
 	AH = OpenArchive(inputFileSpec, opts->format);
 
@@ -703,15 +492,9 @@ restore_one_database(const char *inputFileSpec, RestoreOptions *opts,
-	 * restoring multiple databases, then only update AH handle for cleanup as
-	if (!append_data)
 
@@ -731,21 +514,25 @@ restore_one_database(const char *inputFileSpec, RestoreOptions *opts,

 
-	return n_errors;
+	return exit_code;
 }
 
 static void
 
@@ -763,7 +550,6 @@ usage(const char *progname)
 	printf(_("  -c, --clean                  clean (drop) database objects before recreating\n"));
 	printf(_("  -C, --create                 create the target database\n"));
 	printf(_("  -e, --exit-on-error          exit on error, default is to continue\n"));
-	printf(_("  -g, --globals-only           restore only global objects, no databases\n"));
 	printf(_("  -I, --index=NAME             restore named index\n"));
 	printf(_("  -j, --jobs=NUM               use this many parallel jobs to restore\n"));
 	printf(_("  -L, --use-list=FILENAME      use table of contents from this file for\n"
@@ -780,7 +566,6 @@ usage(const char *progname)
 	printf(_("  -1, --single-transaction     restore as a single transaction\n"));
 	printf(_("  --disable-triggers           disable triggers during data-only restore\n"));
 	printf(_("  --enable-row-security        enable row security\n"));
-	printf(_("  --exclude-database=PATTERN   do not restore the specified database(s)\n"));
 	printf(_("  --filter=FILENAME            restore or skip objects based on expressions\n"
 			 "                               in FILENAME\n"));
 	printf(_("  --if-exists                  use IF EXISTS when dropping objects\n"));
@@ -788,7 +573,6 @@ usage(const char *progname)
 	printf(_("  --no-data                    do not restore data\n"));
 	printf(_("  --no-data-for-failed-tables  do not restore data of tables that could not be\n"
 			 "                               created\n"));
-	printf(_("  --no-globals                 do not restore global objects (roles and tablespaces)\n"));
 	printf(_("  --no-policies                do not restore row security policies\n"));
 	printf(_("  --no-publications            do not restore publications\n"));
 	printf(_("  --no-schema                  do not restore schema\n"));
@@ -817,8 +601,8 @@ usage(const char *progname)
 	printf(_("  --role=ROLENAME          do SET ROLE before restore\n"));
 
 	printf(_("\n"
@@ -923,409 +707,3 @@ read_restore_filters(const char *filename, RestoreOptions *opts)
 
 	filter_free(&fstate);
 }
-
-/*
- * file_exists_in_directory
- *
- * Returns true if the file exists in the given directory.
- */
-static bool
-file_exists_in_directory(const char *dir, const char *filename)
-{
-	struct stat st;
-	char		buf[MAXPGPATH];
-
-	if (snprintf(buf, MAXPGPATH, "%s/%s", dir, filename) >= MAXPGPATH)
-		pg_fatal("directory name too long: \"%s\"", dir);
-
-	return (stat(buf, &st) == 0 && S_ISREG(st.st_mode));
-}
-
-/*
- * get_dbnames_list_to_restore
- *
- * This will mark for skipping any entries from dbname_oid_list that pattern match an
- * entry in the db_exclude_patterns list.
- *
- * Returns the number of database to be restored.
- *
- */
-static int
-get_dbnames_list_to_restore(PGconn *conn,
-							SimplePtrList *dbname_oid_list,
-							SimpleStringList db_exclude_patterns)
-{
-	int			count_db = 0;
-	PQExpBuffer query;
-	PQExpBuffer db_lit;
-	PGresult   *res;
-
-	query = createPQExpBuffer();
-	db_lit = createPQExpBuffer();
-
-	/*
-	 * Process one by one all dbnames and if specified to skip restoring, then
-	 * remove dbname from list.
-	 */
-	for (SimplePtrListCell *db_cell = dbname_oid_list->head;
-		 db_cell; db_cell = db_cell->next)
-	{
-		DbOidName  *dbidname = (DbOidName *) db_cell->ptr;
-		bool		skip_db_restore = false;
-
-		resetPQExpBuffer(query);
-		resetPQExpBuffer(db_lit);
-
-		appendStringLiteralConn(db_lit, dbidname->str, conn);
-
-		for (SimpleStringListCell *pat_cell = db_exclude_patterns.head; pat_cell; pat_cell = pat_cell->next)
-		{
-			/*
-			 * If there is an exact match then we don't need to try a pattern
-			 * match
-			 */
-			if (pg_strcasecmp(dbidname->str, pat_cell->val) == 0)
-				skip_db_restore = true;
-			/* Otherwise, try a pattern match if there is a connection */
-			else
-			{
-				int			dotcnt;
-
-				appendPQExpBufferStr(query, "SELECT 1 ");
-				processSQLNamePattern(conn, query, pat_cell->val, false,
-									  false, NULL, db_lit->data,
-									  NULL, NULL, NULL, &dotcnt);
-
-				if (dotcnt > 0)
-				{
-					pg_log_error("improper qualified name (too many dotted names): %s",
-								 dbidname->str);
-					PQfinish(conn);
-					exit_nicely(1);
-				}
-
-				res = executeQuery(conn, query->data);
-
-				if (PQntuples(res))
-				{
-					skip_db_restore = true;
-					pg_log_info("database name \"%s\" matches --exclude-database pattern \"%s\"", dbidname->str, pat_cell->val);
-				}
-
-				PQclear(res);
-				resetPQExpBuffer(query);
-			}
-
-			if (skip_db_restore)
-				break;
-		}
-
-		/*
-		 * Mark db to be skipped or increment the counter of dbs to be
-		 * restored
-		 */
-		if (skip_db_restore)
-		{
-			pg_log_info("excluding database \"%s\"", dbidname->str);
-			dbidname->oid = InvalidOid;
-		}
-		else
-			count_db++;
-	}
-
-	destroyPQExpBuffer(query);
-	destroyPQExpBuffer(db_lit);
-
-	return count_db;
-}
-
-/*
- * get_dbname_oid_list_from_mfile
- *
- * Open map.dat file and read line by line and then prepare a list of database
- * names and corresponding db_oid.
- *
- * Returns, total number of database names in map.dat file.
- */
-static int
-get_dbname_oid_list_from_mfile(const char *dumpdirpath,
-							   SimplePtrList *dbname_oid_list)
-{
-	StringInfoData linebuf;
-	FILE	   *pfile;
-	char		map_file_path[MAXPGPATH];
-	int			count = 0;
-
-	/*
-	 * If there is no map.dat file in the dump, then return from here as there
-	 * is no database to restore.
-	 */
-	if (!file_exists_in_directory(dumpdirpath, "map.dat"))
-	{
-		pg_log_info("database restoring is skipped because file \"%s\" does not exist in directory \"%s\"", "map.dat", dumpdirpath);
-		return 0;
-	}
-
-	snprintf(map_file_path, MAXPGPATH, "%s/map.dat", dumpdirpath);
-
-	/* Open map.dat file. */
-	pfile = fopen(map_file_path, PG_BINARY_R);
-
-	if (pfile == NULL)
-		pg_fatal("could not open file \"%s\": %m", map_file_path);
-
-	initStringInfo(&linebuf);
-
-	/* Append all the dbname/db_oid combinations to the list. */
-	while (pg_get_line_buf(pfile, &linebuf))
-	{
-		Oid			db_oid = InvalidOid;
-		char	   *dbname;
-		DbOidName  *dbidname;
-		int			namelen;
-		char	   *p = linebuf.data;
-
-		/* look for the dboid. */
-		while (isdigit((unsigned char) *p))
-			p++;
-
-		/* ignore lines that don't begin with a digit */
-		if (p == linebuf.data)
-			continue;
-
-		if (*p == ' ')
-		{
-			sscanf(linebuf.data, "%u", &db_oid);
-			p++;
-		}
-
-		/* dbname is the rest of the line */
-		dbname = p;
-		namelen = strlen(dbname);
-
-		/* Strip trailing newline */
-		if (namelen > 0 && dbname[namelen - 1] == '\n')
-			dbname[--namelen] = '\0';
-
-		/* Report error and exit if the file has any corrupted data. */
-		if (!OidIsValid(db_oid) || namelen < 1)
-			pg_fatal("invalid entry in file \"%s\" on line %d", map_file_path,
-					 count + 1);
-
-		dbidname = pg_malloc(offsetof(DbOidName, str) + namelen + 1);
-		dbidname->oid = db_oid;
-		strlcpy(dbidname->str, dbname, namelen + 1);
-
-		pg_log_info("found database \"%s\" (OID: %u) in file \"%s\"",
-					dbidname->str, db_oid, map_file_path);
-
-		simple_ptr_list_append(dbname_oid_list, dbidname);
-		count++;
-	}
-
-	/* Close map.dat file. */
-	fclose(pfile);
-
-	pfree(linebuf.data);
-
-	return count;
-}
-
-/*
- * restore_all_databases
- *
- * This will restore databases those dumps are present in
- * directory based on map.dat file mapping.
- *
- * This will skip restoring for databases that are specified with
- * exclude-database option.
- *
- * returns, number of errors while doing restore.
- */
-static int
-restore_all_databases(const char *inputFileSpec,
-					  SimpleStringList db_exclude_patterns, RestoreOptions *opts,
-					  int numWorkers)
-{
-	SimplePtrList dbname_oid_list = {NULL, NULL};
-	int			num_db_restore = 0;
-	int			num_total_db;
-	int			n_errors_total = 0;
-	char	   *connected_db = NULL;
-	PGconn	   *conn = NULL;
-	RestoreOptions *original_opts = pg_malloc0_object(RestoreOptions);
-	RestoreOptions *tmpopts = pg_malloc0_object(RestoreOptions);
-
-	memcpy(original_opts, opts, sizeof(RestoreOptions));
-
-	/* Save db name to reuse it for all the database. */
-	if (opts->cparams.dbname)
-		connected_db = opts->cparams.dbname;
-
-	num_total_db = get_dbname_oid_list_from_mfile(inputFileSpec, &dbname_oid_list);
-
-	pg_log_info(ngettext("found %d database name in \"%s\"",
-						 "found %d database names in \"%s\"",
-						 num_total_db),
-				num_total_db, "map.dat");
-
-	/*
-	 * If exclude-patterns is given, connect to the database to process them.
-	 */
-	if (db_exclude_patterns.head != NULL)
-	{
-		if (opts->cparams.dbname)
-		{
-			conn = ConnectDatabase(opts->cparams.dbname, NULL, opts->cparams.pghost,
-								   opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT,
-								   false, progname, NULL, NULL, NULL, NULL);
-
-			if (!conn)
-				pg_fatal("could not connect to database \"%s\"", opts->cparams.dbname);
-		}
-
-		if (!conn)
-		{
-			pg_log_info("trying to connect to database \"%s\"", "postgres");
-
-			conn = ConnectDatabase("postgres", NULL, opts->cparams.pghost,
-								   opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT,
-								   false, progname, NULL, NULL, NULL, NULL);
-
-			/* Try with template1. */
-			if (!conn)
-			{
-				pg_log_info("trying to connect to database \"%s\"", "template1");
-
-				conn = ConnectDatabase("template1", NULL, opts->cparams.pghost,
-									   opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT,
-									   false, progname, NULL, NULL, NULL, NULL);
-				if (!conn)
-				{
-					pg_log_error("could not connect to databases \"postgres\" or \"template1\"\n"
-								 "Please specify an alternative database.");
-					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
-					exit_nicely(1);
-				}
-			}
-		}
-
-		/* Filter the db list according to the exclude patterns. */
-		num_db_restore = get_dbnames_list_to_restore(conn, &dbname_oid_list,
-													 db_exclude_patterns);
-		PQfinish(conn);
-	}
-	else
-		num_db_restore = num_total_db;
-
-	/* Exit if no db needs to be restored. */
-	if (num_db_restore == 0)
-	{
-		pg_log_info(ngettext("no database needs restoring out of %d database",
-							 "no database needs restoring out of %d databases", num_total_db),
-					num_total_db);
-		pg_free(original_opts);
-		pg_free(tmpopts);
-		return 0;
-	}
-
-	pg_log_info("need to restore %d databases out of %d databases", num_db_restore, num_total_db);
-
-	/*
-	 * We have a list of databases to restore after processing the
-	 * exclude-database switch(es).  Now we can restore them one by one.
-	 */
-	for (SimplePtrListCell *db_cell = dbname_oid_list.head;
-		 db_cell; db_cell = db_cell->next)
-	{
-		DbOidName  *dbidname = (DbOidName *) db_cell->ptr;
-		char		subdirpath[MAXPGPATH];
-		char		subdirdbpath[MAXPGPATH];
-		char		dbfilename[MAXPGPATH];
-		int			n_errors;
-
-		/* ignore dbs marked for skipping */
-		if (dbidname->oid == InvalidOid)
-			continue;
-
-		/*
-		 * Since pg_backup_archiver.c may modify RestoreOptions during the
-		 * previous restore, we must provide a fresh copy of the original
-		 * "opts" for each call to restore_one_database.
-		 */
-		memcpy(tmpopts, original_opts, sizeof(RestoreOptions));
-
-		snprintf(subdirdbpath, MAXPGPATH, "%s/databases", inputFileSpec);
-
-		/*
-		 * Look for the database dump file/dir. If there is an {oid}.tar or
-		 * {oid}.dmp file, use it. Otherwise try to use a directory called
-		 * {oid}
-		 */
-		snprintf(dbfilename, MAXPGPATH, "%u.tar", dbidname->oid);
-		if (file_exists_in_directory(subdirdbpath, dbfilename))
-			snprintf(subdirpath, MAXPGPATH, "%s/databases/%u.tar", inputFileSpec, dbidname->oid);
-		else
-		{
-			snprintf(dbfilename, MAXPGPATH, "%u.dmp", dbidname->oid);
-
-			if (file_exists_in_directory(subdirdbpath, dbfilename))
-				snprintf(subdirpath, MAXPGPATH, "%s/databases/%u.dmp", inputFileSpec, dbidname->oid);
-			else
-				snprintf(subdirpath, MAXPGPATH, "%s/databases/%u", inputFileSpec, dbidname->oid);
-		}
-
-		pg_log_info("restoring database \"%s\"", dbidname->str);
-
-		/* If database is already created, then don't set createDB flag. */
-		if (tmpopts->cparams.dbname)
-		{
-			PGconn	   *test_conn;
-
-			test_conn = ConnectDatabase(dbidname->str, NULL, tmpopts->cparams.pghost,
-										tmpopts->cparams.pgport, tmpopts->cparams.username, TRI_DEFAULT,
-										false, progname, NULL, NULL, NULL, NULL);
-			if (test_conn)
-			{
-				PQfinish(test_conn);
-
-				/* Use already created database for connection. */
-				tmpopts->createDB = 0;
-				tmpopts->cparams.dbname = dbidname->str;
-			}
-			else
-			{
-				if (!tmpopts->createDB)
-				{
-					pg_log_info("skipping restore of database \"%s\": database does not exist and %s was not specified",
-								dbidname->str, "-C/--create");
-					continue;
-				}
-
-				/* We'll have to create it */
-				tmpopts->createDB = 1;
-				tmpopts->cparams.dbname = connected_db;
-			}
-		}
-
-		/* Restore the single database. */
-		n_errors = restore_one_database(subdirpath, tmpopts, numWorkers, true);
-
-		n_errors_total += n_errors;
-
-		/* Print a summary of ignored errors during single database restore. */
-		if (n_errors)
-			pg_log_warning("errors ignored on database \"%s\" restore: %d", dbidname->str, n_errors);
-	}
-
-	/* Log number of processed databases. */
-	pg_log_info("number of restored databases is %d", num_db_restore);
-
-	/* Free dbname and dboid list. */
-	simple_ptr_list_destroy(&dbname_oid_list);
-
-	pg_free(original_opts);
-	pg_free(tmpopts);
-
-	return n_errors_total;
-}
diff --git a/src/bin/pg_dump/t/001_basic.pl b/src/bin/pg_dump/t/001_basic.pl
index 687e842cde9..b2558046224 100644
--- a/src/bin/pg_dump/t/001_basic.pl
+++ b/src/bin/pg_dump/t/001_basic.pl
@@ -87,8 +87,8 @@ command_fails_like(
 
 command_fails_like(
 	[ 'pg_restore', '-s', '-a', '-f -' ],
-	qr/\Qpg_restore: error: options -a\/--data-only and -s\/--schema-only cannot be used together\E/,
-	'pg_restore: options -a/--data-only and -s/--schema-only cannot be used together'
+	qr/\Qpg_restore: error: options -s\/--schema-only and -a\/--data-only cannot be used together\E/,
+	'pg_restore: options -s/--schema-only and -a/--data-only cannot be used together'
 );
 
 command_fails_like(
@@ -205,12 +205,7 @@ command_fails_like(
 command_fails_like(
 	[ 'pg_restore', '-f -', '-F', 'garbage' ],
 	qr/\Qpg_restore: error: unrecognized archive format "garbage";\E/,
-	'pg_restore: unrecognized archive format');
-
-command_fails_like(
-	[ 'pg_restore', '-f -', '-F', '' ],
-	qr/\Qpg_restore: error: unrecognized archive format "";\E/,
-	'pg_restore: empty archive format');
+	'pg_dump: unrecognized archive format');
 
 command_fails_like(
 	[ 'pg_dump', '--on-conflict-do-nothing' ],
@@ -251,105 +246,8 @@ command_fails_like(
 # also fails for -r and -t, but it seems pointless to add more tests for those.
 command_fails_like(
 	[ 'pg_dumpall', '--exclude-database=foo', '--globals-only' ],
-	qr/\Qpg_dumpall: error: options --exclude-database and -g\/--globals-only cannot be used together\E/,
-	'pg_dumpall: options --exclude-database and -g/--globals-only cannot be used together'
+	qr/\Qpg_dumpall: error: option --exclude-database cannot be used together with -g\/--globals-only\E/,
+	'pg_dumpall: option --exclude-database cannot be used together with -g/--globals-only'
 );
 
-command_fails_like(
-	[ 'pg_dumpall', '-a', '--no-data' ],
-	qr/\Qpg_dumpall: error: options -a\/--data-only and --no-data cannot be used together\E/,
-	'pg_dumpall: options -a\/--data-only and --no-data cannot be used together'
-);
-
-command_fails_like(
-	[ 'pg_dumpall', '-s', '--no-schema' ],
-	qr/\Qpg_dumpall: error: options -s\/--schema-only and --no-schema cannot be used together\E/,
-	'pg_dumpall: options -s\/--schema-only and --no-schema cannot be used together'
-);
-
-command_fails_like(
-	[ 'pg_dumpall', '--statistics-only', '--no-statistics' ],
-	qr/\Qpg_dumpall: error: options --statistics-only and --no-statistics cannot be used together\E/,
-	'pg_dumpall: options --statistics-only and --no-statistics cannot be used together'
-);
-
-command_fails_like(
-	[ 'pg_dumpall', '--statistics', '--no-statistics' ],
-	qr/\Qpg_dumpall: error: options --statistics and --no-statistics cannot be used together\E/,
-	'pg_dumpall: options --statistics-only and --no-statistics cannot be used together'
-);
-
-command_fails_like(
-	[ 'pg_dumpall', '--statistics', '--tablespaces-only' ],
-	qr/\Qpg_dumpall: error: options --statistics and -t\/--tablespaces-only cannot be used together\E/,
-	'pg_dumpall: options --statistics and -t\/--tablespaces-only cannot be used together'
-);
-
-command_fails_like(
-	[ 'pg_dumpall', '--format', 'x' ],
-	qr/\Qpg_dumpall: error: unrecognized output format "x";\E/,
-	'pg_dumpall: unrecognized output format');
-
-command_fails_like(
-	[ 'pg_dumpall', '--format', 'd', '--restrict-key=uu', '-f dumpfile' ],
-	qr/\Qpg_dumpall: error: option --restrict-key can only be used with --format=plain\E/,
-	'pg_dumpall: --restrict-key can only be used with plain dump format');
-
-command_fails_like(
-	[
-		'pg_dumpall', '--format', 'd', '--globals-only',
-		'--clean', '-f', 'dumpfile'
-	],
-	qr/\Qpg_dumpall: error: options --clean and -g\/--globals-only cannot be used together in non-text dump\E/,
-	'pg_dumpall: --clean and -g/--globals-only cannot be used together in non-text dump'
-);
-
-command_fails_like(
-	[ 'pg_dumpall', '--format', 'd' ],
-	qr/\Qpg_dumpall: error: option -F\/--format=d|c|t requires option -f\/--file\E/,
-	'pg_dumpall: non-plain format requires --file option');
-
-command_fails_like(
-	[ 'pg_restore', '--exclude-database=foo', '--globals-only', '-d', 'xxx' ],
-	qr/\Qpg_restore: error: options --exclude-database and -g\/--globals-only cannot be used together\E/,
-	'pg_restore: options --exclude-database and -g/--globals-only cannot be used together'
-);
-
-command_fails_like(
-	[ 'pg_restore', '--data-only', '--globals-only', '-d', 'xxx' ],
-	qr/\Qpg_restore: error: options -a\/--data-only and -g\/--globals-only cannot be used together\E/,
-	'pg_restore: error: options -a/--data-only and -g/--globals-only cannot be used together'
-);
-
-command_fails_like(
-	[ 'pg_restore', '--schema-only', '--globals-only', '-d', 'xxx' ],
-	qr/\Qpg_restore: error: options -g\/--globals-only and -s\/--schema-only cannot be used together\E/,
-	'pg_restore: error: options -g/--globals-only and -s/--schema-only cannot be used together'
-);
-
-command_fails_like(
-	[ 'pg_restore', '--statistics-only', '--globals-only', '-d', 'xxx' ],
-	qr/\Qpg_restore: error: options -g\/--globals-only and --statistics-only cannot be used together\E/,
-	'pg_restore: error: options -g/--globals-only and --statistics-only cannot be used together'
-);
-
-command_fails_like(
-	[ 'pg_restore', '--exclude-database=foo', '-d', 'xxx', 'dumpdir' ],
-	qr/\Qpg_restore: error: option --exclude-database can be used only when restoring an archive created by pg_dumpall\E/,
-	'When option --exclude-database is used in pg_restore with dump of pg_dump'
-);
-
-command_fails_like(
-	[ 'pg_restore', '--globals-only', '-d', 'xxx', 'dumpdir' ],
-	qr/\Qpg_restore: error: option -g\/--globals-only can be used only when restoring an archive created by pg_dumpall\E/,
-	'When option --globals-only is used in pg_restore with the dump of pg_dump'
-);
-
-command_fails_like(
-	[
-		'pg_restore', '--globals-only', '--no-globals', '-d', 'xxx',
-		'dumpdir'
-	],
-	qr/\Qpg_restore: error: options -g\/--globals-only and --no-globals cannot be used together\E/,
-	'options --no-globals and --globals-only cannot be used together');
 done_testing();
diff --git a/src/bin/pg_dump/t/005_pg_dump_filterfile.pl b/src/bin/pg_dump/t/005_pg_dump_filterfile.pl
index cecf0442088..72a7c90c6af 100644
--- a/src/bin/pg_dump/t/005_pg_dump_filterfile.pl
+++ b/src/bin/pg_dump/t/005_pg_dump_filterfile.pl
@@ -570,8 +570,8 @@ command_fails_like(
 		'--filter' => "$tempdir/inputfile.txt",
 		'--globals-only'
 	],
-	qr/\Qpg_dumpall: error: options --exclude-database and -g\/--globals-only cannot be used together\E/,
-	'pg_dumpall: options --exclude-database and -g/--globals-only cannot be used together'
+	qr/\Qpg_dumpall: error: option --exclude-database cannot be used together with -g\/--globals-only\E/,
+	'pg_dumpall: option --exclude-database cannot be used together with -g/--globals-only'
 );
 
 # Test invalid filter command
diff --git a/src/bin/pg_dump/t/007_pg_dumpall.pl b/src/bin/pg_dump/t/007_pg_dumpall.pl
deleted file mode 100644
index 22f11a13a9a..00000000000
--- a/src/bin/pg_dump/t/007_pg_dumpall.pl
+++ /dev/null
@@ -1,661 +0,0 @@
-# Copyright (c) 2021-2026, PostgreSQL Global Development Group
-
-use strict;
-use warnings FATAL => 'all';
-
-use PostgreSQL::Test::Cluster;
-use PostgreSQL::Test::Utils;
-use Test::More;
-
-my $tempdir = PostgreSQL::Test::Utils::tempdir;
-my $run_db = 'postgres';
-my $sep = $windows_os ? "\\" : "/";
-
-# Tablespace locations used by "restore_tablespace" test case.
-my $tablespace1 = "${tempdir}${sep}tbl1";
-my $tablespace2 = "${tempdir}${sep}tbl2";
-mkdir($tablespace1) || die "mkdir $tablespace1 $!";
-mkdir($tablespace2) || die "mkdir $tablespace2 $!";
-
-# escape tablespace locations on Windows.
-my $tablespace2_orig = $tablespace2;
-$tablespace1 = $windows_os ? ($tablespace1 =~ s/\\/\\\\/gr) : $tablespace1;
-$tablespace2 = $windows_os ? ($tablespace2 =~ s/\\/\\\\/gr) : $tablespace2;
-
-# Where pg_dumpall will be executed.
-my $node = PostgreSQL::Test::Cluster->new('node');
-$node->init;
-$node->start;
-
-
-###############################################################
-# Definition of the pg_dumpall test cases to run.
-#
-# Each of these test cases are named and those names are used for fail
-# reporting and also to save the dump and restore information needed for the
-# test to assert.
-#
-# The "setup_sql" is a psql valid script that contains SQL commands to execute
-# before of actually execute the tests. The setups are all executed before of
-# any test execution.
-#
-# The "dump_cmd" and "restore_cmd" are the commands that will be executed. The
-# "restore_cmd" must have the --file flag to save the restore output so that we
-# can assert on it.
-#
-# The "like" and "unlike" is a regexp that is used to match the pg_restore
-# output. It must have at least one of then filled per test cases but it also
-# can have both. See "excluding_databases" test case for example.
-my %pgdumpall_runs = (
-	restore_roles => {
-		setup_sql => '
-		CREATE ROLE dumpall WITH ENCRYPTED PASSWORD \'admin\' SUPERUSER;
-		CREATE ROLE dumpall2 WITH REPLICATION CONNECTION LIMIT 10;',
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_roles",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_roles.sql",
-			"$tempdir/restore_roles",
-		],
-		like => qr/
-			\s*\QCREATE ROLE dumpall2;\E
-			\s*\QALTER ROLE dumpall2 WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN REPLICATION NOBYPASSRLS CONNECTION LIMIT 10;\E
-		/xm
-	},
-
-	restore_tablespace => {
-		setup_sql => "
-		CREATE ROLE tap;
-		CREATE TABLESPACE tbl1 OWNER tap LOCATION '$tablespace1';
-		CREATE TABLESPACE tbl2 OWNER tap LOCATION '$tablespace2' WITH (seq_page_cost=1.0);",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_tablespace",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_tablespace.sql",
-			"$tempdir/restore_tablespace",
-		],
-		# Match "E" as optional since it is added on LOCATION when running on
-		# Windows.
-		like => qr/^
-			\n\QCREATE TABLESPACE tbl2 OWNER tap LOCATION \E(?:E)?\Q'$tablespace2_orig';\E
-			\n\QALTER TABLESPACE tbl2 SET (seq_page_cost=1.0);\E
-		/xm,
-	},
-
-	restore_grants => {
-		setup_sql => "
-		CREATE DATABASE tapgrantsdb;
-		CREATE SCHEMA private;
-		CREATE SEQUENCE serial START 101;
-		CREATE FUNCTION fn() RETURNS void AS \$\$
-		BEGIN
-		END;
-		\$\$ LANGUAGE plpgsql;
-		CREATE ROLE super;
-		CREATE ROLE grant1;
-		CREATE ROLE grant2;
-		CREATE ROLE grant3;
-		CREATE ROLE grant4;
-		CREATE ROLE grant5;
-		CREATE ROLE grant6;
-		CREATE ROLE grant7;
-		CREATE ROLE grant8;
-
-		CREATE TABLE t (id int);
-		INSERT INTO t VALUES (1), (2), (3), (4);
-
-		GRANT SELECT ON TABLE t TO grant1;
-		GRANT INSERT ON TABLE t TO grant2;
-		GRANT ALL PRIVILEGES ON TABLE t to grant3;
-		GRANT CONNECT, CREATE ON DATABASE tapgrantsdb TO grant4;
-		GRANT USAGE, CREATE ON SCHEMA private TO grant5;
-		GRANT USAGE, SELECT, UPDATE ON SEQUENCE serial TO grant6;
-		GRANT super TO grant7;
-		GRANT EXECUTE ON FUNCTION fn() TO grant8;
-		",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_grants",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_grants.sql",
-			"$tempdir/restore_grants",
-		],
-		like => qr/^
-			\n\QGRANT ALL ON SCHEMA private TO grant5;\E
-			(.*\n)*
-			\n\QGRANT ALL ON FUNCTION public.fn() TO grant8;\E
-			(.*\n)*
-			\n\QGRANT ALL ON SEQUENCE public.serial TO grant6;\E
-			(.*\n)*
-			\n\QGRANT SELECT ON TABLE public.t TO grant1;\E
-			\n\QGRANT INSERT ON TABLE public.t TO grant2;\E
-			\n\QGRANT ALL ON TABLE public.t TO grant3;\E
-			(.*\n)*
-			\n\QGRANT CREATE,CONNECT ON DATABASE tapgrantsdb TO grant4;\E
-		/xm,
-	},
-
-	excluding_databases => {
-		setup_sql => 'CREATE DATABASE db1;
-		\c db1
-		CREATE TABLE t1 (id int);
-		INSERT INTO t1 VALUES (1), (2), (3), (4);
-		CREATE TABLE t2 (id int);
-		INSERT INTO t2 VALUES (1), (2), (3), (4);
-
-		CREATE DATABASE db2;
-		\c db2
-		CREATE TABLE t3 (id int);
-		INSERT INTO t3 VALUES (1), (2), (3), (4);
-		CREATE TABLE t4 (id int);
-		INSERT INTO t4 VALUES (1), (2), (3), (4);
-
-		CREATE DATABASE dbex3;
-		\c dbex3
-		CREATE TABLE t5 (id int);
-		INSERT INTO t5 VALUES (1), (2), (3), (4);
-		CREATE TABLE t6 (id int);
-		INSERT INTO t6 VALUES (1), (2), (3), (4);
-
-		CREATE DATABASE dbex4;
-		\c dbex4
-		CREATE TABLE t7 (id int);
-		INSERT INTO t7 VALUES (1), (2), (3), (4);
-		CREATE TABLE t8 (id int);
-		INSERT INTO t8 VALUES (1), (2), (3), (4);
-
-		CREATE DATABASE db5;
-		\c db5
-		CREATE TABLE t9 (id int);
-		INSERT INTO t9 VALUES (1), (2), (3), (4);
-		CREATE TABLE t10 (id int);
-		INSERT INTO t10 VALUES (1), (2), (3), (4);
-		',
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/excluding_databases",
-			'--exclude-database' => 'dbex*',
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'directory',
-			'--file' => "$tempdir/excluding_databases.sql",
-			'--exclude-database' => 'db5',
-			"$tempdir/excluding_databases",
-		],
-		like => qr/^
-			\n\QCREATE DATABASE db1\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t1 (\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t2 (\E
-			(.*\n)*
-			\n\QCREATE DATABASE db2\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t3 (\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t4 (/xm,
-		unlike => qr/^
-			\n\QCREATE DATABASE db3\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t5 (\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t6 (\E
-			(.*\n)*
-			\n\QCREATE DATABASE db4\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t7 (\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t8 (\E
-			\n\QCREATE DATABASE db5\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t9 (\E
-			(.*\n)*
-			\n\QCREATE TABLE public.t10 (\E
-		/xm,
-	},
-
-	format_directory => {
-		setup_sql => "CREATE TABLE format_directory(a int, b boolean, c text);
-		INSERT INTO format_directory VALUES (1, true, 'name1'), (2, false, 'name2');",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/format_directory",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'directory',
-			'--file' => "$tempdir/format_directory.sql",
-			"$tempdir/format_directory",
-		],
-		like => qr/^\n\QCOPY public.format_directory (a, b, c) FROM stdin;/xm
-	},
-
-	format_tar => {
-		setup_sql => "CREATE TABLE format_tar(a int, b boolean, c text);
-		INSERT INTO format_tar VALUES (1, false, 'name3'), (2, true, 'name4');",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'tar',
-			'--file' => "$tempdir/format_tar",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'tar',
-			'--file' => "$tempdir/format_tar.sql",
-			"$tempdir/format_tar",
-		],
-		like => qr/^\n\QCOPY public.format_tar (a, b, c) FROM stdin;/xm
-	},
-
-	format_custom => {
-		setup_sql => "CREATE TABLE format_custom(a int, b boolean, c text);
-		INSERT INTO format_custom VALUES (1, false, 'name5'), (2, true, 'name6');",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'custom',
-			'--file' => "$tempdir/format_custom",
-		],
-		restore_cmd => [
-			'pg_restore', '-C',
-			'--format' => 'custom',
-			'--file' => "$tempdir/format_custom.sql",
-			"$tempdir/format_custom",
-		],
-		like => qr/^ \n\QCOPY public.format_custom (a, b, c) FROM stdin;/xm
-	},
-
-	dump_globals_only => {
-		setup_sql => "CREATE TABLE format_dir(a int, b boolean, c text);
-		INSERT INTO format_dir VALUES (1, false, 'name5'), (2, true, 'name6');",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--globals-only',
-			'--file' => "$tempdir/dump_globals_only",
-		],
-		restore_cmd => [
-			'pg_restore', '-C', '--globals-only',
-			'--format' => 'directory',
-			'--file' => "$tempdir/dump_globals_only.sql",
-			"$tempdir/dump_globals_only",
-		],
-		like => qr/
-            ^\s*\QCREATE ROLE dumpall;\E\s*\n
-			/xm
-	},
-
-	restore_no_globals => {
-		setup_sql => "CREATE TABLE no_globals_test(a int, b text);
-		INSERT INTO no_globals_test VALUES (1, 'hello'), (2, 'world');",
-		dump_cmd => [
-			'pg_dumpall',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_no_globals",
-		],
-		restore_cmd => [
-			'pg_restore', '-C', '--no-globals',
-			'--format' => 'directory',
-			'--file' => "$tempdir/restore_no_globals.sql",
-			"$tempdir/restore_no_globals",
-		],
-		like => qr/^\n\QCOPY public.no_globals_test (a, b) FROM stdin;\E/xm,
-		unlike => qr/^\QCREATE ROLE dumpall;\E/xm,
-	},);
-
-# First execute the setup_sql
-foreach my $run (sort keys %pgdumpall_runs)
-{
-	if ($pgdumpall_runs{$run}->{setup_sql})
-	{
-		$node->safe_psql($run_db, $pgdumpall_runs{$run}->{setup_sql});
-	}
-}
-
-# Execute the tests
-foreach my $run (sort keys %pgdumpall_runs)
-{
-	# Create a new target cluster to pg_restore each test case run so that we
-	# don't need to take care of the cleanup from the target cluster after each
-	# run.
-	my $target_node = PostgreSQL::Test::Cluster->new("target_$run");
-	$target_node->init;
-	$target_node->start;
-
-	# Dumpall from node cluster.
-	$node->command_ok(\@{ $pgdumpall_runs{$run}->{dump_cmd} },
-		"$run: pg_dumpall runs");
-
-	# Restore the dump on "target_node" cluster.
-	my @restore_cmd = (
-		@{ $pgdumpall_runs{$run}->{restore_cmd} },
-		'--host', $target_node->host, '--port', $target_node->port);
-
-	my ($stdout, $stderr) = run_command(\@restore_cmd);
-
-	# pg_restore --file output file.
-	my $output_file = slurp_file("$tempdir/${run}.sql");
-
-	if (   !($pgdumpall_runs{$run}->{like})
-		&& !($pgdumpall_runs{$run}->{unlike}))
-	{
-		die "missing \"like\" or \"unlike\" in test \"$run\"";
-	}
-
-	if ($pgdumpall_runs{$run}->{like})
-	{
-		like($output_file, $pgdumpall_runs{$run}->{like}, "should dump $run");
-	}
-
-	if ($pgdumpall_runs{$run}->{unlike})
-	{
-		unlike(
-			$output_file,
-			$pgdumpall_runs{$run}->{unlike},
-			"should not dump $run");
-	}
-
-	$target_node->stop;
-	$target_node->clean_node;
-}
-
-# Some negative test case with dump of pg_dumpall and restore using pg_restore
-# report an error when -C is not used in pg_restore with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom",
-		'--format' => 'custom',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: option -C\/--create must be specified when restoring an archive created by pg_dumpall\E/,
-	'When -C is not used in pg_restore with dump of pg_dumpall');
-
-# report an error when \l/--list option is used with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--list',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: option -l\/--list cannot be used when restoring an archive created by pg_dumpall\E/,
-	'When --list is used in pg_restore with dump of pg_dumpall');
-
-# report an error when -L/--use-list option is used with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--use-list' => 'use',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: option -L\/--use-list cannot be used when restoring an archive created by pg_dumpall\E/,
-	'When -L/--use-list is used in pg_restore with dump of pg_dumpall');
-
-# report an error when --strict-names option is used with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--strict-names',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: option --strict-names cannot be used when restoring an archive created by pg_dumpall\E/,
-	'When --strict-names is used in pg_restore with dump of pg_dumpall');
-
-# report an error when --clean and -g/--globals-only are used in pg_restore with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--clean',
-		'--globals-only',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: options --clean and -g\/--globals-only cannot be used together when restoring an archive created by pg_dumpall\E/,
-	'When --clean and -g/--globals-only are used in pg_restore with dump of pg_dumpall'
-);
-
-# report an error when non-exist database is given with -d option
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'-d' => 'dbpq',
-	],
-	qr/\QFATAL:  database "dbpq" does not exist\E/,
-	'When non-existent database is given with -d option in pg_restore with dump of pg_dumpall'
-);
-
-# report an error when --no-schema is used with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--no-schema',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: option --no-schema cannot be used when restoring an archive created by pg_dumpall\E/,
-	'When --no-schema is used in pg_restore with dump of pg_dumpall');
-
-# report an error when --data-only is used with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--data-only',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: option -a\/--data-only cannot be used when restoring an archive created by pg_dumpall\E/,
-	'When --data-only is used in pg_restore with dump of pg_dumpall');
-
-# report an error when --statistics-only is used with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--statistics-only',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: option --statistics-only cannot be used when restoring an archive created by pg_dumpall\E/,
-	'When --statistics-only is used in pg_restore with dump of pg_dumpall');
-
-# report an error when --section excludes pre-data with dump of pg_dumpall
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--section' => 'post-data',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: option --section cannot exclude --pre-data when restoring a pg_dumpall archive\E/,
-	'When --section=post-data is used in pg_restore with dump of pg_dumpall');
-
-# report an error when --globals-only and --data-only are used together
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--globals-only',
-		'--data-only',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: options -a\/--data-only and -g\/--globals-only cannot be used together\E/,
-	'When --globals-only and --data-only are used together');
-
-# report an error when --globals-only and --schema-only are used together
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--globals-only',
-		'--schema-only',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: options -g\/--globals-only and -s\/--schema-only cannot be used together\E/,
-	'When --globals-only and --schema-only are used together');
-
-# report an error when --globals-only and --statistics-only are used together
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--globals-only',
-		'--statistics-only',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: options -g\/--globals-only and --statistics-only cannot be used together\E/,
-	'When --globals-only and --statistics-only are used together');
-
-# report an error when --globals-only and --statistics are used together
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--globals-only',
-		'--statistics',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: options --statistics and -g\/--globals-only cannot be used together\E/,
-	'When --globals-only and --statistics are used together');
-
-# report an error when --globals-only and --exit-on-error are used together
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--globals-only',
-		'--exit-on-error',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: options --exit-on-error and -g\/--globals-only cannot be used together\E/,
-	'When --globals-only and --exit-on-error are used together');
-
-# report an error when --globals-only and --single-transaction are used together
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--globals-only',
-		'--single-transaction',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: options -g\/--globals-only and -1\/--single-transaction cannot be used together\E/,
-	'When --globals-only and --single-transaction are used together');
-
-# report an error when --globals-only and --transaction-size are used together
-$node->command_fails_like(
-	[
-		'pg_restore',
-		"$tempdir/format_custom", '-C',
-		'--format' => 'custom',
-		'--globals-only',
-		'--transaction-size' => '100',
-		'--file' => "$tempdir/error_test.sql",
-	],
-	qr/\Qpg_restore: error: options -g\/--globals-only and --transaction-size cannot be used together\E/,
-	'When --globals-only and --transaction-size are used together');
-
-# verify map.dat preamble exists
-my $map_dat_content = slurp_file("$tempdir/format_directory/map.dat");
-like(
-	$map_dat_content,
-	qr/^# map\.dat\n.*# This file maps oids to database names/ms,
-	'map.dat contains expected preamble');
-
-# verify commenting out a line in map.dat skips that database
-$node->safe_psql(
-	$run_db, 'CREATE DATABASE comment_test_db;
-\c comment_test_db
-CREATE TABLE comment_test_table (id int);');
-
-$node->command_ok(
-	[
-		'pg_dumpall',
-		'--format' => 'directory',
-		'--file' => "$tempdir/comment_test",
-	],
-	'pg_dumpall for comment test');
-
-# Modify map.dat to comment out the comment_test_db entry
-my $map_content = slurp_file("$tempdir/comment_test/map.dat");
-$map_content =~ s/^(\d+ comment_test_db)$/# $1/m;
-open(my $fh, '>', "$tempdir/comment_test/map.dat")
-  or die "Cannot open map.dat: $!";
-print $fh $map_content;
-close($fh);
-
-# Create a target node and restore - commented db should be skipped
-my $target_comment = PostgreSQL::Test::Cluster->new("target_comment");
-$target_comment->init;
-$target_comment->start;
-
-$node->command_ok(
-	[
-		'pg_restore', '-C',
-		'--format' => 'directory',
-		'--file' => "$tempdir/comment_test_restore.sql",
-		'--host', $target_comment->host,
-		'--port', $target_comment->port,
-		"$tempdir/comment_test",
-	],
-	'pg_restore with commented out database in map.dat');
-
-my $restore_output = slurp_file("$tempdir/comment_test_restore.sql");
-unlike(
-	$restore_output,
-	qr/CREATE DATABASE comment_test_db/,
-	'commented out database in map.dat is not restored');
-
-# Test that --clean implies --if-exists for pg_dumpall archives
-$node->command_ok(
-	[
-		'pg_restore', '-C',
-		'--format' => 'custom',
-		'--clean',
-		'--file' => "$tempdir/clean_test.sql",
-		"$tempdir/format_custom",
-	],
-	'pg_restore with --clean on pg_dumpall archive');
-
-my $clean_output = slurp_file("$tempdir/clean_test.sql");
-like(
-	$clean_output,
-	qr/DROP ROLE IF EXISTS/,
-	'--clean implies --if-exists: DROP ROLE IF EXISTS in output');
-
-$node->stop('fast');
-
-done_testing();
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index f9eb23e52c9..1969d467c1d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -648,7 +648,6 @@ DatumTupleFields
 DbInfo
 DbInfoArr
 DbLocaleInfo
-DbOidName
 DdlOptType
 DdlOption
 DeClonePtrType
-- 
2.43.0

---

From: Noah Misch <[email-masked]>
Date: Wed, 17 Jun 2026 14:59:52 -0700

> release notes, but apart from that I think it does the right thing.

I checked "git diff 763aaa06f03^ src/bin/pg_dump/{*.c,*.h,t}" and I agree this
reverts everything.  Thanks for working through the revert.

---

From: Andrew Dunstan <[email-masked]>
Date: Thu, 18 Jun 2026 09:43:09 -0400

> reverts everything.  Thanks for working through the revert.

Thanks for checking. Pushed with the release notes update.

cheers

andrew