From 0cac227d9c5d75899c055039a0861ce71f734b2e Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <mahi6run@gmail.com>
Date: Tue, 17 Feb 2026 15:36:20 +0530
Subject: [PATCH] Non text modes for pg_dumpall, correspondingly change 
 pg_restore

    pg_dumpall acquires a new -F/--format option, with the same meanings as
    pg_dump. The default is p, meaning plain text. For any other value, a
    directory is created containing two files, toc.glo and map.dat. The
    first contains commands restoring the global data in custom format, and the second
    contains a map from oids to database names in text format. It will also contain a
    subdirectory called databases, inside which it will create archives in
    the specified format, named using the database oids.

    In these casess the -f argument is required.

    If pg_restore encounters a directory containing map.dat and toc.glo,
    it restores the global settings from toc.glo if exist, and then
    restores each database.

    pg_restore acquires two new options: -g/--globals-only which suppresses
    restoration of any databases, and --exclude-database which inhibits
    restoration of particualr database(s) in the same way the same option
    works in pg_dumpall.

v23 internal 1
---
 doc/src/sgml/ref/pg_dumpall.sgml     | 109 ++++-
 doc/src/sgml/ref/pg_restore.sgml     |  68 ++-
 src/bin/pg_dump/meson.build          |   1 +
 src/bin/pg_dump/parallel.c           |  10 +
 src/bin/pg_dump/pg_backup.h          |   2 +-
 src/bin/pg_dump/pg_backup_archiver.c |  63 ++-
 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         | 688 +++++++++++++++++++++-----
 src/bin/pg_dump/pg_restore.c         | 691 ++++++++++++++++++++++++++-
 src/bin/pg_dump/t/001_basic.pl       |  50 ++
 src/bin/pg_dump/t/007_pg_dumpall.pl  | 432 +++++++++++++++++
 src/tools/pgindent/typedefs.list     |   1 +
 14 files changed, 1961 insertions(+), 159 deletions(-)
 create mode 100644 src/bin/pg_dump/t/007_pg_dumpall.pl

diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index 8834b7ec141..1e4678334fa 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -16,7 +16,10 @@ PostgreSQL documentation
 
  <refnamediv>
   <refname>pg_dumpall</refname>
-  <refpurpose>extract a <productname>PostgreSQL</productname> database cluster into a script file</refpurpose>
+
+  <refpurpose>
+   export a <productname>PostgreSQL</productname> database cluster as an SQL script or to other formats
+  </refpurpose>
  </refnamediv>
 
  <refsynopsisdiv>
@@ -33,7 +36,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 SQL script file or an archive.  The output 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 +55,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
@@ -131,16 +139,93 @@ 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>toc.glo</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 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
+        <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>
       <listitem>
        <para>
         Dump only global objects (roles and tablespaces), no databases.
+        Note: <term><option>--globals-only</option></term> can not be used with
+        <term><option>--clean</option></term> with non-text dump format.
        </para>
       </listitem>
      </varlistentry>
@@ -937,9 +1022,16 @@ exclude database <replaceable class="parameter">PATTERN</replaceable>
   <title>Examples</title>
   <para>
    To dump all databases:
-
+   If format is given, then dump will be based on format, default plain.
 <screen>
 <prompt>$</prompt> <userinput>pg_dumpall &gt; db.out</userinput>
+</screen>
+
+<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>
+<prompt>$</prompt> <userinput>pg_dumpall --format=plain -f db.out</userinput>
 </screen>
   </para>
 
@@ -956,6 +1048,15 @@ 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 dump was taken in non-text format, then use pg_restore to restore all databases.
+<screen>
+<prompt>$</prompt> <userinput>pg_restore db.out -d postgres -C</userinput>
+</screen>
+   This will restore all the databases. If user don't want to restore some databases, then use
+   --exclude-pattern to skip those.
+</para>
  </refsect1>
 
  <refsect1>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index 420a308a7c7..c4f59187048 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> databases from archives
+   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>
@@ -152,6 +161,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>
@@ -247,6 +258,21 @@ 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>.
+        Note: <term><option>--globals-only</option></term> can not be used with <term><option>--exit-on-error</option></term>,
+              <term><option>--single-transaction</option></term>, <term><option>--clean</option></term> and <term><option>--transaction-size=N</option></term>
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-I <replaceable class="parameter">index</replaceable></option></term>
       <term><option>--index=<replaceable class="parameter">index</replaceable></option></term>
@@ -581,6 +607,28 @@ 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 79bd5036841..7c9a475963b 100644
--- a/src/bin/pg_dump/meson.build
+++ b/src/bin/pg_dump/meson.build
@@ -103,6 +103,7 @@ 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 56cb2c1f32d..29fa21a1cc0 100644
--- a/src/bin/pg_dump/parallel.c
+++ b/src/bin/pg_dump/parallel.c
@@ -333,6 +333,16 @@ 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 2f8d9799c30..fda912ba0a9 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
 
 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 7afcc0859c8..faa08cedaa5 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -86,7 +86,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);
 
