From ac608a750fa559492b961c7dcd2f4b1fe47a7d3e Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <mahi6run@gmail.com>
Date: Wed, 8 Jan 2025 22:05:37 +0530
Subject: [PATCH] pg_dumpall with directory|tar|custom format and restore  it 
 by pg_restore

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

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

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

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

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

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

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

TODO1: test cases for new added options.

TODO2: We can dump and restore databases in parallel mode.
This needs more study.
---
 doc/src/sgml/ref/pg_dumpall.sgml |  67 +++
 doc/src/sgml/ref/pg_restore.sgml |  30 ++
 src/bin/pg_dump/pg_dumpall.c     | 170 ++++++-
 src/bin/pg_dump/pg_restore.c     | 781 ++++++++++++++++++++++++++++++-
 4 files changed, 1020 insertions(+), 28 deletions(-)

diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index 014f279258..77a8d6a0c5 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -125,6 +125,73 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+<varlistentry>
+      <term><option>-F <replaceable class="parameter">format</replaceable></option></term>
+      <term><option>--format=<replaceable class="parameter">format</replaceable></option></term>
+      <listitem>
+       <para>
+        Specify format of dump files.  If we want to dump all the databases,
+        then pass this as directory so that dump of all databases can be taken
+        in separate subdirectory in archive format.
+        by default, this is plain.
+
+       <variablelist>
+        <varlistentry>
+         <term><literal>d</literal></term>
+         <term><literal>directory</literal></term>
+         <listitem>
+          <para>
+           Output a directory-format archive suitable for input into pg_restore. This will create a directory
+           with one file for each table and large object being dumped, plus a so-called Table of Contents
+           file describing the dumped objects in a machine-readable format that pg_restore can read. A
+           directory format archive can be manipulated with standard Unix tools; for example, files in an
+           uncompressed archive can be compressed with the gzip, lz4, or zstd tools. This format is compressed
+           by default using gzip and also supports parallel dumps.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term><literal>p</literal></term>
+         <term><literal>plain</literal></term>
+         <listitem>
+          <para>
+           Output a plain-text SQL script file (the default).
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term><literal>c</literal></term>
+         <term><literal>custom</literal></term>
+         <listitem>
+          <para>
+           Output a custom-format archive suitable for input into pg_restore. Together with the
+           directory output format, this is the most flexible output format in that it allows manual
+           selection and reordering of archived items during restore. This format is also
+           compressed by default.
+          </para>
+         </listitem>
+        </varlistentry>
+
+         <varlistentry>
+         <term><literal>t</literal></term>
+         <term><literal>tar</literal></term>
+         <listitem>
+          <para>
+           Output a tar-format archive suitable for input into pg_restore. The tar format is
+           compatible with the directory format: extracting a tar-format archive produces a valid
+           directory-format archive. However, the tar format does not support compression. Also,
+           when using tar format the relative order of table data items cannot be changed during restore.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        </variablelist>
+        </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--filter=<replaceable class="parameter">filename</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index b8b27e1719..ab2e035671 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -315,6 +315,16 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-g</option></term>
+      <term><option>--globals-only</option></term>
+      <listitem>
+       <para>
+        Restore only global objects (roles and tablespaces), no databases.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-I <replaceable class="parameter">index</replaceable></option></term>
       <term><option>--index=<replaceable class="parameter">index</replaceable></option></term>
@@ -932,6 +942,26 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--exclude-database=<replaceable class="parameter">pattern</replaceable></option></term>
+      <listitem>
+       <para>
+        Do not restore databases whose name matches
+        <replaceable class="parameter">pattern</replaceable>.
+        Multiple patterns can be excluded by writing multiple
+        <option>--exclude-database</option> switches.  The
+        <replaceable class="parameter">pattern</replaceable> parameter is
+        interpreted as a pattern according to the same rules used by
+        <application>psql</application>'s <literal>\d</literal>
+        commands (see <xref linkend="app-psql-patterns"/>),
+        so multiple databases can also be excluded by writing wildcard
+        characters in the pattern.  When using wildcards, be careful to
+        quote the pattern if needed to prevent shell wildcard expansion.
+       </para>
+      </listitem>
+     </varlistentry>
+
+
     </variablelist>
    </para>
  </refsect1>
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 396f79781c..b30dbd8c3b 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -15,6 +15,7 @@
 
 #include "postgres_fe.h"
 
+#include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -29,6 +30,7 @@
 #include "filter.h"
 #include "getopt_long.h"
 #include "pg_backup.h"
+#include "pg_backup_archiver.h"
 
 /* version string we expect back from pg_dump */
 #define PGDUMP_VERSIONSTR "pg_dump (PostgreSQL) " PG_VERSION "\n"
@@ -64,9 +66,10 @@ static void dropTablespaces(PGconn *conn);
 static void dumpTablespaces(PGconn *conn);
 static void dropDBs(PGconn *conn);
 static void dumpUserConfig(PGconn *conn, const char *username);
-static void dumpDatabases(PGconn *conn);
+static void dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat);
 static void dumpTimestamp(const char *msg);
-static int	runPgDump(const char *dbname, const char *create_opts);
+static int runPgDump(const char *dbname, const char *create_opts,
+					 char *dbfile, ArchiveFormat archDumpFormat);
 static void buildShSecLabels(PGconn *conn,
 							 const char *catalog_name, Oid objectId,
 							 const char *objtype, const char *objname,
@@ -81,6 +84,7 @@ static void executeCommand(PGconn *conn, const char *query);
 static void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns,
 								   SimpleStringList *names);
 static void read_dumpall_filters(const char *filename, SimpleStringList *pattern);
