aboutsummaryrefslogtreecommitdiff
path: root/src/bin/pg_dump
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/pg_dump')
-rw-r--r--src/bin/pg_dump/common.c12
-rw-r--r--src/bin/pg_dump/pg_backup.h3
-rw-r--r--src/bin/pg_dump/pg_backup_archiver.c7
-rw-r--r--src/bin/pg_dump/pg_dump.c464
-rw-r--r--src/bin/pg_dump/pg_dump.h46
-rw-r--r--src/bin/pg_dump/pg_dump_sort.c20
-rw-r--r--src/bin/pg_dump/pg_restore.c3
-rw-r--r--src/bin/pg_dump/t/002_pg_dump.pl76
8 files changed, 618 insertions, 13 deletions
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index d4e36421d29..89530a9f0fb 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -292,6 +292,18 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading partition key information for interesting tables\n");
getTablePartitionKeyInfo(fout, tblinfo, numTables);
+ if (g_verbose)
+ write_msg(NULL, "reading publications\n");
+ getPublications(fout);
+
+ if (g_verbose)
+ write_msg(NULL, "reading publication membership\n");
+ getPublicationTables(fout, tblinfo, numTables);
+
+ if (g_verbose)
+ write_msg(NULL, "reading subscriptions\n");
+ getSubscriptions(fout);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 7241cdfc449..6480fb8e745 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -119,6 +119,7 @@ typedef struct _restoreOptions
bool *idWanted; /* array showing which dump IDs to emit */
int enable_row_security;
int sequence_data; /* dump sequence data even in schema-only mode */
+ int include_subscriptions;
} RestoreOptions;
typedef struct _dumpOptions
@@ -152,6 +153,8 @@ typedef struct _dumpOptions
int outputNoTablespaces;
int use_setsessauth;
int enable_row_security;
+ int include_subscriptions;
+ int no_create_subscription_slots;
/* default, if no "inclusion" switches appear, is to dump everything */
bool include_everything;
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index b89bd99e49f..929f1b592bc 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -172,6 +172,7 @@ dumpOptionsFromRestoreOptions(RestoreOptions *ropt)
dopt->include_everything = ropt->include_everything;
dopt->enable_row_security = ropt->enable_row_security;
dopt->sequence_data = ropt->sequence_data;
+ dopt->include_subscriptions = ropt->include_subscriptions;
return dopt;
}
@@ -3266,6 +3267,8 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
strcmp(type, "SERVER") == 0 ||
+ strcmp(type, "PUBLICATION") == 0 ||
+ strcmp(type, "SUBSCRIPTION") == 0 ||
strcmp(type, "USER MAPPING") == 0)
{
/* We already know that search_path was set properly */
@@ -3476,7 +3479,9 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData, bool acl_pass)
strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(te->desc, "FOREIGN DATA WRAPPER") == 0 ||
- strcmp(te->desc, "SERVER") == 0)
+ strcmp(te->desc, "SERVER") == 0 ||
+ strcmp(te->desc, "PUBLICATION") == 0 ||
+ strcmp(te->desc, "SUBSCRIPTION") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 883fde1e5aa..0bb363957a4 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -246,6 +246,9 @@ static void getBlobs(Archive *fout);
static void dumpBlob(Archive *fout, BlobInfo *binfo);
static int dumpBlobs(Archive *fout, void *arg);
static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
+static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
+static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
+static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
static void dumpDatabase(Archive *AH);
static void dumpEncoding(Archive *AH);
static void dumpStdStrings(Archive *AH);
@@ -338,6 +341,7 @@ main(int argc, char **argv)
{"enable-row-security", no_argument, &dopt.enable_row_security, 1},
{"exclude-table-data", required_argument, NULL, 4},
{"if-exists", no_argument, &dopt.if_exists, 1},
+ {"include-subscriptions", no_argument, &dopt.include_subscriptions, 1},
{"inserts", no_argument, &dopt.dump_inserts, 1},
{"lock-wait-timeout", required_argument, NULL, 2},
{"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
@@ -348,6 +352,7 @@ main(int argc, char **argv)
{"snapshot", required_argument, NULL, 6},
{"strict-names", no_argument, &strict_names, 1},
{"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
+ {"no-create-subscription-slots", no_argument, &dopt.no_create_subscription_slots, 1},
{"no-security-labels", no_argument, &dopt.no_security_labels, 1},
{"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1},
{"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
@@ -849,6 +854,7 @@ main(int argc, char **argv)
ropt->include_everything = dopt.include_everything;
ropt->enable_row_security = dopt.enable_row_security;
ropt->sequence_data = dopt.sequence_data;
+ ropt->include_subscriptions = dopt.include_subscriptions;
if (compressLevel == -1)
ropt->compression = 0;
@@ -929,7 +935,10 @@ help(const char *progname)
" access to)\n"));
printf(_(" --exclude-table-data=TABLE do NOT dump data for the named table(s)\n"));
printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
+ printf(_(" --include-subscriptions dump logical replication subscriptions\n"));
printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
+ printf(_(" --no-create-subscription-slots\n"
+ " do not create replication slots for subscriptions\n"));
printf(_(" --no-security-labels do not dump security label assignments\n"));
printf(_(" --no-synchronized-snapshots do not use synchronized snapshots in parallel jobs\n"));
printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
@@ -3311,6 +3320,449 @@ dumpPolicy(Archive *fout, PolicyInfo *polinfo)
destroyPQExpBuffer(delqry);
}
+/*
+ * getPublications
+ * get information about publications
+ */
+void
+getPublications(Archive *fout)
+{
+ PQExpBuffer query;
+ PGresult *res;
+ PublicationInfo *pubinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_pubname;
+ int i_rolname;
+ int i_puballtables;
+ int i_pubinsert;
+ int i_pubupdate;
+ int i_pubdelete;
+ int i,
+ ntups;
+
+ if (fout->remoteVersion < 100000)
+ return;
+
+ query = createPQExpBuffer();
+
+ resetPQExpBuffer(query);
+
+ /* Get the publications. */
+ appendPQExpBuffer(query,
+ "SELECT p.tableoid, p.oid, p.pubname, "
+ "(%s p.pubowner) AS rolname, "
+ "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete "
+ "FROM pg_catalog.pg_publication p",
+ username_subquery);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ if (ntups == 0)
+ {
+ /*
+ * There are no publications defined. Clean up and return.
+ */
+ PQclear(res);
+ return;
+ }
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_pubname = PQfnumber(res, "pubname");
+ i_rolname = PQfnumber(res, "rolname");
+ i_puballtables = PQfnumber(res, "puballtables");
+ i_pubinsert = PQfnumber(res, "pubinsert");
+ i_pubupdate = PQfnumber(res, "pubupdate");
+ i_pubdelete = PQfnumber(res, "pubdelete");
+
+ pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ pubinfo[i].dobj.objType = DO_PUBLICATION;
+ pubinfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&pubinfo[i].dobj);
+ pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
+ pubinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ pubinfo[i].puballtables =
+ (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
+ pubinfo[i].pubinsert =
+ (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
+ pubinfo[i].pubupdate =
+ (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
+ pubinfo[i].pubdelete =
+ (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
+
+ if (strlen(pubinfo[i].rolname) == 0)
+ write_msg(NULL, "WARNING: owner of publication \"%s\" appears to be invalid\n",
+ pubinfo[i].dobj.name);
+ }
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpPublication
+ * dump the definition of the given publication
+ */
+static void
+dumpPublication(Archive *fout, PublicationInfo *pubinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer delq;
+ PQExpBuffer query;
+
+ if (dopt->dataOnly)
+ return;
+
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
+ fmtId(pubinfo->dobj.name));
+
+ appendPQExpBuffer(query, "CREATE PUBLICATION %s",
+ fmtId(pubinfo->dobj.name));
+
+ if (pubinfo->puballtables)
+ appendPQExpBufferStr(query, " FOR ALL TABLES");
+
+ appendPQExpBufferStr(query, " WITH (");
+ if (pubinfo->pubinsert)
+ appendPQExpBufferStr(query, "PUBLISH INSERT");
+ else
+ appendPQExpBufferStr(query, "NOPUBLISH INSERT");
+
+ if (pubinfo->pubupdate)
+ appendPQExpBufferStr(query, ", PUBLISH UPDATE");
+ else
+ appendPQExpBufferStr(query, ", NOPUBLISH UPDATE");
+
+ if (pubinfo->pubdelete)
+ appendPQExpBufferStr(query, ", PUBLISH DELETE");
+ else
+ appendPQExpBufferStr(query, ", NOPUBLISH DELETE");
+
+ appendPQExpBufferStr(query, ");\n");
+
+ ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
+ pubinfo->dobj.name,
+ NULL,
+ NULL,
+ pubinfo->rolname, false,
+ "PUBLICATION", SECTION_POST_DATA,
+ query->data, delq->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * getPublicationTables
+ * get information about publication membership for dumpable tables.
+ */
+void
+getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
+{
+ PQExpBuffer query;
+ PGresult *res;
+ PublicationRelInfo *pubrinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_pubname;
+ int i,
+ j,
+ ntups;
+
+ if (fout->remoteVersion < 100000)
+ return;
+
+ query = createPQExpBuffer();
+
+ for (i = 0; i < numTables; i++)
+ {
+ TableInfo *tbinfo = &tblinfo[i];
+
+ /* Only plain tables can be aded to publications. */
+ if (tbinfo->relkind != RELKIND_RELATION)
+ continue;
+
+ /*
+ * Ignore publication membership of tables whose definitions are
+ * not to be dumped.
+ */
+ if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+ continue;
+
+ if (g_verbose)
+ write_msg(NULL, "reading publication membership for table \"%s.%s\"\n",
+ tbinfo->dobj.namespace->dobj.name,
+ tbinfo->dobj.name);
+
+ resetPQExpBuffer(query);
+
+ /* Get the publication memebership for the table. */
+ appendPQExpBuffer(query,
+ "SELECT pr.tableoid, pr.oid, p.pubname "
+ "FROM pg_catalog.pg_publication_rel pr,"
+ " pg_catalog.pg_publication p "
+ "WHERE pr.prrelid = '%u'"
+ " AND p.oid = pr.prpubid",
+ tbinfo->dobj.catId.oid);
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ if (ntups == 0)
+ {
+ /*
+ * Table is not member of any publications. Clean up and return.
+ */
+ PQclear(res);
+ continue;
+ }
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_pubname = PQfnumber(res, "pubname");
+
+ pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
+
+ for (j = 0; j < ntups; j++)
+ {
+ pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
+ pubrinfo[j].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, j, i_tableoid));
+ pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
+ AssignDumpId(&pubrinfo[j].dobj);
+ pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
+ pubrinfo[j].pubname = pg_strdup(PQgetvalue(res, j, i_pubname));
+ pubrinfo[j].pubtable = tbinfo;
+ }
+ PQclear(res);
+ }
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpPublicationTable
+ * dump the definition of the given publication table mapping
+ */
+static void
+dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+ TableInfo *tbinfo = pubrinfo->pubtable;
+ PQExpBuffer query;
+ char *tag;
+
+ if (dopt->dataOnly)
+ return;
+
+ tag = psprintf("%s %s", pubrinfo->pubname, tbinfo->dobj.name);
+
+ query = createPQExpBuffer();
+
+ appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE",
+ fmtId(pubrinfo->pubname));
+ appendPQExpBuffer(query, " %s;",
+ fmtId(tbinfo->dobj.name));
+
+ /*
+ * There is no point in creating drop query as drop query as the drop
+ * is done by table drop.
+ */
+ ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
+ tag,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ "", false,
+ "PUBLICATION TABLE", SECTION_POST_DATA,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ free(tag);
+ destroyPQExpBuffer(query);
+}
+
+
+/*
+ * getSubscriptions
+ * get information about subscriptions
+ */
+void
+getSubscriptions(Archive *fout)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PGresult *res;
+ SubscriptionInfo *subinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_subname;
+ int i_rolname;
+ int i_subenabled;
+ int i_subconninfo;
+ int i_subslotname;
+ int i_subpublications;
+ int i,
+ ntups;
+
+ if (!dopt->include_subscriptions || fout->remoteVersion < 100000)
+ return;
+
+ query = createPQExpBuffer();
+
+ resetPQExpBuffer(query);
+
+ /* Get the subscriptions in current database. */
+ appendPQExpBuffer(query,
+ "SELECT s.tableoid, s.oid, s.subname,"
+ "(%s s.subowner) AS rolname, s.subenabled, "
+ " s.subconninfo, s.subslotname, s.subpublications "
+ "FROM pg_catalog.pg_subscription s "
+ "WHERE s.subdbid = (SELECT oid FROM pg_catalog.pg_database"
+ " WHERE datname = current_database())",
+ username_subquery);
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ if (ntups == 0)
+ {
+ /*
+ * There are no subscriptions defined. Clean up and return.
+ */
+ PQclear(res);
+ return;
+ }
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_subname = PQfnumber(res, "subname");
+ i_rolname = PQfnumber(res, "rolname");
+ i_subenabled = PQfnumber(res, "subenabled");
+ i_subconninfo = PQfnumber(res, "subconninfo");
+ i_subslotname = PQfnumber(res, "subslotname");
+ i_subpublications = PQfnumber(res, "subpublications");
+
+ subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
+
+ for (i = 0; i < ntups; i++)
+ {
+ subinfo[i].dobj.objType = DO_SUBSCRIPTION;
+ subinfo[i].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, i, i_tableoid));
+ subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&subinfo[i].dobj);
+ subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
+ subinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ subinfo[i].subenabled =
+ (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
+ subinfo[i].subconninfo = pg_strdup(PQgetvalue(res, i, i_subconninfo));
+ subinfo[i].subslotname = pg_strdup(PQgetvalue(res, i, i_subslotname));
+ subinfo[i].subpublications =
+ pg_strdup(PQgetvalue(res, i, i_subpublications));
+
+ if (strlen(subinfo[i].rolname) == 0)
+ write_msg(NULL, "WARNING: owner of subscription \"%s\" appears to be invalid\n",
+ subinfo[i].dobj.name);
+ }
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpSubscription
+ * dump the definition of the given subscription
+ */
+static void
+dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ PQExpBuffer publications;
+ char **pubnames = NULL;
+ int npubnames = 0;
+ int i;
+
+ if (dopt->dataOnly)
+ return;
+
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
+ fmtId(subinfo->dobj.name));
+
+ appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
+ fmtId(subinfo->dobj.name));
+ appendStringLiteralAH(query, subinfo->subconninfo, fout);
+
+ /* Build list of quoted publications and append them to query. */
+ if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
+ {
+ write_msg(NULL,
+ "WARNING: could not parse subpublications array\n");
+ if (pubnames)
+ free(pubnames);
+ pubnames = NULL;
+ npubnames = 0;
+ }
+
+ publications = createPQExpBuffer();
+ for (i = 0; i < npubnames; i++)
+ {
+ if (i > 0)
+ appendPQExpBufferStr(publications, ", ");
+
+ appendPQExpBufferStr(publications, fmtId(pubnames[i]));
+ }
+
+ appendPQExpBuffer(query, " PUBLICATION %s WITH (", publications->data);
+
+ if (subinfo->subenabled)
+ appendPQExpBufferStr(query, "ENABLED");
+ else
+ appendPQExpBufferStr(query, "DISABLED");
+
+ appendPQExpBufferStr(query, ", SLOT NAME = ");
+ appendStringLiteralAH(query, subinfo->subslotname, fout);
+
+ if (dopt->no_create_subscription_slots)
+ appendPQExpBufferStr(query, ", NOCREATE SLOT");
+
+ appendPQExpBufferStr(query, ");\n");
+
+ ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
+ subinfo->dobj.name,
+ NULL,
+ NULL,
+ subinfo->rolname, false,
+ "SUBSCRIPTION", SECTION_POST_DATA,
+ query->data, delq->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(publications);
+ if (pubnames)
+ free(pubnames);
+
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -8752,6 +9204,15 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_POLICY:
dumpPolicy(fout, (PolicyInfo *) dobj);
break;
+ case DO_PUBLICATION:
+ dumpPublication(fout, (PublicationInfo *) dobj);
+ break;
+ case DO_PUBLICATION_REL:
+ dumpPublicationTable(fout, (PublicationRelInfo *) dobj);
+ break;
+ case DO_SUBSCRIPTION:
+ dumpSubscription(fout, (SubscriptionInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -16627,6 +17088,9 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_EVENT_TRIGGER:
case DO_DEFAULT_ACL:
case DO_POLICY:
+ case DO_PUBLICATION:
+ case DO_PUBLICATION_REL:
+ case DO_SUBSCRIPTION:
/* Post-data objects: must come after the post-data boundary */
addObjectDependency(dobj, postDataBound->dumpId);
break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 0c920a39076..77de22fcb8b 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -79,7 +79,10 @@ typedef enum
DO_POST_DATA_BOUNDARY,
DO_EVENT_TRIGGER,
DO_REFRESH_MATVIEW,
- DO_POLICY
+ DO_POLICY,
+ DO_PUBLICATION,
+ DO_PUBLICATION_REL,
+ DO_SUBSCRIPTION
} DumpableObjectType;
/* component types of an object which can be selected for dumping */
@@ -567,6 +570,43 @@ typedef struct _policyInfo
} PolicyInfo;
/*
+ * The PublicationInfo struct is used to represent publications.
+ */
+typedef struct _PublicationInfo
+{
+ DumpableObject dobj;
+ char *rolname;
+ bool puballtables;
+ bool pubinsert;
+ bool pubupdate;
+ bool pubdelete;
+} PublicationInfo;
+
+/*
+ * The PublicationRelInfo struct is used to represent publication table
+ * mapping.
+ */
+typedef struct _PublicationRelInfo
+{
+ DumpableObject dobj;
+ TableInfo *pubtable;
+ char *pubname;
+} PublicationRelInfo;
+
+/*
+ * The SubscriptionInfo struct is used to represent subscription.
+ */
+typedef struct _SubscriptionInfo
+{
+ DumpableObject dobj;
+ char *rolname;
+ bool subenabled;
+ char *subconninfo;
+ char *subslotname;
+ char *subpublications;
+} SubscriptionInfo;
+
+/*
* We build an array of these with an entry for each object that is an
* extension member according to pg_depend.
*/
@@ -663,5 +703,9 @@ extern void processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);
extern void getTablePartitionKeyInfo(Archive *fout, TableInfo *tblinfo, int numTables);
+extern void getPublications(Archive *fout);
+extern void getPublicationTables(Archive *fout, TableInfo tblinfo[],
+ int numTables);
+extern void getSubscriptions(Archive *fout);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 1db680b950c..ea643397ba8 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -71,7 +71,10 @@ static const int dbObjectTypePriority[] =
26, /* DO_POST_DATA_BOUNDARY */
33, /* DO_EVENT_TRIGGER */
34, /* DO_REFRESH_MATVIEW */
- 35 /* DO_POLICY */
+ 35, /* DO_POLICY */
+ 36, /* DO_PUBLICATION */
+ 37, /* DO_PUBLICATION_REL */
+ 38 /* DO_SUBSCRIPTION */
};
static DumpId preDataBoundId;
@@ -1397,6 +1400,21 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"POLICY (ID %d OID %u)",
obj->dumpId, obj->catId.oid);
return;
+ case DO_PUBLICATION:
+ snprintf(buf, bufsize,
+ "PUBLICATION (ID %d OID %u)",
+ obj->dumpId, obj->catId.oid);
+ return;
+ case DO_PUBLICATION_REL:
+ snprintf(buf, bufsize,
+ "PUBLICATION TABLE (ID %d OID %u)",
+ obj->dumpId, obj->catId.oid);
+ return;
+ case DO_SUBSCRIPTION:
+ snprintf(buf, bufsize,
+ "SUBSCRIPTION (ID %d OID %u)",
+ obj->dumpId, obj->catId.oid);
+ return;
case DO_PRE_DATA_BOUNDARY:
snprintf(buf, bufsize,
"PRE-DATA BOUNDARY (ID %d)",
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 239b0d8ac03..497677494bc 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -72,6 +72,7 @@ main(int argc, char **argv)
char *inputFileSpec;
static int disable_triggers = 0;
static int enable_row_security = 0;
+ static int include_subscriptions = 0;
static int if_exists = 0;
static int no_data_for_failed_tables = 0;
static int outputNoTablespaces = 0;
@@ -116,6 +117,7 @@ main(int argc, char **argv)
{"disable-triggers", no_argument, &disable_triggers, 1},
{"enable-row-security", no_argument, &enable_row_security, 1},
{"if-exists", no_argument, &if_exists, 1},
+ {"include-subscriptions", no_argument, &include_subscriptions, 1},
{"no-data-for-failed-tables", no_argument, &no_data_for_failed_tables, 1},
{"no-tablespaces", no_argument, &outputNoTablespaces, 1},
{"role", required_argument, NULL, 2},
@@ -356,6 +358,7 @@ main(int argc, char **argv)
opts->disable_triggers = disable_triggers;
opts->enable_row_security = enable_row_security;
+ opts->include_subscriptions = include_subscriptions;
opts->noDataForFailedTables = no_data_for_failed_tables;
opts->noTablespace = outputNoTablespaces;
opts->use_setsessauth = use_setsessauth;
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index b732627c3a2..488eec30f57 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -301,7 +301,7 @@ my %tests = (
'ALTER FUNCTION dump_test.pltestlang_call_handler() OWNER TO' => {
all_runs => 1,
- catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs)',
+ catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs and PUBLICATIONs)',
regexp => qr/^
\QALTER FUNCTION dump_test.pltestlang_call_handler() \E
\QOWNER TO \E
@@ -358,7 +358,7 @@ my %tests = (
'ALTER PROCEDURAL LANGUAGE pltestlang OWNER TO' => {
all_runs => 1,
- catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs)',
+ catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs and PUBLICATIONs)',
regexp => qr/^ALTER PROCEDURAL LANGUAGE pltestlang OWNER TO .*;/m,
like => {
binary_upgrade => 1,
@@ -382,7 +382,7 @@ my %tests = (
'ALTER SCHEMA dump_test OWNER TO' => {
all_runs => 1,
- catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs)',
+ catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs and PUBLICATIONs)',
regexp => qr/^ALTER SCHEMA dump_test OWNER TO .*;/m,
like => {
binary_upgrade => 1,
@@ -406,7 +406,7 @@ my %tests = (
'ALTER SCHEMA dump_test_second_schema OWNER TO' => {
all_runs => 1,
- catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs)',
+ catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs and PUBLICATIONs)',
regexp => qr/^ALTER SCHEMA dump_test_second_schema OWNER TO .*;/m,
like => {
binary_upgrade => 1,
@@ -524,7 +524,7 @@ my %tests = (
'ALTER TABLE test_table OWNER TO' => {
all_runs => 1,
- catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs)',
+ catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs and PUBLICATIONs)',
regexp => qr/^ALTER TABLE test_table OWNER TO .*;/m,
like => {
binary_upgrade => 1,
@@ -577,7 +577,7 @@ my %tests = (
'ALTER TABLE test_second_table OWNER TO' => {
all_runs => 1,
- catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs)',
+ catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs and PUBLICATIONs)',
regexp => qr/^ALTER TABLE test_second_table OWNER TO .*;/m,
like => {
binary_upgrade => 1,
@@ -601,7 +601,7 @@ my %tests = (
'ALTER TABLE test_third_table OWNER TO' => {
all_runs => 1,
- catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs)',
+ catch_all => 'ALTER ... OWNER commands (except LARGE OBJECTs and PUBLICATIONs)',
regexp => qr/^ALTER TABLE test_third_table OWNER TO .*;/m,
like => {
binary_upgrade => 1,
@@ -623,10 +623,10 @@ my %tests = (
only_dump_test_table => 1,
test_schema_plus_blobs => 1, }, },
- # catch-all for ALTER ... OWNER (except LARGE OBJECTs)
- 'ALTER ... OWNER commands (except LARGE OBJECTs)' => {
+ # catch-all for ALTER ... OWNER (except LARGE OBJECTs and PUBLICATIONs)
+ 'ALTER ... OWNER commands (except LARGE OBJECTs and PUBLICATIONs)' => {
all_runs => 0, # catch-all
- regexp => qr/^ALTER (?!LARGE OBJECT)(.*) OWNER TO .*;/m,
+ regexp => qr/^ALTER (?!LARGE OBJECT|PUBLICATION)(.*) OWNER TO .*;/m,
like => {}, # use more-specific options above
unlike => {
column_inserts => 1,
@@ -2217,6 +2217,62 @@ my %tests = (
pg_dumpall_globals_clean => 1,
role => 1,
section_pre_data => 1, }, },
+
+ 'CREATE PUBLICATION pub1' => {
+ create_order => 50,
+ create_sql => 'CREATE PUBLICATION pub1;',
+ regexp => qr/^
+ \QCREATE PUBLICATION pub1 WITH (PUBLISH INSERT, PUBLISH UPDATE, PUBLISH DELETE);\E
+ /xm,
+ like => {
+ binary_upgrade => 1,
+ clean => 1,
+ clean_if_exists => 1,
+ createdb => 1,
+ defaults => 1,
+ exclude_test_table_data => 1,
+ exclude_dump_test_schema => 1,
+ exclude_test_table => 1,
+ no_privs => 1,
+ no_owner => 1,
+ only_dump_test_schema => 1,
+ only_dump_test_table => 1,
+ pg_dumpall_dbprivs => 1,
+ schema_only => 1,
+ section_post_data => 1,
+ test_schema_plus_blobs => 1, },
+ unlike => {
+ section_pre_data => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1, }, },
+ 'ALTER PUBLICATION pub1 ADD TABLE test_table' => {
+ create_order => 51,
+ create_sql => 'ALTER PUBLICATION pub1 ADD TABLE dump_test.test_table;',
+ regexp => qr/^
+ \QALTER PUBLICATION pub1 ADD TABLE test_table;\E
+ /xm,
+ like => {
+ binary_upgrade => 1,
+ clean => 1,
+ clean_if_exists => 1,
+ createdb => 1,
+ defaults => 1,
+ exclude_test_table_data => 1,
+ no_privs => 1,
+ no_owner => 1,
+ only_dump_test_schema => 1,
+ only_dump_test_table => 1,
+ pg_dumpall_dbprivs => 1,
+ schema_only => 1,
+ section_post_data => 1,
+ test_schema_plus_blobs => 1, },
+ unlike => {
+ section_pre_data => 1,
+ exclude_dump_test_schema => 1,
+ exclude_test_table => 1,
+ pg_dumpall_globals => 1,
+ pg_dumpall_globals_clean => 1, }, },
+
'CREATE SCHEMA dump_test' => {
all_runs => 1,
catch_all => 'CREATE ... commands',