@@ -339,9 +339,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;
@@ -458,7 +463,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");
 
@@ -761,6 +766,15 @@ RestoreArchive(Archive *AHX)
 			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->tag && (strcmp(te->tag, "DROP_GLOBAL") == 0))
+				continue;
+
 			switch (_tocEntryRestorePass(te))
 			{
 				case RESTORE_PASS_MAIN:
@@ -1316,7 +1330,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)
@@ -1691,11 +1705,15 @@ 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)
+		  const pg_compress_specification compression_spec,
+		  bool append_data)
 {
 	CompressFileHandle *CFH;
 	const char *mode;
@@ -1715,7 +1733,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;
@@ -2391,7 +2409,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 = false;
+	AH->public.std_strings = true;
 
 	/* sql error handling */
 	AH->public.exit_on_error = true;
@@ -3027,6 +3045,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;
+
 	/*
 	 * Process exclusions that affect certain classes of TOC entries.
 	 */
@@ -3062,6 +3090,14 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
 		if (ropt->no_subscriptions &&
 			strncmp(te->tag, "SUBSCRIPTION", strlen("SUBSCRIPTION")) == 0)
 			return 0;
+
+		/*
+		 * Global object TOC entries (e.g., ROLEs or TABLESPACEs) are treated as
+		 * REQ_SCHEMA; follow this convention for their associated comments.
+		 */
+		if (strncmp(te->desc, "ROLE", strlen("ROLE")) == 0 ||
+			strncmp(te->desc, "TABLESPACE", strlen("TABLESPACE")) == 0)
+			return REQ_SCHEMA;
 	}
 
 	/*
@@ -3091,6 +3127,14 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
 		if (ropt->no_subscriptions &&
 			strncmp(te->tag, "SUBSCRIPTION", strlen("SUBSCRIPTION")) == 0)
 			return 0;
+
+		/*
+		 * Global object TOC entries (e.g., ROLEs or TABLESPACEs) are treated as
+		 * REQ_SCHEMA; follow this convention for their associated SECURITY LABELs.
+		 */
+		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 */
@@ -3865,6 +3909,9 @@ _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 325b53fc9bd..365073b3eae 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -394,6 +394,7 @@ 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);
 
 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_dump.c b/src/bin/pg_dump/pg_dump.c
index 49598304335..d3d0cac98df 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1292,7 +1292,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 98389d2034c..ec6fdaa822c 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -30,6 +30,7 @@
 #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"
@@ -67,7 +68,7 @@ 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);
+static int	runPgDump(const char *dbname, const char *create_opts, char *dbfile);
 static void buildShSecLabels(PGconn *conn,
 							 const char *catalog_name, Oid objectId,
 							 const char *objtype, const char *objname,
@@ -76,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 ArchiveFormat parseDumpFormat(const char *format);
+static int	createDumpId(void);
 
 static char pg_dump_bin[MAXPGPATH];
 static PQExpBuffer pgdumpopts;
@@ -123,6 +126,11 @@ static SimpleStringList database_exclude_patterns = {NULL, NULL};
 static SimpleStringList database_exclude_names = {NULL, NULL};
 
 static char *restrict_key;
+static Archive *fout = NULL;
+static pg_compress_specification compression_spec = {0};
+static int	dumpIdVal = 0;
+static ArchiveFormat archDumpFormat = archNull;
+static const CatalogId nilCatalogId = {0, 0};
 
 int
 main(int argc, char *argv[])
@@ -148,6 +156,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
@@ -197,6 +206,7 @@ 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;
@@ -207,6 +217,7 @@ main(int argc, char *argv[])
 	int			c,
 				ret;
 	int			optindex;
+	DumpOptions dopt;
 
 	pg_logging_init(argv[0]);
 	pg_logging_set_level(PG_LOG_WARNING);
@@ -244,8 +255,9 @@ main(int argc, char *argv[])
 	}
 
 	pgdumpopts = createPQExpBuffer();
+	InitDumpOptions(&dopt);
 
-	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)
 		{
@@ -273,7 +285,9 @@ 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;
@@ -313,6 +327,7 @@ main(int argc, char *argv[])
 
 			case 'U':
 				pguser = pg_strdup(optarg);
+				dopt.cparams.username = pg_strdup(optarg);
 				break;
 
 			case 'v':
@@ -434,6 +449,32 @@ main(int argc, char *argv[])
 		exit_nicely(1);
 	}
 
+	/* Get format for dump. */
+	archDumpFormat = parseDumpFormat(format_name);
+
+	/*
+	 * 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 %s=d|c|t requires option %s",
+					 "-F/--format", "-f/--file");
+		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
@@ -494,6 +535,27 @@ 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.
 	 */