+static ArchiveFormat parseDumpFormat(const char *format);
 
 static char pg_dump_bin[MAXPGPATH];
 static const char *progname;
@@ -147,6 +151,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
@@ -188,6 +193,8 @@ main(int argc, char *argv[])
 	char	   *pgdb = NULL;
 	char	   *use_role = NULL;
 	const char *dumpencoding = NULL;
+	ArchiveFormat archDumpFormat = archNull;
+	const char *formatName = "p";
 	trivalue	prompt_password = TRI_DEFAULT;
 	bool		data_only = false;
 	bool		globals_only = false;
@@ -237,7 +244,7 @@ main(int argc, char *argv[])
 
 	pgdumpopts = createPQExpBuffer();
 
-	while ((c = getopt_long(argc, argv, "acd:E:f:gh:l:Op:rsS:tU:vwWx", long_options, &optindex)) != -1)
+	while ((c = getopt_long(argc, argv, "acd:E:f:F:gh:l:Op:rsS:tU:vwWx", long_options, &optindex)) != -1)
 	{
 		switch (c)
 		{
@@ -265,7 +272,9 @@ main(int argc, char *argv[])
 				appendPQExpBufferStr(pgdumpopts, " -f ");
 				appendShellString(pgdumpopts, filename);
 				break;
-
+			case 'F':
+				formatName = pg_strdup(optarg);
+				break;
 			case 'g':
 				globals_only = true;
 				break;
@@ -414,6 +423,8 @@ main(int argc, char *argv[])
 		exit_nicely(1);
 	}
 
+	archDumpFormat = parseDumpFormat(formatName);
+
 	/*
 	 * If password values are not required in the dump, switch to using
 	 * pg_roles which is equally useful, just more likely to have unrestricted
@@ -497,9 +508,31 @@ main(int argc, char *argv[])
 						   &database_exclude_names);
 
 	/*
-	 * Open the output file if required, otherwise use stdout
+	 * Open the output file if required, otherwise use stdout.
 	 */
-	if (filename)
+	if (archDumpFormat != archNull)
+	{
+		char	toc_path[MAXPGPATH];
+
+		/*
+		 * If directory format is specified then we must provide the directory
+		 * name.
+		 */
+		if (!filename || strcmp(filename, "") == 0)
+			pg_fatal("no output directory specified");
+
+		/* TODO: accept the empty existing directory. */
+		if (mkdir(filename, 0700) < 0)
+			pg_fatal("could not create directory \"%s\": %m",
+					 filename);
+
+		snprintf(toc_path, MAXPGPATH, "%s/global.dat", filename);
+
+		OPF = fopen(toc_path, "w");
+		if (!OPF)
+			pg_fatal("could not open global.dat file: %s", strerror(errno));
+	}
+	else if (filename)
 	{
 		OPF = fopen(filename, PG_BINARY_W);
 		if (!OPF)
@@ -607,7 +640,7 @@ main(int argc, char *argv[])
 	}
 
 	if (!globals_only && !roles_only && !tablespaces_only)
-		dumpDatabases(conn);
+		dumpDatabases(conn, archDumpFormat);
 
 	PQfinish(conn);
 
@@ -620,7 +653,7 @@ main(int argc, char *argv[])
 		fclose(OPF);
 
 		/* sync the resulting file, errors are not fatal */
-		if (dosync)
+		if (dosync && (archDumpFormat == archNull))
 			(void) fsync_fname(filename, false);
 	}
 
@@ -637,6 +670,8 @@ help(void)
 
 	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"));