@@ -543,19 +605,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.
 	 */
@@ -593,35 +642,111 @@ main(int argc, char *argv[])
 	if (quote_all_identifiers)
 		executeCommand(conn, "SET quote_all_identifiers = true");
 
-	fprintf(OPF, "--\n-- PostgreSQL database cluster dump\n--\n\n");
-	if (verbose)
-		dumpTimestamp("Started on");
+	/* create a archive file for global commands. */
+	if (archDumpFormat != archNull)
+	{
+		PQExpBuffer qry = createPQExpBuffer();
+		char            global_path[MAXPGPATH];
+		const char *encname;
+
+		/* Set file path for global sql commands. */
+		snprintf(global_path, MAXPGPATH, "%s/toc.glo", filename);
 
-	/*
-	 * 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);
+		/* Open the output file */
+		fout = CreateArchive(global_path, archCustom, compression_spec,
+				dosync, archModeWrite, NULL, DATA_DIR_SYNC_METHOD_FSYNC);
 
-	/*
-	 * 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.
-	 */
+		/* Make dump options accessible right away */
+		SetArchiveOptions(fout, &dopt, NULL);
 
-	/* Restore will need to write to the target cluster */
-	fprintf(OPF, "SET default_transaction_read_only = off;\n\n");
+		((ArchiveHandle *) fout)->connection = conn;
+		((ArchiveHandle *) fout)->public.numWorkers = 1;
 
-	/* Replicate encoding and standard_conforming_strings in output */
-	fprintf(OPF, "SET client_encoding = '%s';\n",
+		/* 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. */
+		appendPQExpBuffer(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, "SET standard_conforming_strings = on;\n");
+		fprintf(OPF, "\n");
+	}
 
 	if (!data_only && !statistics_only && !no_schema)
 	{
@@ -630,8 +755,14 @@ 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)
+		if (output_clean || archDumpFormat != archNull)
 		{
 			if (!globals_only && !roles_only && !tablespaces_only)
 				dropDBs(conn);
@@ -665,28 +796,45 @@ main(int argc, char *argv[])
 			dumpTablespaces(conn);
 	}
 
-	/*
-	 * 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);
+	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);
+	}
 
 	if (!globals_only && !roles_only && !tablespaces_only)
 		dumpDatabases(conn);
 
-	PQfinish(conn);
+	if (archDumpFormat == archNull)
+	{
+		PQfinish(conn);
 
-	if (verbose)
-		dumpTimestamp("Completed on");
-	fprintf(OPF, "--\n-- PostgreSQL database cluster dump complete\n--\n\n");
+		if (verbose)
+			dumpTimestamp("Completed on");
+		fprintf(OPF, "--\n-- PostgreSQL database cluster dump complete\n--\n\n");
 
-	if (filename)
+		if (filename)
+		{
+			fclose(OPF);
+
+			/* sync the resulting file, errors are not fatal */
+			if (dosync && (archDumpFormat == archNull))
+				(void) fsync_fname(filename, false);
+		}
+	}
+	else
 	{
-		fclose(OPF);
+		RestoreOptions *ropt;
 
-		/* sync the resulting file, errors are not fatal */
-		if (dosync)
-			(void) fsync_fname(filename, false);
+		ropt = NewRestoreOptions();
+		SetArchiveOptions(fout, &dopt, ropt);
+
+		/* Mark which entries should be output */
+		ProcessArchiveRestoreOptions(fout);
+		CloseArchive(fout);
 	}
 
 	exit_nicely(0);
@@ -696,12 +844,14 @@ main(int argc, char *argv[])
 static void
 help(void)
 {
-	printf(_("%s exports a PostgreSQL database cluster as an SQL script.\n\n"), progname);
+	printf(_("%s exports a PostgreSQL database cluster as an SQL script or to other formats.\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"));
@@ -796,24 +946,45 @@ dropRoles(PGconn *conn)
 
 	i_rolname = PQfnumber(res, "rolname");
 
-	if (PQntuples(res) > 0)
+	if (PQntuples(res) > 0 && archDumpFormat == archNull)
 		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);
 
-		fprintf(OPF, "DROP ROLE %s%s;\n",
-				if_exists ? "IF EXISTS " : "",
-				fmtId(rolename));
+		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 = "DROP_GLOBAL",
+						.description = "DROP_GLOBAL",
+						.section = SECTION_PRE_DATA,
+						.createStmt = delQry->data));
+		}
+
+		destroyPQExpBuffer(delQry);
 	}
 
 	PQclear(res);
 	destroyPQExpBuffer(buf);
 