@@ -1487,10 +1522,13 @@ expand_dbname_patterns(PGconn *conn,
  * Dump contents of databases.
  */
 static void
-dumpDatabases(PGconn *conn)
+dumpDatabases(PGconn *conn, ArchiveFormat archDumpFormat)
 {
 	PGresult   *res;
 	int			i;
+	char		db_subdir[MAXPGPATH];
+	char		dbfilepath[MAXPGPATH];
+	FILE       *map_file;
 
 	/*
 	 * Skip databases marked not datallowconn, since we'd be unable to connect
@@ -1504,7 +1542,7 @@ dumpDatabases(PGconn *conn)
 	 * doesn't have some failure mode with --clean.
 	 */
 	res = executeQuery(conn,
-					   "SELECT datname "
+					   "SELECT datname, oid "
 					   "FROM pg_database d "
 					   "WHERE datallowconn AND datconnlimit != -2 "
 					   "ORDER BY (datname <> 'template1'), datname");
@@ -1512,9 +1550,30 @@ dumpDatabases(PGconn *conn)
 	if (PQntuples(res) > 0)
 		fprintf(OPF, "--\n-- Databases\n--\n\n");
 
+	/*
+	 * If directory format is specified then create a subdirectory under the
+	 * main directory and each database dump file will be created under the
+	 * subdirectory in archive mode as per single db pg_dump.
+	 */
+	if (archDumpFormat != archNull)
+	{
+		char    map_file_path[MAXPGPATH];
+
+		snprintf(db_subdir, MAXPGPATH, "%s/databases", filename);
+		if (mkdir(db_subdir, 0755) != 0)
+			pg_log_error("could not create subdirectory \"%s\": %m", db_subdir);
+
+		/* Create a map file (to store dboid and dbname) */
+		snprintf(map_file_path, MAXPGPATH, "%s/map.dat", filename);
+		map_file = fopen(map_file_path, "w");
+		if (!map_file)
+			pg_fatal("could not open map file: %s", strerror(errno));
+	}
+
 	for (i = 0; i < PQntuples(res); i++)
 	{
 		char	   *dbname = PQgetvalue(res, i, 0);
+		char	   *oid = PQgetvalue(res, i, 1);
 		const char *create_opts;
 		int			ret;
 
@@ -1522,6 +1581,14 @@ dumpDatabases(PGconn *conn)
 		if (strcmp(dbname, "template0") == 0)
 			continue;
 
+		if (archDumpFormat != archNull)
+		{
+			snprintf(dbfilepath, MAXPGPATH, "-f %s/%s", db_subdir, oid);
+
+			/* append dboid and dbname in map file. */
+			fprintf(map_file, "%s %s\n", oid, dbname);
+		}
+
 		/* Skip any explicitly excluded database */
 		if (simple_string_list_member(&database_exclude_names, dbname))
 		{
@@ -1531,7 +1598,8 @@ dumpDatabases(PGconn *conn)
 
 		pg_log_info("dumping database \"%s\"", dbname);
 
-		fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n", dbname);
+		if (archDumpFormat == archNull)
+			fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n", dbname);
 
 		/*
 		 * We assume that "template1" and "postgres" already exist in the
@@ -1549,20 +1617,21 @@ dumpDatabases(PGconn *conn)
 			{
 				create_opts = "";
 				/* Since pg_dump won't emit a \connect command, we must */
-				fprintf(OPF, "\\connect %s\n\n", dbname);
+				if (archDumpFormat == archNull)
+					fprintf(OPF, "\\connect %s\n\n", dbname);
 			}
 		}
 		else
 			create_opts = "--create";
 
-		if (filename)
+		if ((archDumpFormat == archNull) && filename)
 			fclose(OPF);
 
-		ret = runPgDump(dbname, create_opts);
+		ret = runPgDump(dbname, create_opts, dbfilepath, archDumpFormat);
 		if (ret != 0)
 			pg_fatal("pg_dump failed on database \"%s\", exiting", dbname);
 
-		if (filename)
+		if ((archDumpFormat == archNull) && filename)
 		{
 			OPF = fopen(filename, PG_BINARY_A);
 			if (!OPF)
@@ -1571,6 +1640,10 @@ dumpDatabases(PGconn *conn)
 		}
 	}
 
+	/* Close map file */
+	if (archDumpFormat != archNull)
+		fclose(map_file);
+
 	PQclear(res);
 }
 
@@ -1580,7 +1653,8 @@ dumpDatabases(PGconn *conn)
  * Run pg_dump on dbname, with specified options.
  */
 static int
-runPgDump(const char *dbname, const char *create_opts)
+runPgDump(const char *dbname, const char *create_opts, char *dbfile,
+		ArchiveFormat archDumpFormat)
 {
 	PQExpBufferData connstrbuf;
 	PQExpBufferData cmd;
@@ -1589,17 +1663,38 @@ 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 dump, then append file name and dump format to
+	 * the pg_dump command.
 	 */
-	if (filename)
-		appendPQExpBufferStr(&cmd, " -Fa ");
+	if (archDumpFormat != archNull)
+	{
+		printfPQExpBuffer(&cmd, "\"%s\" %s %s", pg_dump_bin,
+						  dbfile, create_opts);
+
+		if (archDumpFormat == archDirectory)
+			appendPQExpBufferStr(&cmd, " -F d ");
+		else if (archDumpFormat == archCustom)
+			appendPQExpBufferStr(&cmd, " -F c ");
+		else if (archDumpFormat == archTar)
+			appendPQExpBufferStr(&cmd, " -F t ");
+		else
+			pg_fatal("invalid dump format %d, specified, please use d/c/p only", archDumpFormat);
+	}
 	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
@@ -1994,3 +2089,30 @@ read_dumpall_filters(const char *filename, SimpleStringList *pattern)
 
 	filter_free(&fstate);
 }
+
+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("invalid dump format \"%s\" specified", format);
+
+	return archDumpFormat;
+}
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 88ae39d938..e6a70adea9 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -41,27 +41,69 @@
 #include "postgres_fe.h"
 
 #include <ctype.h>
+#include <sys/stat.h>
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
 
+#include "common/connect.h"
+#include "compress_io.h"
+#include "common/string.h"
 #include "fe_utils/option_utils.h"
+#include "fe_utils/string_utils.h"
 #include "filter.h"
 #include "getopt_long.h"
 #include "parallel.h"
+#include "pg_backup_archiver.h"
 #include "pg_backup_utils.h"
 
+typedef struct SimpleDatabaseOidListCell
+{
+	struct SimpleDatabaseOidListCell	*next;
+	Oid									db_oid;
+	const char							*db_name;
+} SimpleDatabaseOidListCell;
+
+typedef struct SimpleDatabaseOidList
+{
+	SimpleDatabaseOidListCell *head;
+	SimpleDatabaseOidListCell *tail;
+} SimpleDatabaseOidList;
+
+static void
+simple_db_oid_list_append(SimpleDatabaseOidList *list, Oid db_oid, const char *dbname);
+
 static void usage(const char *progname);
 static void read_restore_filters(const char *filename, RestoreOptions *opts);