-	fprintf(OPF, "\n\n");
+	if (archDumpFormat == archNull)
+		fprintf(OPF, "\n\n");
 }
 
 /*
@@ -823,6 +994,8 @@ static void
 dumpRoles(PGconn *conn)
 {
 	PQExpBuffer buf = createPQExpBuffer();
+	PQExpBuffer comment_buf = createPQExpBuffer();
+	PQExpBuffer seclabel_buf = createPQExpBuffer();
 	PGresult   *res;
 	int			i_oid,
 				i_rolname,
@@ -894,7 +1067,7 @@ dumpRoles(PGconn *conn)
 	i_rolcomment = PQfnumber(res, "rolcomment");
 	i_is_current_user = PQfnumber(res, "is_current_user");
 
-	if (PQntuples(res) > 0)
+	if (PQntuples(res) > 0 && archDumpFormat == archNull)
 		fprintf(OPF, "--\n-- Roles\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
@@ -913,6 +1086,8 @@ dumpRoles(PGconn *conn)
 		}
 
 		resetPQExpBuffer(buf);
+		resetPQExpBuffer(comment_buf);
+		resetPQExpBuffer(seclabel_buf);
 
 		if (binary_upgrade)
 		{
@@ -989,17 +1164,53 @@ dumpRoles(PGconn *conn)
 
 		if (!no_comments && !PQgetisnull(res, i, i_rolcomment))
 		{
-			appendPQExpBuffer(buf, "COMMENT ON ROLE %s IS ", fmtId(rolename));
-			appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolcomment), conn);
-			appendPQExpBufferStr(buf, ";\n");
+			appendPQExpBuffer(comment_buf, "COMMENT ON ROLE %s IS ", fmtId(rolename));
+			appendStringLiteralConn(comment_buf, PQgetvalue(res, i, i_rolcomment), conn);
+			appendPQExpBufferStr(comment_buf, ";\n");
 		}
 
 		if (!no_security_labels)
 			buildShSecLabels(conn, "pg_authid", auth_oid,
 							 "ROLE", rolename,
-							 buf);
+							 seclabel_buf);
 
-		fprintf(OPF, "%s", buf->data);
+		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("%s %s", "ROLE", 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));
+		}
 	}
 
 	/*
@@ -1007,7 +1218,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)
+	if (PQntuples(res) > 0 && archDumpFormat == archNull)
 		fprintf(OPF, "\n--\n-- User Configurations\n--\n");
 
 	for (i = 0; i < PQntuples(res); i++)
@@ -1015,9 +1226,12 @@ dumpRoles(PGconn *conn)
 
 	PQclear(res);
 
-	fprintf(OPF, "\n\n");
+	if (archDumpFormat == archNull)
+		fprintf(OPF, "\n\n");
 
 	destroyPQExpBuffer(buf);
+	destroyPQExpBuffer(comment_buf);
+	destroyPQExpBuffer(seclabel_buf);
 }
 
 
@@ -1031,6 +1245,7 @@ static void
 dumpRoleMembership(PGconn *conn)
 {
 	PQExpBuffer buf = createPQExpBuffer();
+	PQExpBuffer qurybuf = createPQExpBuffer();
 	PQExpBuffer optbuf = createPQExpBuffer();
 	PGresult   *res;
 	int			start = 0,
@@ -1093,7 +1308,7 @@ dumpRoleMembership(PGconn *conn)
 	i_inherit_option = PQfnumber(res, "inherit_option");
 	i_set_option = PQfnumber(res, "set_option");
 
-	if (PQntuples(res) > 0)
+	if (PQntuples(res) > 0 && archDumpFormat == archNull)
 		fprintf(OPF, "--\n-- Role memberships\n--\n\n");
 
 	/*
@@ -1229,8 +1444,9 @@ dumpRoleMembership(PGconn *conn)
 
 				/* Generate the actual GRANT statement. */
 				resetPQExpBuffer(optbuf);
-				fprintf(OPF, "GRANT %s", fmtId(role));
-				fprintf(OPF, " TO %s", fmtId(member));
+				resetPQExpBuffer(qurybuf);
+				appendPQExpBuffer(qurybuf, "GRANT %s", fmtId(role));
+				appendPQExpBuffer(qurybuf, " TO %s", fmtId(member));
 				if (*admin_option == 't')
 					appendPQExpBufferStr(optbuf, "ADMIN OPTION");
 				if (dump_grant_options)
@@ -1251,10 +1467,21 @@ dumpRoleMembership(PGconn *conn)
 					appendPQExpBufferStr(optbuf, "SET FALSE");
 				}
 				if (optbuf->data[0] != '\0')
-					fprintf(OPF, " WITH %s", optbuf->data);
+					appendPQExpBuffer(qurybuf, " WITH %s", optbuf->data);
 				if (dump_grantors)
-					fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
-				fprintf(OPF, ";\n");
+					appendPQExpBuffer(qurybuf, " GRANTED BY %s", fmtId(grantor));
+				appendPQExpBuffer(qurybuf, ";\n");
+
+				if (archDumpFormat == archNull)
+					fprintf(OPF, "%s", qurybuf->data);
+				else
+					ArchiveEntry(fout,
+								 nilCatalogId,	/* catalog ID */
+								 createDumpId(),	/* dump ID */
+								 ARCHIVE_OPTS(.tag = fmtId(role),
+											  .description = "ROLE PROPERTIES",
+											  .section = SECTION_PRE_DATA,
+											  .createStmt = qurybuf->data));
 			}
 		}
 
@@ -1265,8 +1492,10 @@ dumpRoleMembership(PGconn *conn)
 
 	PQclear(res);
 	destroyPQExpBuffer(buf);
+	destroyPQExpBuffer(optbuf);
 
-	fprintf(OPF, "\n\n");
+	if (archDumpFormat == archNull)
+		fprintf(OPF, "\n\n");
 }
 
 
@@ -1293,7 +1522,7 @@ dumpRoleGUCPrivs(PGconn *conn)
 					   "FROM pg_catalog.pg_parameter_acl "
 					   "ORDER BY 1");
 
-	if (PQntuples(res) > 0)
+	if (PQntuples(res) > 0 && archDumpFormat == archNull)
 		fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
@@ -1318,14 +1547,25 @@ dumpRoleGUCPrivs(PGconn *conn)
 			exit_nicely(1);
 		}
 
-		fprintf(OPF, "%s", buf->data);
+		if (archDumpFormat == archNull)
+			fprintf(OPF, "%s", buf->data);
+		else
+			ArchiveEntry(fout,
+						 nilCatalogId,	/* catalog ID */
+						 createDumpId(),	/* dump ID */
+						 ARCHIVE_OPTS(.tag = fmtId(parowner),
+									  .description = "ROLE PROPERTIES",
+									  .section = SECTION_PRE_DATA,
+									  .createStmt = buf->data));
 
 		free(fparname);
 		destroyPQExpBuffer(buf);
 	}
 
 	PQclear(res);
-	fprintf(OPF, "\n\n");
+
+	if (archDumpFormat == archNull)
+		fprintf(OPF, "\n\n");
 }
 
 
@@ -1347,21 +1587,45 @@ dropTablespaces(PGconn *conn)
 					   "WHERE spcname !~ '^pg_' "
 					   "ORDER BY 1");
 
-	if (PQntuples(res) > 0)
+	if (PQntuples(res) > 0 && archDumpFormat == archNull)
 		fprintf(OPF, "--\n-- Drop tablespaces\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
 	{
 		char	   *spcname = PQgetvalue(res, i, 0);
+		PQExpBuffer	delQry = createPQExpBuffer();
 
-		fprintf(OPF, "DROP TABLESPACE %s%s;\n",
+		appendPQExpBuffer(delQry, "DROP TABLESPACE %s%s;\n",
 				if_exists ? "IF EXISTS " : "",
 				fmtId(spcname));
+
+		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 = "DROP_GLOBAL",
+						.description = "DROP_GLOBAL",
+						.section = SECTION_PRE_DATA,
+						.createStmt = delQry->data));
+		}
+
+		destroyPQExpBuffer(delQry);
 	}
 
 	PQclear(res);
 