+static bool IsFileExistsInDirectory(const char *dir, const char *filename);
+static bool restoreOneDatabase(const char *inputFileSpec,
+		RestoreOptions *opts, int numWorkers);
+static PGconn *connectDatabase(const char *dbname, const char *conn_string,
+		const char *pghost, const char *pgport, const char *pguser,
+		trivalue prompt_password, bool fail_on_error);
+static PGresult *executeQuery(PGconn *conn, const char *query);
+static int ReadOneStatement(StringInfo inBuf, FILE *f_glo);
+static int restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
+	SimpleStringList db_exclude_patterns, RestoreOptions *opts, int numWorkers);
+static void execute_global_sql_commands(PGconn *conn, const char *dumpdirpath);
+static int filter_dbnames_for_restore(PGconn *conn,
+		SimpleDatabaseOidList *dbname_oid_list, SimpleStringList db_exclude_patterns);
+static int get_dbname_oid_list_from_mfile(const char *dumpdirpath,
+		SimpleDatabaseOidList *dbname_oid_list);
+static void simple_db_oid_list_append(SimpleDatabaseOidList *list, Oid db_oid,
+		const char *dbname);
+static bool is_full_pattern(PGconn *conn, const char *str, const char *ptrn);
+static void simple_string_list_delete(SimpleStringList *list,
+		SimpleStringListCell *cell, SimpleStringListCell *prev);
+static void simple_db_oid_list_delete(SimpleDatabaseOidList *list,
+		SimpleDatabaseOidListCell *cell, SimpleDatabaseOidListCell *prev);
 
 int
 main(int argc, char **argv)
 {
 	RestoreOptions *opts;
 	int			c;
-	int			exit_code;
 	int			numWorkers = 1;
-	Archive    *AH;
 	char	   *inputFileSpec;
 	static int	disable_triggers = 0;
 	static int	enable_row_security = 0;
@@ -77,11 +119,14 @@ main(int argc, char **argv)
 	static int	strict_names = 0;
 	bool		data_only = false;
 	bool		schema_only = false;
+	SimpleStringList    db_exclude_patterns = {NULL, NULL};
+	bool				globals_only = false;
 
 	struct option cmdopts[] = {
 		{"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'},
@@ -128,6 +173,7 @@ main(int argc, char **argv)
 		{"no-security-labels", no_argument, &no_security_labels, 1},
 		{"no-subscriptions", no_argument, &no_subscriptions, 1},
 		{"filter", required_argument, NULL, 4},
+		{"exclude-database", required_argument, NULL, 6},
 
 		{NULL, 0, NULL, 0}
 	};
@@ -156,7 +202,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, "aAcCd:ef:F:gh:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1",
 							cmdopts, NULL)) != -1)
 	{
 		switch (c)
@@ -183,11 +229,14 @@ main(int argc, char **argv)
 				if (strlen(optarg) != 0)
 					opts->formatName = pg_strdup(optarg);
 				break;
+			case 'g':
+				/* restore only global.dat file from directory */
+				globals_only = true;
+				break;
 			case 'h':
 				if (strlen(optarg) != 0)
 					opts->cparams.pghost = pg_strdup(optarg);
 				break;
-
 			case 'j':			/* number of restore jobs */
 				if (!option_parse_int(optarg, "-j/--jobs", 1,
 									  PG_MAX_JOBS,
@@ -302,6 +351,10 @@ main(int argc, char **argv)
 					exit(1);
 				opts->exit_on_error = true;
 				break;
+			case 6:
+				/* list of databases patterns those needs to skip while restoring */
+				simple_string_list_append(&db_exclude_patterns, optarg);
+				break;
 
 			default:
 				/* getopt_long already emitted a complaint */
@@ -329,6 +382,16 @@ main(int argc, char **argv)
 	if (!opts->cparams.dbname && !opts->filename && !opts->tocSummary)
 		pg_fatal("one of -d/--dbname and -f/--file must be specified");
 
+	if (db_exclude_patterns.head != NULL && globals_only)
+	{
+		pg_log_error("option --exclude-database cannot be used together with -g/--globals-only");
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+		exit_nicely(1);
+	}
+
+	if (globals_only && opts->cparams.dbname == NULL)
+		pg_fatal("option -g/--globals-only requires option -d/--dbname");
+
 	/* Should get at most one of -d and -f, else user is confused */
 	if (opts->cparams.dbname)
 	{
@@ -406,6 +469,73 @@ main(int argc, char **argv)
 		}
 	}
 
+	/*
+	 * If toc.dat file does not present in current path, then check for
+	 * global.dat and map.dat files.  If both files are present, then
+	 * restore all the databases from map.dat file list and skip restoring for
+	 * --exclude-database patterns.
+	 */
+	if (inputFileSpec != NULL &&
+			!IsFileExistsInDirectory(inputFileSpec, "toc.dat"))
+	{
+		/* If global.dat and map.dat are exist, then proces them. */
+		if (IsFileExistsInDirectory(pg_strdup(inputFileSpec), "global.dat")
+				&& IsFileExistsInDirectory(pg_strdup(inputFileSpec), "map.dat"))
+		{
+			PGconn	*conn = NULL; /* Connection to restore global sql commands. */
+
+			/*
+			 * If we are restoring dump of multiple databases, then connection
+			 * should be given.
+			 */
+			if (opts->cparams.dbname == NULL)
+				pg_fatal(" -d/--dbanme should be given when using archive dump of pg_dumpall");
+
+			/*
+			 * To restore multiple databases, create database option should be
+			 * specified.
+			 */
+			if (opts->createDB != 1)
+				pg_fatal("option -C/--create should be specified when using dump of pg_dumpall");
+
+			/* Connect to database to execute global sql commands from global.dat file. */
+			conn = connectDatabase(opts->cparams.dbname, NULL, opts->cparams.pghost,
+					opts->cparams.pgport, opts->cparams.username, TRI_DEFAULT, false);
+
+			if (!conn)
+				pg_fatal("could not connect to database \"%s\"", opts->cparams.dbname);
+
+			/* Open global.dat file and execute all the sql commands. */
+			execute_global_sql_commands(conn, inputFileSpec);
+
+			/* If globals-only, then return from here. */
+			if (globals_only)
+			{
+				PQfinish(conn);
+				return 0;
+			}
+
+			/* Now restore all the databases from map.dat file. */
+			return restoreAllDatabases(conn, inputFileSpec, db_exclude_patterns,
+					opts, numWorkers);
+		}/* end if */
+	}/* end if */
+
+	return restoreOneDatabase(inputFileSpec, opts, numWorkers);
+}
+
+/*
+ * restoreOneDatabase
+ *
+ * This will restore one database using toc.dat file.
+ */
+static bool
+restoreOneDatabase(const char *inputFileSpec, RestoreOptions *opts,
+		int numWorkers)
+{
+	Archive		*AH;
+	bool		exit_code;
+
 	AH = OpenArchive(inputFileSpec, opts->format);
 
 	SetArchiveOptions(AH, NULL, opts);
@@ -471,6 +601,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"
@@ -483,6 +614,7 @@ usage(const char *progname)
 	printf(_("  -S, --superuser=NAME         superuser user name to use for disabling triggers\n"));
 	printf(_("  -t, --table=NAME             restore named relation (table, view, etc.)\n"));
 	printf(_("  -T, --trigger=NAME           restore named trigger\n"));
+	printf(_("  --exclude-database=PATTERN   exclude databases whose name matches with pattern\n"));
 	printf(_("  -x, --no-privileges          skip restoration of access privileges (grant/revoke)\n"));
 	printf(_("  -1, --single-transaction     restore as a single transaction\n"));
 	printf(_("  --disable-triggers           disable triggers during data-only restore\n"));
@@ -621,3 +753,644 @@ read_restore_filters(const char *filename, RestoreOptions *opts)
 
 	filter_free(&fstate);
 }
+
+/*
+ * IsFileExistsInDirectory
+ *
+ * Returns true if file exist in current directory.
+ */
+static bool
+IsFileExistsInDirectory(const char *dir, const char *filename)
+{
+	struct stat			st;
+	char				buf[MAXPGPATH];
+
+	if (snprintf(buf, MAXPGPATH, "%s/%s", dir, filename) >= MAXPGPATH)
+		pg_fatal("directory name too long: \"%s\"", dir);
+
+	return (stat(buf, &st) == 0 && S_ISREG(st.st_mode));
+}
+
+/*
+ * connectDatabase
+ *
+ * Make a database connection with the given parameters.  An
+ * interactive password prompt is automatically issued if required.
+ *
+ * If fail_on_error is false, we return NULL without printing any message
+ * on failure, but preserve any prompted password for the next try.
+ */
+static PGconn *
+connectDatabase(const char *dbname, const char *connection_string,
+				const char *pghost, const char *pgport, const char *pguser,
+				trivalue prompt_password, bool fail_on_error)
+{
+	PGconn				*conn;
+	bool				new_pass;
+	const char			*remoteversion_str;
+	int					my_version;
+	const char			**keywords = NULL;
+	const char			**values = NULL;
+	PQconninfoOption 	*conn_opts = NULL;
+	static char			*password = NULL;
+	static int			server_version;
+
+	if (prompt_password == TRI_YES && !password)
+		password = simple_prompt("Password: ", false);
+
+	/*
+	 * Start the connection.  Loop until we have a password if requested by
+	 * backend.
+	 */
+	do
+	{
+		int					argcount = 6;
+		PQconninfoOption	*conn_opt;
+		char				*err_msg = NULL;
+		int					i = 0;
+
+		free(keywords);
+		free(values);
+		PQconninfoFree(conn_opts);
+
+		/*
+		 * Merge the connection info inputs given in form of connection string
+		 * and other options.  Explicitly discard any dbname value in the
+		 * connection string; otherwise, PQconnectdbParams() would interpret
+		 * that value as being itself a connection string.
+		 */
+		if (connection_string)
+		{
+			conn_opts = PQconninfoParse(connection_string, &err_msg);
+			if (conn_opts == NULL)
+				pg_fatal("%s", err_msg);
+
+			for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
+			{
+				if (conn_opt->val != NULL && conn_opt->val[0] != '\0' &&
+					strcmp(conn_opt->keyword, "dbname") != 0)
+					argcount++;
+			}
+
+			keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
+			values = pg_malloc0((argcount + 1) * sizeof(*values));
+
+			for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
+			{
+				if (conn_opt->val != NULL && conn_opt->val[0] != '\0' &&
+					strcmp(conn_opt->keyword, "dbname") != 0)
+				{
+					keywords[i] = conn_opt->keyword;
+					values[i] = conn_opt->val;
+					i++;
+				}
+			}
+		}
+		else
+		{
+			keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
+			values = pg_malloc0((argcount + 1) * sizeof(*values));
+		}
+
+		if (pghost)
+		{
+			keywords[i] = "host";
+			values[i] = pghost;
+			i++;
+		}
+		if (pgport)
+		{
+			keywords[i] = "port";
+			values[i] = pgport;
+			i++;
+		}
+		if (pguser)
+		{
+			keywords[i] = "user";
+			values[i] = pguser;
+			i++;
+		}
+		if (password)
+		{
+			keywords[i] = "password";
+			values[i] = password;
+			i++;
+		}
+		if (dbname)
+		{
+			keywords[i] = "dbname";
+			values[i] = dbname;
+			i++;
+		}
+		keywords[i] = "fallback_application_name";
+		values[i] = progname;
+		i++;
+
+		new_pass = false;
+		conn = PQconnectdbParams(keywords, values, true);
+
+		if (!conn)
+			pg_fatal("could not connect to database \"%s\"", dbname);
+
+		if (PQstatus(conn) == CONNECTION_BAD &&
+			PQconnectionNeedsPassword(conn) &&
+			!password &&
+			prompt_password != TRI_NO)
+		{
+			PQfinish(conn);
+			password = simple_prompt("Password: ", false);
+			new_pass = true;
+		}
+	} while (new_pass);
+
+	/* check to see that the backend connection was successfully made */
+	if (PQstatus(conn) == CONNECTION_BAD)
+	{
+		if (fail_on_error)
+			pg_fatal("%s", PQerrorMessage(conn));
+		else
+		{
+			PQfinish(conn);
+
+			free(keywords);
+			free(values);
+			PQconninfoFree(conn_opts);
+
+			return NULL;
+		}
+	}
+
+	free(keywords);
+	free(values);
+	PQconninfoFree(conn_opts);
+
+	/* Check version */
+	remoteversion_str = PQparameterStatus(conn, "server_version");
+	if (!remoteversion_str)
+		pg_fatal("could not get server version");
+	server_version = PQserverVersion(conn);
+	if (server_version == 0)
+		pg_fatal("could not parse server version \"%s\"",
+				 remoteversion_str);
+
+	my_version = PG_VERSION_NUM;
+
+	/*
+	 * We allow the server to be back to 9.2, and up to any minor release of
+	 * our own major version.  (See also version check in pg_dump.c.)
+	 */
+	if (my_version != server_version
+		&& (server_version < 90200 ||
+			(server_version / 100) > (my_version / 100)))
+	{
+		pg_log_error("aborting because of server version mismatch");
+		pg_log_error_detail("server version: %s; %s version: %s",
+							remoteversion_str, progname, PG_VERSION);
+		exit_nicely(1);
+	}
+
+	PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL));
+
+	return conn;
+}
+
+/*
+ * executeQuery
+ *
+ * Run a query, return the results, exit program on failure.
+ */
+static PGresult *
+executeQuery(PGconn *conn, const char *query)
+{
+	PGresult	*res;
+
+	pg_log_info("executing %s", query);
+
+	res = PQexec(conn, query);
+	if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+	{
+		pg_log_error("query failed: %s", PQerrorMessage(conn));
+		pg_log_error_detail("Query was: %s", query);
+		PQfinish(conn);
+		exit_nicely(1);
+	}
+
+	return res;
+}
+
+/*
+ *	ReadOneStatement
+ *
+ * This will start reading from passed file pointer using fgetc and read till
+ * semicolon(sql statement terminator for global.sql file)
+ *
+ *	EOF is returned if end-of-file input is seen; time to shut down.
+ */
+
+static int
+ReadOneStatement(StringInfo inBuf, FILE *f_glo)
+{
+	int			c; /* character read from getc() */
+
+	resetStringInfo(inBuf);
+
+	/*
+	 * Read characters until EOF or the appropriate delimiter is seen.
+	 */
+	while ((c = fgetc(f_glo)) != EOF)
+	{
+		appendStringInfoChar(inBuf, (char) c);
+
+		if (c == '\n')
+		{
+			if(inBuf->len > 1 &&
+					inBuf->data[inBuf->len - 2] == ';')
+				break;
+			else
+				continue;
+		}
+	}
+
+	/* No input before EOF signal means time to quit. */
+	if (c == EOF && inBuf->len == 0)
+		return EOF;
+
+	/* Add '\0' to make it look the same as message case. */
+	appendStringInfoChar(inBuf, (char) '\0');
+
+	return 'Q';
+}
+
+/*
+ * filter_dbnames_for_restore
+ *
+ * This will remove names from all dblist that are given with exclude-database
+ * option.
+ *
+ * returns number of dbnames those will be restored.
+ */
+static int
+filter_dbnames_for_restore(PGconn *conn, SimpleDatabaseOidList *dbname_oid_list,
+		SimpleStringList db_exclude_patterns)
+{
+	SimpleDatabaseOidListCell	*dboid_cell = dbname_oid_list->head;
+	SimpleDatabaseOidListCell	*dboidprecell = NULL;
+	int							count_db = 0;
+
+	/* Return 0 if there is no db to restore. */
+	if (dboid_cell == NULL)
+		return 0;
+
+	/* Process one by one all dbnames and if needs to skip restoring. */
+	while (dboid_cell != NULL)
+	{
+		bool						skip_db_restore = false;
+		SimpleStringListCell		*prev = NULL;
+		SimpleDatabaseOidListCell	*next = dboid_cell->next;
+
+		/* Now match this dbname with exclude-database list. */
+		for (SimpleStringListCell *celldb = db_exclude_patterns.head; celldb; celldb = celldb->next)
+		{
+			if (is_full_pattern(conn, dboid_cell->db_name, celldb->val))
+			{
+				/*
+				 * As we need to skip this dbname so set flag to remove it from
+				 * list.
+				 */
+				skip_db_restore = true;
+
+				/*
+				 * As this pattern is skipped, now delete entry from list to
+				 * avoid multiple looping.
+				 */
+				simple_string_list_delete(&db_exclude_patterns, celldb, prev);
+				break;
+			}
+
+			prev = celldb;
+		}
+
+		/* Increment count if db needs to be restored. */
+		if (skip_db_restore)
+			simple_db_oid_list_delete(dbname_oid_list, dboid_cell, dboidprecell);
+		else
+		{
+			count_db++; /* Increment db couter. */
+			dboidprecell = dboid_cell;
+		}
+
+		/* Process next dbname from dbname list. */
+		dboid_cell = next;
+	}
+
+	return count_db;
+}
+
+/*
+ * get_dbname_oid_list_from_mfile
+ *
+ * Open map.dat file and read line by line and then prepare a list of database
+ * names and correspoding db_oid.
+ *
+ * Returns, total number of database names in map.dat file.
+ */
+static int
+get_dbname_oid_list_from_mfile(const char *dumpdirpath, SimpleDatabaseOidList *dbname_oid_list)
+{
+	FILE    *pfile;
+	char    map_file_path[MAXPGPATH];
+	char    line[MAXPGPATH];
+	int     count = 0;
+
+	snprintf(map_file_path, MAXPGPATH, "%s/map.dat", dumpdirpath);
+
+	/* Open map.dat file. */
+	pfile = fopen(map_file_path, "r");
+
+	if (pfile == NULL)
+		pg_fatal("could not open map.dat file: %s", strerror(errno));
+
+	/* Append all the dbname and db_oid to the list. */
+	while((fgets(line, MAXPGPATH, pfile)) != NULL)
+	{
+		Oid         db_oid;
+		char        dbname[MAXPGPATH + 1];
+
+		/* Extract dbname and db_oid from line */
+		sscanf(line, "%u %s" , &db_oid, dbname);
+
+		pg_log_info("found dbname as :%s and db_oid:%d in map.dat file while restoring", dbname, db_oid);
+
+		/* Report error if file has any corrupted data. */
+		if (!OidIsValid(db_oid) || strlen(dbname) == 0)
+			pg_fatal("invalid entry in map.dat file at line : %d", count + 1);
+
+		/*
+		 * XXX : before adding dbanme into list, we can verify that this db
+		 * needs to skipped for restore or not but as of now, we are making
+		 * a list of all the databases.
+		 */
+		simple_db_oid_list_append(dbname_oid_list, db_oid, dbname);
+		count++;
+	}
+
+	/* Close map.dat file. */
+	fclose(pfile);
+
+	return count;
+}
+
+/*
+ * restoreAllDatabases
+ *
+ * This will restore databases those dumps are present in
+ * directory based on map.dat file mapping.
+ *
+ * This will skip restoring for databases that are specified with
+ * exclude-database option.
+ */
+static int
+restoreAllDatabases(PGconn *conn, const char *dumpdirpath,
+		SimpleStringList db_exclude_patterns, RestoreOptions *opts,
+		int numWorkers)
+{
+	SimpleDatabaseOidList			dbname_oid_list = {NULL, NULL};
+	SimpleDatabaseOidListCell		*dboid_cell;
+	int								exit_code = 0;
+	int								num_db_restore;
+	int								num_total_db;
+
+	num_total_db = get_dbname_oid_list_from_mfile(dumpdirpath, &dbname_oid_list);
+
+	/* If map.dat has no entry, return from here. */
+	if (dbname_oid_list.head == NULL)
+		return 0;
+
+	pg_log_info("found total %d database names in map.dat file", num_total_db);
+
+	/* Skip any explicitly excluded database. */
+	num_db_restore = filter_dbnames_for_restore(conn, &dbname_oid_list,
+			db_exclude_patterns);
+
+	/* Close the db connection as we are done globals and patterns. */
+	PQfinish(conn);
+
+	/* Exit if no db needs to be restored. */
+	if (dbname_oid_list.head == NULL)
+		return 0;
+
+	pg_log_info("needs to restore %d databases out of %d databases", num_db_restore, num_total_db);
+
+	/*
+	 * XXX: TODO till now, we made a list of databases, those needs to be restored
+	 * after skipping names of exclude-database.  Now we can launch parallel
+	 * workers to restore these databases.
+	 */
+	dboid_cell = dbname_oid_list.head;
+
+	while(dboid_cell != NULL)
+	{
+		char		subdirpath[MAXPGPATH];
+		int			dbexit_code;
+
+		/*
+		 * We need to reset override_dbname so that objects can be restored into
+		 * already created database. (used with -d/--dbname option)
+		 */
+		if (opts->cparams.override_dbname)
+		{
+			pfree(opts->cparams.override_dbname);
+			opts->cparams.override_dbname = NULL;
+		}
+
+		snprintf(subdirpath, MAXPGPATH, "%s/databases/%u", dumpdirpath, dboid_cell->db_oid);
+
+		/*
+		 * Database -d/--dbname is already created so reset createDB to ignore
+		 * database creation error.
+		 */
+		if (pg_strcasecmp(dboid_cell->db_name, opts->cparams.dbname) == 0)
+			opts->createDB = 0;
+
+		pg_log_info("restoring database \"%s\"", dboid_cell->db_name);
+
+		dbexit_code = restoreOneDatabase(subdirpath, opts, numWorkers);
+
+		/* Store exit_code to report it back. */
+		if (exit_code == 0 && dbexit_code != 0)
+			exit_code = dbexit_code;
+
+		/* Set createDB option to create new database. */
+		if (pg_strcasecmp(dboid_cell->db_name, opts->cparams.dbname) == 0)
+			opts->createDB = 1;
+
+		dboid_cell = dboid_cell->next;
+	} /* end while */
+
+	/* Log number of processed databases.*/
+	pg_log_info("number of restored databases are %d", num_db_restore);
+
+	return exit_code;
+}
+
+/*
+ * execute_global_sql_commands
+ *
+ * This will open global.dat file and will execute all global sql commands one
+ * by one statement.
+ * Semicolon is considered as statement terminator.
+ */
+static void
+execute_global_sql_commands(PGconn *conn, const char *dumpdirpath)
+{
+	char            global_file_path[MAXPGPATH];
+	PGresult		*result;
+	StringInfoData	sqlstatement;
+	FILE			*pfile;
+
+	snprintf(global_file_path, MAXPGPATH, "%s/global.dat", dumpdirpath);
+
+	/* now open global.dat file */
+	pfile = fopen(global_file_path, "r");
+
+	if (pfile == NULL)
+		pg_fatal("could not open global.dat file: %s", strerror(errno));
+
+	/* Init sqlstatement to append commands */
+	initStringInfo(&sqlstatement);
+
+	/* Process file till EOF and execute sql statements */
+	while (ReadOneStatement(&sqlstatement, pfile) != EOF)
+	{
+		result = PQexec(conn, sqlstatement.data);
+
+		switch (PQresultStatus(result))
+		{
+			case PGRES_COMMAND_OK:
+			case PGRES_TUPLES_OK:
+			case PGRES_EMPTY_QUERY:
+				break;
+			default:
+				pg_log_error("could not execute query: %s \nCommand was: %s", PQerrorMessage(conn), sqlstatement.data);
+		}
+		PQclear(result);
+	}
+
+	fclose(pfile);
+}
+
+/*
+ * simple_db_oid_list_append
+ *
+ * appends a node to the list in the end.
+ */
+static void
+simple_db_oid_list_append(SimpleDatabaseOidList *list, Oid db_oid, const char *dbname)
+{
+	SimpleDatabaseOidListCell *cell;
+
+	cell = pg_malloc_object(SimpleDatabaseOidListCell);
+
+	cell->next = NULL;
+	cell->db_oid = db_oid;
+	cell->db_name = pg_strdup(dbname);
+
+	if (list->tail)
+		list->tail->next = cell;
+	else
+		list->head = cell;
+	list->tail = cell;
+}
+
+/*
+ * simple_string_list_delete
+ *
+ * delete cell from string list.
+ */
+static void
+simple_string_list_delete(SimpleStringList *list, SimpleStringListCell *cell,
+		SimpleStringListCell *prev)
+{
+	if (prev == NULL)
+	{
+		list->head = cell->next;
+		pfree(cell);
+	}
+	else
+	{
+		prev->next = cell->next;
+		pfree(cell);
+	}
+}
+
+/*
+ * simple_db_oid_list_delete
+ *
+ * delete cell from database and oid list.
+ */
+static void
+simple_db_oid_list_delete(SimpleDatabaseOidList *list, SimpleDatabaseOidListCell *cell,
+		SimpleDatabaseOidListCell *prev)
+{
+	if (prev == NULL)
+	{
+		list->head = cell->next;
+		pfree(cell);
+	}
+	else
+	{
+		prev->next = cell->next;
+		pfree(cell);
+	}
+}
+
+/*
+ * is_full_pattern
+ *
+ * Returns true if we can constuct 1st string from given pattern.
+ *
+ */
+static bool
+is_full_pattern(PGconn *conn, const char *str, const char *ptrn)
+{
+	PQExpBuffer		query;
+	PGresult		*result;
+
+	query = createPQExpBuffer();
+
+	printfPQExpBuffer(query,
+			"SELECT substring ( "
+			" '%s' , "
+			" '%s' ) ", str, ptrn);
+
+   result = executeQuery(conn, query->data);
+
+	if (PQresultStatus(result) == PGRES_TUPLES_OK)
+	{
+		if (PQntuples(result) == 1)
+		{
+			const char	*outstr;
+
+			outstr = PQgetvalue(result, 0, 0);
+
+			PQclear(result);
+			destroyPQExpBuffer(query);
+
+			/*
+			 * If output string of substring function is matches with str, then
+			 * we can construct str from pattern.
+			 */
+			if (pg_strcasecmp(outstr, str) == 0)
+				return true;
+			else
+				return false;
+		}
+	}
+	else
+		pg_log_error("could not execute query: %s \nCommand was: %s", PQerrorMessage(conn), query->data);
+
+	PQclear(result);
+	destroyPQExpBuffer(query);
+
+	return false;
+}
-- 
2.39.3