-	fprintf(OPF, "\n\n");
+	if (archDumpFormat == archNull)
+		fprintf(OPF, "\n\n");
 }
 
 /*
@@ -1371,6 +1635,8 @@ static void
 dumpTablespaces(PGconn *conn)
 {
 	PGresult   *res;
+	PQExpBuffer comment_buf = createPQExpBuffer();
+	PQExpBuffer seclabel_buf = createPQExpBuffer();
 	int			i;
 
 	/*
@@ -1387,7 +1653,7 @@ dumpTablespaces(PGconn *conn)
 					   "WHERE spcname !~ '^pg_' "
 					   "ORDER BY 1");
 
-	if (PQntuples(res) > 0)
+	if (PQntuples(res) > 0 && archDumpFormat == archNull)
 		fprintf(OPF, "--\n-- Tablespaces\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
@@ -1406,6 +1672,9 @@ 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");
@@ -1447,24 +1716,67 @@ dumpTablespaces(PGconn *conn)
 
 		if (!no_comments && spccomment && spccomment[0] != '\0')
 		{
-			appendPQExpBuffer(buf, "COMMENT ON TABLESPACE %s IS ", fspcname);
-			appendStringLiteralConn(buf, spccomment, conn);
-			appendPQExpBufferStr(buf, ";\n");
+			appendPQExpBuffer(comment_buf, "COMMENT ON TABLESPACE %s IS ", fspcname);
+			appendStringLiteralConn(comment_buf, spccomment, conn);
+			appendPQExpBufferStr(comment_buf, ";\n");
 		}
 
 		if (!no_security_labels)
 			buildShSecLabels(conn, "pg_tablespace", spcoid,
 							 "TABLESPACE", spcname,
-							 buf);
+							 seclabel_buf);
 
-		fprintf(OPF, "%s", buf->data);
+		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("%s %s", "TABLESPACE", 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));
+		}
 
 		free(fspcname);
 		destroyPQExpBuffer(buf);
 	}
 
 	PQclear(res);
-	fprintf(OPF, "\n\n");
+	destroyPQExpBuffer(comment_buf);
+	destroyPQExpBuffer(seclabel_buf);
+
+	if (archDumpFormat == archNull)
+		fprintf(OPF, "\n\n");
 }
 
 
@@ -1487,12 +1799,13 @@ dropDBs(PGconn *conn)
 					   "WHERE datallowconn AND datconnlimit != -2 "
 					   "ORDER BY datname");
 
-	if (PQntuples(res) > 0)
+	if (PQntuples(res) > 0 && archDumpFormat == archNull)
 		fprintf(OPF, "--\n-- Drop databases (except postgres and template1)\n--\n\n");
 
 	for (i = 0; i < PQntuples(res); i++)
 	{
 		char	   *dbname = PQgetvalue(res, i, 0);
+		PQExpBuffer delQry = createPQExpBuffer();
 
 		/*
 		 * Skip "postgres" and "template1"; dumpDatabases() will deal with
@@ -1503,15 +1816,35 @@ dropDBs(PGconn *conn)
 			strcmp(dbname, "template0") != 0 &&
 			strcmp(dbname, "postgres") != 0)
 		{
-			fprintf(OPF, "DROP DATABASE %s%s;\n",
-					if_exists ? "IF EXISTS " : "",
-					fmtId(dbname));
+			if (archDumpFormat == archNull)
+			{
+				appendPQExpBuffer(delQry, "DROP DATABASE %s%s;\n",
+						if_exists ? "IF EXISTS " : "",
+						fmtId(dbname));
+				fprintf(OPF, "%s", delQry->data);
+			}
+			else
+			{
+				appendPQExpBuffer(delQry, "DROP DATABASE IF EXISTS %s;\n",
+						fmtId(dbname));
+
+				ArchiveEntry(fout,
+						nilCatalogId,  /* catalog ID */
+						createDumpId(),        /* dump ID */
+						ARCHIVE_OPTS(.tag = "DROP_GLOBAL",
+							.description = "DROP_GLOBAL",
+							.section = SECTION_PRE_DATA,
+							.createStmt = delQry->data));
+			}
+
+			destroyPQExpBuffer(delQry);
 		}
 	}
 
 	PQclear(res);
 
-	fprintf(OPF, "\n\n");
+	if (archDumpFormat == archNull)
+		fprintf(OPF, "\n\n");
 }
 
 
@@ -1533,7 +1866,7 @@ dumpUserConfig(PGconn *conn, const char *username)
 
 	res = executeQuery(conn, buf->data);
 
-	if (PQntuples(res) > 0)
+	if (PQntuples(res) > 0 && archDumpFormat == archNull)
 	{
 		char	   *sanitized;
 
@@ -1548,7 +1881,17 @@ dumpUserConfig(PGconn *conn, const char *username)
 		makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
 							   "ROLE", username, NULL, NULL,
 							   buf);
-		fprintf(OPF, "%s", buf->data);
+
+		if (archDumpFormat == archNull)
+			fprintf(OPF, "%s", buf->data);
+		else
+			ArchiveEntry(fout,
+						 nilCatalogId,	/* catalog ID */
+						 createDumpId(),	/* dump ID */
+						 ARCHIVE_OPTS(.tag = username,
+									  .description = "ROLE PROPERTIES",
+									  .section = SECTION_PRE_DATA,
+									  .createStmt = buf->data));
 	}
 
 	PQclear(res);
@@ -1618,6 +1961,9 @@ dumpDatabases(PGconn *conn)
 {
 	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
@@ -1631,19 +1977,43 @@ 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");
 
-	if (PQntuples(res) > 0)
+	if (PQntuples(res) > 0 && archDumpFormat == archNull)
 		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	   *sanitized;
-		const char *create_opts;
+		char	   *oid = PQgetvalue(res, i, 1);
+		const char *create_opts = "";
 		int			ret;
 
 		/* Skip template0, even if it's not marked !datallowconn. */
@@ -1660,7 +2030,10 @@ dumpDatabases(PGconn *conn)
 		pg_log_info("dumping database \"%s\"", dbname);
 
 		sanitized = sanitize_line(dbname, true);
-		fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n", sanitized);
+
+		if (archDumpFormat == archNull)
+			fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n", sanitized);
+
 		free(sanitized);
 
 		/*
@@ -1675,24 +2048,40 @@ dumpDatabases(PGconn *conn)
 		{
 			if (output_clean)
 				create_opts = "--clean --create";
+			/* 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 = "";
-				/* Since pg_dump won't emit a \connect command, we must */
-				fprintf(OPF, "\\connect %s\n\n", dbname);
-			}
 		}
 		else
 			create_opts = "--create";
 
-		if (filename)
+		if (filename && archDumpFormat == archNull)
 			fclose(OPF);
 
-		ret = runPgDump(dbname, create_opts);
+		/*
+		 * 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);
+		}
+
+		ret = runPgDump(dbname, create_opts, dbfilepath);
 		if (ret != 0)
 			pg_fatal("pg_dump failed on database \"%s\", exiting", dbname);
 
-		if (filename)
+		if (filename && archDumpFormat == archNull)
 		{
 			OPF = fopen(filename, PG_BINARY_A);
 			if (!OPF)
@@ -1701,6 +2090,10 @@ dumpDatabases(PGconn *conn)
 		}
 	}
 
+	/* Close map file */
+	if (archDumpFormat != archNull)
+		fclose(map_file);
+
 	PQclear(res);
 }
 
@@ -1710,7 +2103,7 @@ 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)
 {
 	PQExpBufferData connstrbuf;
 	PQExpBufferData cmd;
@@ -1719,17 +2112,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 not a 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\" %s -f %s %s", pg_dump_bin,
+						  pgdumpopts->data, 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
@@ -1874,3 +2286,47 @@ 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;
+}
+
+/*
+ * createDumpId
+ *
+ * This will return next last used oid.
+ */
+static int
+createDumpId(void)
+{
+	return ++dumpIdVal;
+}
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 84b8d410c9e..a53545c47dd 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,12 +41,16 @@
 #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 "dumputils.h"
 #include "fe_utils/option_utils.h"
+#include "fe_utils/string_utils.h"
 #include "filter.h"
 #include "getopt_long.h"
 #include "parallel.h"
@@ -54,18 +58,41 @@
 
 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);
+static int	restore_global_objects(const char *inputFileSpec, RestoreOptions *opts);
+
+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,
+										SimplePtrList *dbname_oid_list,
+										SimpleStringList db_exclude_patterns);
+static int	get_dbname_oid_list_from_mfile(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 		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;
@@ -89,6 +116,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'},
@@ -142,6 +170,7 @@ main(int argc, char **argv)
 		{"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}
 	};
@@ -170,7 +199,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)
@@ -197,11 +226,14 @@ main(int argc, char **argv)
 				if (strlen(optarg) != 0)
 					opts->formatName = pg_strdup(optarg);
 				break;
+			case 'g':
+				/* restore only global sql commands. */
+				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,
@@ -321,6 +353,10 @@ 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);
@@ -347,6 +383,14 @@ 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 %s cannot be used together with %s",
+					 "--exclude-database", "-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)
 	{
@@ -420,6 +464,31 @@ main(int argc, char **argv)
 		pg_fatal("options %s and %s cannot be used together",
 				 "-1/--single-transaction", "--transaction-size");
 
+	if (opts->single_txn && globals_only)
+		pg_fatal("options %s and %s cannot be used together when restoring an archive created by pg_dumpall",
+				"--single-transaction", "-g/--globals-only");
+
+	if (opts->txn_size && globals_only)
+		pg_fatal("options %s and %s cannot be used together when restoring an archive created by pg_dumpall",
+				"--transaction-size", "-g/--globals-only");
+
+	if (opts->exit_on_error && globals_only)
+		pg_fatal("options %s and %s cannot be used together when restoring an archive created by pg_dumpall",
+				"--exit-on-error", "-g/--globals-only");
+
+	if (data_only && globals_only)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-a/--data-only", "-g/--globals-only");
+	if (schema_only && globals_only)
+		pg_fatal("options %s and %s cannot be used together",
+				 "-s/--schema-only", "-g/--globals-only");
+	if (statistics_only && globals_only)
+		pg_fatal("options %s and %s cannot be used together",
+				 "--statistics-only", "-g/--globals-only");
+	if (with_statistics && globals_only)
+		pg_fatal("options %s and %s cannot be used together",
+				 "--statistics", "-g/--globals-only");
+
 	/*
 	 * -C is not compatible with -1, because we can't create a database inside
 	 * a transaction block.
@@ -485,6 +554,176 @@ 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 = (RestoreOptions *) pg_malloc0(sizeof(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");
+
+		if (no_schema)
+			pg_fatal("option %s cannot be used when restoring an archive created by pg_dumpall",
+					 "--no-schenma");
+
+		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.
+		 */
+		if (!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);
+		}
+
+		/*
+		 * Always 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);
+
+		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);
+		}
+
+		/* 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;
+
+	/*
+	 * We're talking to the DB directly, don't send comments since they
+	 * obscure SQL when displaying errors
+	 */
+	((ArchiveHandle *) AH)->noTocComments = 0;
+
+	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);
 
 	SetArchiveOptions(AH, NULL, opts);
@@ -492,13 +731,25 @@ 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 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.
 	 */
-	on_exit_close_archive(AH);
+	if (!append_data)
+		on_exit_close_archive(AH);
+	else
+		replace_on_exit_close_archive(AH);
 
 	/* Let the archiver know how noisy to be */
 	AH->verbose = opts->verbose;
 
+	/*
+	 * We're talking to the DB directly, don't send comments since they
+	 * obscure SQL when displaying errors
+	 */
+	((ArchiveHandle *) AH)->noTocComments = 0;
+
 	/*
 	 * Whether to keep submitting sql commands as "pg_restore ... | psql ... "
 	 */
@@ -514,25 +765,21 @@ 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 PostgreSQL databases from archives created by pg_dump or pg_dumpall.\n\n"), progname);
 	printf(_("Usage:\n"));
 	printf(_("  %s [OPTION]... [FILE]\n"), progname);
 
@@ -550,6 +797,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"
@@ -566,6 +814,7 @@ 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"));
@@ -601,8 +850,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\n"
+			 "combined 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);
@@ -707,3 +956,409 @@ 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(char *dumpdirpath, SimplePtrList *dbname_oid_list)
+{
+	StringInfoData linebuf;
+	FILE	   *pfile;
+	char		map_file_path[MAXPGPATH];
+	int			count = 0;
+	int			len;
+
+
+	/*
+	 * If there is no map.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;
+	}
+
+	len = strlen(dumpdirpath);
+
+	/* Trim slash from directry name. */
+	while (len > 1 && dumpdirpath[len - 1] == '/')
+	{
+		dumpdirpath[len - 1] = '\0';
+		len--;
+	}
+
+	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);
+
+		dbidname = pg_malloc(offsetof(DbOidName, str) + namelen + 1);
+		dbidname->oid = db_oid;
+		strlcpy(dbidname->str, dbname, namelen);
+
+		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);
+
+	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 = (RestoreOptions *) pg_malloc0(sizeof(RestoreOptions));
+	RestoreOptions *tmpopts = (RestoreOptions *) pg_malloc0(sizeof(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((char*) 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, then connect to the database to process
+	 * it.
+	 */
+	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);
+
+	/* Close the db connection as we are done with exclude-database patterns. */
+	PQfinish(conn);
+
+	/* 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);
+		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));
+
+		/*
+		 * We need to reset override_dbname so that objects can be restored
+		 * into an already created database. (used with -d/--dbname option)
+		 */
+		if (tmpopts->cparams.override_dbname)
+		{
+			pfree(tmpopts->cparams.override_dbname);
+			tmpopts->cparams.override_dbname = NULL;
+		}
+
+		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
+			{
+				/* 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);
+
+	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 ab9310eb42b..f427fdbfa24 100644
--- a/src/bin/pg_dump/t/001_basic.pl
+++ b/src/bin/pg_dump/t/001_basic.pl
@@ -244,4 +244,54 @@ 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');
+
+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_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', '--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 -s\/--schema-only and -g\/--globals-only cannot be used together\E/,
+	'pg_restore: error: options -s/--schema-only and -g/--globals-only cannot be used together'
+);
+
+command_fails_like(
+	[ 'pg_restore', '--statistics-only', '--globals-only', '-d', 'xxx' ],
+	qr/\Qpg_restore: error: options --statistics-only and -g\/--globals-only cannot be used together\E/,
+	'pg_restore: error: options --statistics-only and -g/--globals-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'
+);
 done_testing();
diff --git a/src/bin/pg_dump/t/007_pg_dumpall.pl b/src/bin/pg_dump/t/007_pg_dumpall.pl
new file mode 100644
index 00000000000..d754a80fa60
--- /dev/null
+++ b/src/bin/pg_dump/t/007_pg_dumpall.pl
@@ -0,0 +1,432 @@
+# 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 $!";
+
+# 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 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';\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
+	},);
+
+# 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: 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');
+
+# test case 2: 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');
+
+# test case 3: 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');
+
+# test case 4: 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');
+
+# test case 5: 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');
+
+# test case 6: 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'
+);
+
+
+$node->stop('fast');
+
+done_testing();
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 241945734ec..1a89ef94bec 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -600,6 +600,7 @@ CustomScanMethods
 CustomScanState
 CycleCtr
 DBState
+DbOidName
 DCHCacheEntry
 DEADLOCK_INFO
 DECountItem
-- 
2.52.0

