diff --git a/doc/src/sgml/ref/alter_publication.sgml b/doc/src/sgml/ref/alter_publication.sgml
index 028770f2149..7f0e46380cc 100644
--- a/doc/src/sgml/ref/alter_publication.sgml
+++ b/doc/src/sgml/ref/alter_publication.sgml
@@ -22,8 +22,8 @@ PostgreSQL documentation
ALTER PUBLICATION name ADD publication_object [, ...]
-ALTER PUBLICATION name SET publication_object [, ...]
ALTER PUBLICATION name DROP publication_drop_object [, ...]
+ALTER PUBLICATION name SET { publication_object [, ...] | publication_all_object [, ... ] }
ALTER PUBLICATION name SET ( publication_parameter [= value] [, ... ] )
ALTER PUBLICATION name OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
ALTER PUBLICATION name RENAME TO new_name
@@ -33,6 +33,11 @@ ALTER PUBLICATION name RENAME TO table_and_columns [, ... ]
TABLES IN SCHEMA { schema_name | CURRENT_SCHEMA } [, ... ]
+and publication_all_object is one of:
+
+ ALL TABLES [ EXCEPT TABLE ( except_table_object [, ... ] ) ]
+ ALL SEQUENCES
+
and publication_drop_object is one of:
TABLE [ ONLY ] table_name [ * ] [, ... ]
@@ -41,6 +46,10 @@ ALTER PUBLICATION name RENAME TO and table_and_columns is:
[ ONLY ] table_name [ * ] [ ( column_name [, ... ] ) ] [ WHERE ( expression ) ]
+
+and except_table_object is:
+
+ [ ONLY ] table_name [ * ]
@@ -53,18 +62,46 @@ ALTER PUBLICATION name RENAME TO
- The first three variants change which tables/schemas are part of the
- publication. The SET clause will replace the list of
- tables/schemas in the publication with the specified list; the existing
- tables/schemas that were present in the publication will be removed. The
- ADD and DROP clauses will add and
- remove one or more tables/schemas from the publication. Note that adding
- tables/schemas to a publication that is already subscribed to will require an
+ The first two variants modify which tables/schemas are part of the
+ publication. The ADD and DROP clauses
+ will add and remove one or more tables/schemas from the publication.
+
+
+
+ The third variant either modifies the included tables/schemas
+ or marks the publication as FOR ALL SEQUENCES or
+ FOR ALL TABLES, optionally using
+ EXCEPT TABLE to exclude specific tables. The
+ SET ALL TABLES clause can transform an empty publication,
+ or one defined for ALL SEQUENCES (or both
+ ALL TABLES and ALL SEQUENCES), into
+ a publication defined for ALL TABLES. Likewise,
+ SET ALL SEQUENCES can convert an empty publication, or
+ one defined for ALL TABLES (or both
+ ALL TABLES and ALL SEQUENCES), into a
+ publication defined for ALL SEQUENCES. In addition,
+ SET ALL TABLES can be used to update the
+ EXCEPT TABLE list of a FOR ALL TABLES
+ publication. If EXCEPT TABLE is specified with a list of
+ tables, the existing exclusion list is replaced with the specified tables.
+ If EXCEPT TABLE is omitted, the existing exclusion list
+ is cleared. The SET clause, when used with a publication
+ defined with FOR TABLE or
+ FOR TABLES IN SCHEMA, replaces the list of tables/schemas
+ in the publication with the specified list; the existing tables or schemas
+ that were present in the publication will be removed.
+
+
+
+ Note that adding tables/except tables/schemas to a publication that is
+ already subscribed to will require an
- ALTER SUBSCRIPTION ... REFRESH PUBLICATION action on the
- subscribing side in order to become effective. Note also that
- DROP TABLES IN SCHEMA will not drop any schema tables
- that were specified using
+ ALTER SUBSCRIPTION ... REFRESH PUBLICATION action
+ on the subscribing side in order to become effective. Likewise altering a
+ publication to set ALL TABLES or to set or unset
+ ALL SEQUENCES also requires the subscriber to refresh the
+ publication. Note also that DROP TABLES IN SCHEMA will
+ not drop any schema tables that were specified using
FOR TABLE/
ADD TABLE.
@@ -83,8 +120,9 @@ ALTER PUBLICATION name RENAME TO
You must own the publication to use ALTER PUBLICATION.
Adding a table to a publication additionally requires owning that table.
- The ADD TABLES IN SCHEMA and
- SET TABLES IN SCHEMA to a publication requires the
+ The ADD TABLES IN SCHEMA,
+ SET TABLES IN SCHEMA, SET ALL TABLES,
+ and SET ALL SEQUENCES to a publication requires the
invoking user to be a superuser.
To alter the owner, you must be able to SET ROLE to the
new owning role, and that role must have CREATE
@@ -222,6 +260,19 @@ ALTER PUBLICATION mypublication ADD TABLE users (user_id, firstname), department
Change the set of columns published for a table:
ALTER PUBLICATION mypublication SET TABLE users (user_id, firstname, lastname), TABLE departments;
+
+
+
+ Replace the publication's EXCEPT TABLE list:
+
+ALTER PUBLICATION mypublication SET ALL TABLES EXCEPT TABLE (users, departments);
+
+
+
+ Reset the publication to be a FOR ALL TABLES publication with no excluded
+ tables:
+
+ALTER PUBLICATION mypublication SET ALL TABLES;
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index a79157c43bf..c92ff3f51c3 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -270,6 +270,49 @@ is_schema_publication(Oid pubid)
return result;
}
+/*
+ * Returns true if the publication has explicitly included relation (i.e.,
+ * not marked as EXCEPT).
+ */
+bool
+is_table_publication(Oid pubid)
+{
+ Relation pubrelsrel;
+ ScanKeyData scankey;
+ SysScanDesc scan;
+ HeapTuple tup;
+ bool result = false;
+
+ pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
+ ScanKeyInit(&scankey,
+ Anum_pg_publication_rel_prpubid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(pubid));
+
+ scan = systable_beginscan(pubrelsrel,
+ PublicationRelPrpubidIndexId,
+ true, NULL, 1, &scankey);
+ tup = systable_getnext(scan);
+ if (HeapTupleIsValid(tup))
+ {
+ Form_pg_publication_rel pubrel;
+
+ pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
+
+ /*
+ * For any publication, pg_publication_rel contains either only EXCEPT
+ * entries or only explicitly included tables. Therefore, examining
+ * the first tuple is sufficient to determine table inclusion.
+ */
+ result = !pubrel->prexcept;
+ }
+
+ systable_endscan(scan);
+ table_close(pubrelsrel, AccessShareLock);
+
+ return result;
+}
+
/*
* Returns true if the relation has column list associated with the
* publication, false otherwise.
@@ -440,7 +483,7 @@ attnumstoint2vector(Bitmapset *attrs)
*/
ObjectAddress
publication_add_relation(Oid pubid, PublicationRelInfo *pri,
- bool if_not_exists)
+ bool if_not_exists, AlterPublicationStmt *alter_stmt)
{
Relation rel;
HeapTuple tup;
@@ -455,6 +498,7 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
referenced;
List *relids = NIL;
int i;
+ bool inval_except_table;
rel = table_open(PublicationRelRelationId, RowExclusiveLock);
@@ -543,11 +587,23 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
table_close(rel, RowExclusiveLock);
/*
- * Relations excluded via the EXCEPT clause do not need explicit
- * invalidation as CreatePublication() function invalidates all relations
- * as part of defining a FOR ALL TABLES publication.
+ * Determine whether EXCEPT tables require explicit relcache invalidation.
+ *
+ * For CREATE PUBLICATION with EXCEPT tables, invalidation is skipped
+ * here, as CreatePublication() function invalidates all relations as part
+ * of defining a FOR ALL TABLES publication.
+ *
+ * For ALTER PUBLICATION, invalidation is needed only when adding an
+ * EXCEPT table to a publication already marked as ALL TABLES. For
+ * publications that were originally empty or defined as ALL SEQUENCES and
+ * are being converted to ALL TABLES, invalidation is skipped here, as
+ * AlterPublicationAllFlags() function invalidates all relations while
+ * marking the publication as ALL TABLES publication.
*/
- if (!pri->except)
+ inval_except_table = (alter_stmt != NULL) && pub->alltables &&
+ (alter_stmt->for_all_tables && pri->except);
+
+ if (!pri->except || inval_except_table)
{
/*
* Invalidate relcache so that publication info is rebuilt.
diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index 6a3ca4751fa..9fb80fdff08 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -1272,15 +1272,37 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
PublicationDropTables(pubid, rels, false);
else /* AP_SetObjects */
{
- List *oldrelids = GetIncludedPublicationRelations(pubid,
- PUBLICATION_PART_ROOT);
+ List *oldrelids = NIL;
List *delrels = NIL;
ListCell *oldlc;
- TransformPubWhereClauses(rels, queryString, pubform->pubviaroot);
+ if (stmt->for_all_tables || stmt->for_all_sequences)
+ {
+ /*
+ * In FOR ALL TABLES mode, relations are tracked as exclusions
+ * (EXCEPT TABLES). Fetch the current excluded relations so they
+ * can be reconciled with the specified EXCEPT list.
+ *
+ * This applies only if the existing publication is already
+ * defined as FOR ALL TABLES; otherwise, there are no exclusion
+ * entries to process.
+ */
+ if (pubform->puballtables)
+ {
+ oldrelids = GetExcludedPublicationTables(pubid,
+ PUBLICATION_PART_ROOT);
+ }
+ }
+ else
+ {
+ oldrelids = GetIncludedPublicationRelations(pubid,
+ PUBLICATION_PART_ROOT);
- CheckPubRelationColumnList(stmt->pubname, rels, publish_schema,
- pubform->pubviaroot);
+ TransformPubWhereClauses(rels, queryString, pubform->pubviaroot);
+
+ CheckPubRelationColumnList(stmt->pubname, rels, publish_schema,
+ pubform->pubviaroot);
+ }
/*
* To recreate the relation list for the publication, look for
@@ -1498,6 +1520,16 @@ CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to add or set schemas")));
+ if (stmt->for_all_tables && !superuser())
+ ereport(ERROR,
+ errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to set ALL TABLES"));
+
+ if (stmt->for_all_sequences && !superuser())
+ ereport(ERROR,
+ errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to set ALL SEQUENCES"));
+
/*
* Check that user is allowed to manipulate the publication tables in
* schema
@@ -1546,6 +1578,73 @@ CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
NameStr(pubform->pubname)),
errdetail("Tables or sequences cannot be added to or dropped from FOR ALL SEQUENCES publications."));
}
+
+ if (stmt->for_all_tables || stmt->for_all_sequences)
+ {
+ /*
+ * If the publication already contains specific tables or schemas, we
+ * prevent switching to a ALL state.
+ */
+ if (is_table_publication(pubform->oid) ||
+ is_schema_publication(pubform->oid))
+ {
+ ereport(ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ stmt->for_all_tables ?
+ errmsg("publication \"%s\" does not support ALL TABLES operations", NameStr(pubform->pubname)) :
+ errmsg("publication \"%s\" does not support ALL SEQUENCES operations", NameStr(pubform->pubname)),
+ errdetail("This operation requires the publication to be defined as FOR ALL TABLES/SEQUENCES or to be empty."));
+ }
+ }
+}
+
+/*
+ * Update FOR ALL TABLES / FOR ALL SEQUENCES flags of a publication.
+ */
+static void
+AlterPublicationAllFlags(AlterPublicationStmt *stmt, Relation rel,
+ HeapTuple tup)
+{
+ Form_pg_publication pubform;
+ bool nulls[Natts_pg_publication] = {0};
+ bool replaces[Natts_pg_publication] = {0};
+ Datum values[Natts_pg_publication] = {0};
+ bool dirty = false;
+
+ if (!stmt->for_all_tables && !stmt->for_all_sequences)
+ return;
+
+ pubform = (Form_pg_publication) GETSTRUCT(tup);
+
+ /* Update FOR ALL TABLES flag if changed */
+ if (stmt->for_all_tables != pubform->puballtables)
+ {
+ values[Anum_pg_publication_puballtables - 1] =
+ BoolGetDatum(stmt->for_all_tables);
+ replaces[Anum_pg_publication_puballtables - 1] = true;
+ dirty = true;
+ }
+
+ /* Update FOR ALL SEQUENCES flag if changed */
+ if (stmt->for_all_sequences != pubform->puballsequences)
+ {
+ values[Anum_pg_publication_puballsequences - 1] =
+ BoolGetDatum(stmt->for_all_sequences);
+ replaces[Anum_pg_publication_puballsequences - 1] = true;
+ dirty = true;
+ }
+
+ if (dirty)
+ {
+ tup = heap_modify_tuple(tup, RelationGetDescr(rel), values,
+ nulls, replaces);
+ CatalogTupleUpdate(rel, &tup->t_self, tup);
+ CommandCounterIncrement();
+
+ /* For ALL TABLES, we must invalidate all relcache entries */
+ if (replaces[Anum_pg_publication_puballtables - 1])
+ CacheInvalidateRelcacheAll();
+ }
}
/*
@@ -1591,9 +1690,6 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
&exceptrelations, &schemaidlist);
- /* EXCEPT clause is not supported with ALTER PUBLICATION */
- Assert(exceptrelations == NIL);
-
CheckAlterPublication(stmt, tup, relations, schemaidlist);
heap_freetuple(tup);
@@ -1615,9 +1711,11 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
errmsg("publication \"%s\" does not exist",
stmt->pubname));
+ relations = list_concat(relations, exceptrelations);
AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext,
schemaidlist != NIL);
AlterPublicationSchemas(stmt, tup, schemaidlist);
+ AlterPublicationAllFlags(stmt, rel, tup);
}
/* Cleanup. */
@@ -1953,7 +2051,7 @@ PublicationAddTables(Oid pubid, List *rels, bool if_not_exists,
aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
RelationGetRelationName(rel));
- obj = publication_add_relation(pubid, pub_rel, if_not_exists);
+ obj = publication_add_relation(pubid, pub_rel, if_not_exists, stmt);
if (stmt)
{
EventTriggerCollectSimpleCommand(obj, InvalidObjectAddress,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 67e42e5df29..8d55acb64a2 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -20614,7 +20614,8 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
list_length(exceptpuboids),
RelationGetRelationName(attachrel),
pubnames.data),
- errdetail("The publication EXCEPT clause cannot contain tables that are partitions."));
+ errdetail("The publication EXCEPT clause cannot contain tables that are partitions."),
+ errhint("Change the publication's EXCEPT clause using ALTER PUBLICATION ... SET ALL TABLES."));
}
list_free(exceptpuboids);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0b4a4911370..92ded5307b3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11445,11 +11445,18 @@ pub_except_obj_list: PublicationExceptObjSpec
*
* ALTER PUBLICATION name SET pub_obj [, ...]
*
+ * ALTER PUBLICATION name SET pub_all_obj_type [, ...]
+ *
* pub_obj is one of:
*
* TABLE table_name [, ...]
* TABLES IN SCHEMA schema_name [, ...]
*
+ * pub_all_obj_type is one of:
+ *
+ * ALL TABLES [ EXCEPT TABLE ( table_name [, ...] ) ]
+ * ALL SEQUENCES
+ *
*****************************************************************************/
AlterPublicationStmt:
@@ -11459,6 +11466,7 @@ AlterPublicationStmt:
n->pubname = $3;
n->options = $5;
+ n->for_all_tables = false;
$$ = (Node *) n;
}
| ALTER PUBLICATION name ADD_P pub_obj_list
@@ -11469,6 +11477,7 @@ AlterPublicationStmt:
n->pubobjects = $5;
preprocess_pubobj_list(n->pubobjects, yyscanner);
n->action = AP_AddObjects;
+ n->for_all_tables = false;
$$ = (Node *) n;
}
| ALTER PUBLICATION name SET pub_obj_list
@@ -11479,6 +11488,19 @@ AlterPublicationStmt:
n->pubobjects = $5;
preprocess_pubobj_list(n->pubobjects, yyscanner);
n->action = AP_SetObjects;
+ n->for_all_tables = false;
+ $$ = (Node *) n;
+ }
+ | ALTER PUBLICATION name SET pub_all_obj_type_list
+ {
+ AlterPublicationStmt *n = makeNode(AlterPublicationStmt);
+
+ n->pubname = $3;
+ n->action = AP_SetObjects;
+ preprocess_pub_all_objtype_list($5, &n->pubobjects,
+ &n->for_all_tables,
+ &n->for_all_sequences,
+ yyscanner);
$$ = (Node *) n;
}
| ALTER PUBLICATION name DROP pub_obj_list
@@ -11489,6 +11511,7 @@ AlterPublicationStmt:
n->pubobjects = $5;
preprocess_pubobj_list(n->pubobjects, yyscanner);
n->action = AP_DropObjects;
+ n->for_all_tables = false;
$$ = (Node *) n;
}
;
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 5bdbf1530a2..2f674764cad 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -2329,7 +2329,18 @@ match_previous_words(int pattern_id,
COMPLETE_WITH("TABLES IN SCHEMA", "TABLE");
/* ALTER PUBLICATION SET */
else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET"))
- COMPLETE_WITH("(", "TABLES IN SCHEMA", "TABLE");
+ COMPLETE_WITH("(", "ALL SEQUENCES", "ALL TABLES", "TABLES IN SCHEMA", "TABLE");
+ else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET", "ALL"))
+ COMPLETE_WITH("SEQUENCES", "TABLES");
+ else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET", "ALL", "TABLES"))
+ COMPLETE_WITH("EXCEPT TABLE (");
+ else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET", "ALL", "TABLES", "EXCEPT"))
+ COMPLETE_WITH("TABLE (");
+ else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET", "ALL", "TABLES", "EXCEPT", "TABLE"))
+ COMPLETE_WITH("(");
+ /* Complete "ALTER PUBLICATION FOR TABLE" with "
, ..." */
+ else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET", "ALL", "TABLES", "EXCEPT", "TABLE", "("))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "TABLES", "IN", "SCHEMA"))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
" AND nspname NOT LIKE E'pg\\\\_%%'",
diff --git a/src/include/catalog/pg_publication.h b/src/include/catalog/pg_publication.h
index e25228713e7..89b4bb14f62 100644
--- a/src/include/catalog/pg_publication.h
+++ b/src/include/catalog/pg_publication.h
@@ -195,10 +195,12 @@ extern Oid GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
extern bool is_publishable_relation(Relation rel);
extern bool is_schema_publication(Oid pubid);
+extern bool is_table_publication(Oid pubid);
extern bool check_and_fetch_column_list(Publication *pub, Oid relid,
MemoryContext mcxt, Bitmapset **cols);
extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri,
- bool if_not_exists);
+ bool if_not_exists,
+ AlterPublicationStmt *alter_stmt);
extern Bitmapset *pub_collist_validate(Relation targetrel, List *columns);
extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid,
bool if_not_exists);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4e1ea9d1e8e..df431220ac5 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -4525,6 +4525,8 @@ typedef struct AlterPublicationStmt
List *pubobjects; /* Optional list of publication objects */
AlterPublicationAction action; /* What action to perform with the given
* objects */
+ bool for_all_tables; /* True if ALL TABLES is specified */
+ bool for_all_sequences; /* True if ALL SEQUENCES is specified */
} AlterPublicationStmt;
typedef struct CreateSubscriptionStmt
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index 681d2564ed5..a220f48b285 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -254,9 +254,84 @@ Except Publications:
"testpub_foralltables_excepttable"
"testpub_foralltables_excepttable1"
+---------------------------------------------
+-- SET ALL TABLES/SEQUENCES
+---------------------------------------------
+-- Replace the existing EXCEPT TABLE list (testpub_tbl1) with a new
+-- EXCEPT TABLE list containing only (testpub_tbl2).
+ALTER PUBLICATION testpub_foralltables_excepttable SET ALL TABLES EXCEPT TABLE (testpub_tbl2);
+\dRp+ testpub_foralltables_excepttable
+ Publication testpub_foralltables_excepttable
+ Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description
+--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
+ regress_publication_user | t | f | t | t | t | t | none | f |
+Except tables:
+ "public.testpub_tbl2"
+
+-- Clear the EXCEPT TABLE list, making the publication include all tables.
+ALTER PUBLICATION testpub_foralltables_excepttable SET ALL TABLES;
+\dRp+ testpub_foralltables_excepttable
+ Publication testpub_foralltables_excepttable
+ Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description
+--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
+ regress_publication_user | t | f | t | t | t | t | none | f |
+(1 row)
+
+-- Create an empty publication for subsequent tests.
+CREATE PUBLICATION testpub_forall_tbls_seqs;
+-- Enable both puballtables and puballsequences
+ALTER PUBLICATION testpub_forall_tbls_seqs SET ALL TABLES, ALL SEQUENCES;
+\dRp+ testpub_forall_tbls_seqs
+ Publication testpub_forall_tbls_seqs
+ Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description
+--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
+ regress_publication_user | t | t | t | t | t | t | none | f |
+(1 row)
+
+-- Explicitly test that SET ALL TABLES resets puballsequences to false
+-- Result should be: puballtables = true, puballsequences = false
+ALTER PUBLICATION testpub_forall_tbls_seqs SET ALL TABLES;
+\dRp+ testpub_forall_tbls_seqs
+ Publication testpub_forall_tbls_seqs
+ Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description
+--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
+ regress_publication_user | t | f | t | t | t | t | none | f |
+(1 row)
+
+-- Explicitly test that SET ALL SEQUENCES resets puballtables to false
+-- Result should be: puballtables = false, puballsequences = true
+ALTER PUBLICATION testpub_forall_tbls_seqs SET ALL SEQUENCES;
+\dRp+ testpub_forall_tbls_seqs
+ Publication testpub_forall_tbls_seqs
+ Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description
+--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
+ regress_publication_user | f | t | t | t | t | t | none | f |
+(1 row)
+
+-- fail - SET ALL TABLES/SEQUENCES is not allowed for a 'FOR TABLE' publication
+ALTER PUBLICATION testpub_fortable SET ALL TABLES EXCEPT TABLE (testpub_tbl1);
+ERROR: publication "testpub_fortable" does not support ALL TABLES operations
+DETAIL: This operation requires the publication to be defined as FOR ALL TABLES/SEQUENCES or to be empty.
+ALTER PUBLICATION testpub_fortable SET ALL TABLES;
+ERROR: publication "testpub_fortable" does not support ALL TABLES operations
+DETAIL: This operation requires the publication to be defined as FOR ALL TABLES/SEQUENCES or to be empty.
+ALTER PUBLICATION testpub_fortable SET ALL SEQUENCES;
+ERROR: publication "testpub_fortable" does not support ALL SEQUENCES operations
+DETAIL: This operation requires the publication to be defined as FOR ALL TABLES/SEQUENCES or to be empty.
+-- fail - SET ALL TABLES/SEQUENCES is not allowed for a schema publication
+ALTER PUBLICATION testpub_forschema SET ALL TABLES EXCEPT TABLE (pub_test.testpub_nopk);
+ERROR: publication "testpub_forschema" does not support ALL TABLES operations
+DETAIL: This operation requires the publication to be defined as FOR ALL TABLES/SEQUENCES or to be empty.
+ALTER PUBLICATION testpub_forschema SET ALL TABLES;
+ERROR: publication "testpub_forschema" does not support ALL TABLES operations
+DETAIL: This operation requires the publication to be defined as FOR ALL TABLES/SEQUENCES or to be empty.
+ALTER PUBLICATION testpub_forschema SET ALL SEQUENCES;
+ERROR: publication "testpub_forschema" does not support ALL SEQUENCES operations
+DETAIL: This operation requires the publication to be defined as FOR ALL TABLES/SEQUENCES or to be empty.
RESET client_min_messages;
DROP TABLE testpub_tbl2;
-DROP PUBLICATION testpub_foralltables, testpub_fortable, testpub_forschema, testpub_for_tbl_schema, testpub_foralltables_excepttable, testpub_foralltables_excepttable1;
+DROP PUBLICATION testpub_foralltables, testpub_fortable, testpub_forschema, testpub_for_tbl_schema;
+DROP PUBLICATION testpub_forall_tbls_seqs, testpub_foralltables_excepttable, testpub_foralltables_excepttable1;
---------------------------------------------
-- Tests for inherited tables, and
-- EXCEPT TABLE tests for inherited tables
@@ -361,6 +436,7 @@ CREATE TABLE tab_main (a int) PARTITION BY RANGE(a);
ALTER TABLE tab_main ATTACH PARTITION testpub_root FOR VALUES FROM (0) TO (200);
ERROR: cannot attach table "testpub_root" as partition because it is referenced in publication "testpub8" EXCEPT clause
DETAIL: The publication EXCEPT clause cannot contain tables that are partitions.
+HINT: Change the publication's EXCEPT clause using ALTER PUBLICATION ... SET ALL TABLES.
RESET client_min_messages;
DROP TABLE testpub_root, testpub_part1, tab_main;
DROP PUBLICATION testpub8;
@@ -1541,7 +1617,20 @@ ERROR: permission denied to change owner of publication "testpub4"
HINT: The owner of a FOR ALL TABLES or ALL SEQUENCES or TABLES IN SCHEMA publication must be a superuser.
ALTER PUBLICATION testpub4 owner to regress_publication_user; -- ok
SET ROLE regress_publication_user;
-DROP PUBLICATION testpub4;
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub5 FOR ALL TABLES;
+RESET client_min_messages;
+ALTER PUBLICATION testpub5 OWNER TO regress_publication_user3;
+SET ROLE regress_publication_user3;
+-- fail - SET ALL TABLES/SEQUENCES on a publication requires superuser privileges
+ALTER PUBLICATION testpub5 SET ALL TABLES EXCEPT TABLE (testpub_tbl1); -- fail
+ERROR: must be superuser to set ALL TABLES
+ALTER PUBLICATION testpub5 SET ALL TABLES; -- fail
+ERROR: must be superuser to set ALL TABLES
+ALTER PUBLICATION testpub5 SET ALL SEQUENCES; -- fail
+ERROR: must be superuser to set ALL SEQUENCES
+SET ROLE regress_publication_user;
+DROP PUBLICATION testpub4, testpub5;
DROP ROLE regress_publication_user3;
REVOKE CREATE ON DATABASE regression FROM regress_publication_user2;
DROP TABLE testpub_parted;
diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql
index 405579dad52..22e0a30b5c7 100644
--- a/src/test/regress/sql/publication.sql
+++ b/src/test/regress/sql/publication.sql
@@ -119,9 +119,49 @@ CREATE PUBLICATION testpub_foralltables_excepttable1 FOR ALL TABLES EXCEPT TABLE
-- in the EXCEPT TABLE clause
\d testpub_tbl1
+---------------------------------------------
+-- SET ALL TABLES/SEQUENCES
+---------------------------------------------
+-- Replace the existing EXCEPT TABLE list (testpub_tbl1) with a new
+-- EXCEPT TABLE list containing only (testpub_tbl2).
+ALTER PUBLICATION testpub_foralltables_excepttable SET ALL TABLES EXCEPT TABLE (testpub_tbl2);
+\dRp+ testpub_foralltables_excepttable
+
+-- Clear the EXCEPT TABLE list, making the publication include all tables.
+ALTER PUBLICATION testpub_foralltables_excepttable SET ALL TABLES;
+\dRp+ testpub_foralltables_excepttable
+
+-- Create an empty publication for subsequent tests.
+CREATE PUBLICATION testpub_forall_tbls_seqs;
+
+-- Enable both puballtables and puballsequences
+ALTER PUBLICATION testpub_forall_tbls_seqs SET ALL TABLES, ALL SEQUENCES;
+\dRp+ testpub_forall_tbls_seqs
+
+-- Explicitly test that SET ALL TABLES resets puballsequences to false
+-- Result should be: puballtables = true, puballsequences = false
+ALTER PUBLICATION testpub_forall_tbls_seqs SET ALL TABLES;
+\dRp+ testpub_forall_tbls_seqs
+
+-- Explicitly test that SET ALL SEQUENCES resets puballtables to false
+-- Result should be: puballtables = false, puballsequences = true
+ALTER PUBLICATION testpub_forall_tbls_seqs SET ALL SEQUENCES;
+\dRp+ testpub_forall_tbls_seqs
+
+-- fail - SET ALL TABLES/SEQUENCES is not allowed for a 'FOR TABLE' publication
+ALTER PUBLICATION testpub_fortable SET ALL TABLES EXCEPT TABLE (testpub_tbl1);
+ALTER PUBLICATION testpub_fortable SET ALL TABLES;
+ALTER PUBLICATION testpub_fortable SET ALL SEQUENCES;
+
+-- fail - SET ALL TABLES/SEQUENCES is not allowed for a schema publication
+ALTER PUBLICATION testpub_forschema SET ALL TABLES EXCEPT TABLE (pub_test.testpub_nopk);
+ALTER PUBLICATION testpub_forschema SET ALL TABLES;
+ALTER PUBLICATION testpub_forschema SET ALL SEQUENCES;
+
RESET client_min_messages;
DROP TABLE testpub_tbl2;
-DROP PUBLICATION testpub_foralltables, testpub_fortable, testpub_forschema, testpub_for_tbl_schema, testpub_foralltables_excepttable, testpub_foralltables_excepttable1;
+DROP PUBLICATION testpub_foralltables, testpub_fortable, testpub_forschema, testpub_for_tbl_schema;
+DROP PUBLICATION testpub_forall_tbls_seqs, testpub_foralltables_excepttable, testpub_foralltables_excepttable1;
---------------------------------------------
-- Tests for inherited tables, and
@@ -991,7 +1031,18 @@ ALTER PUBLICATION testpub4 owner to regress_publication_user2; -- fail
ALTER PUBLICATION testpub4 owner to regress_publication_user; -- ok
SET ROLE regress_publication_user;
-DROP PUBLICATION testpub4;
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub5 FOR ALL TABLES;
+RESET client_min_messages;
+ALTER PUBLICATION testpub5 OWNER TO regress_publication_user3;
+SET ROLE regress_publication_user3;
+-- fail - SET ALL TABLES/SEQUENCES on a publication requires superuser privileges
+ALTER PUBLICATION testpub5 SET ALL TABLES EXCEPT TABLE (testpub_tbl1); -- fail
+ALTER PUBLICATION testpub5 SET ALL TABLES; -- fail
+ALTER PUBLICATION testpub5 SET ALL SEQUENCES; -- fail
+
+SET ROLE regress_publication_user;
+DROP PUBLICATION testpub4, testpub5;
DROP ROLE regress_publication_user3;
REVOKE CREATE ON DATABASE regression FROM regress_publication_user2;
diff --git a/src/test/subscription/t/037_except.pl b/src/test/subscription/t/037_except.pl
index 2729df4d5c0..13b99eda258 100644
--- a/src/test/subscription/t/037_except.pl
+++ b/src/test/subscription/t/037_except.pl
@@ -152,18 +152,50 @@ $result =
$node_subscriber->safe_psql('postgres', "SELECT count(*) FROM child1");
is($result, qq(10), 'check replicated inserts on subscriber');
+$node_publisher->safe_psql('postgres',
+ "CREATE TABLE tab2 AS SELECT generate_series(1,10) AS a");
+$node_subscriber->safe_psql('postgres', "CREATE TABLE tab2 (a int)");
+
+# Replace the EXCEPT TABLE list so that only tab2 is excluded.
+$node_publisher->safe_psql('postgres',
+ "ALTER PUBLICATION tab_pub SET ALL TABLES EXCEPT TABLE (tab2)");
+
+# Refresh the subscription so the subscriber picks up the updated
+# publication definition and initiates table synchronization.
+$node_subscriber->safe_psql('postgres',
+ "ALTER SUBSCRIPTION tab_sub REFRESH PUBLICATION");
+
+# Wait for initial table sync to finish
+$node_subscriber->wait_for_subscription_sync($node_publisher, 'tab_sub');
+
+# Verify that initial table synchronization does not occur for tables
+# listed in the EXCEPT TABLE clause.
+$result =
+ $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM tab2");
+is($result, qq(0),
+ 'check there is no initial data copied for the tables specified in the EXCEPT TABLE clause'
+);
+
+# Verify that table synchronization now happens for tab1. Table tab1 is
+# included now since the EXCEPT TABLE list is only (tab2).
+$result =
+ $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM tab1");
+is($result, qq(20),
+ 'check that the data is copied as the tab1 is removed from EXCEPT TABLE clause'
+);
+
# cleanup
$node_subscriber->safe_psql(
'postgres', qq(
DROP SUBSCRIPTION tab_sub;
TRUNCATE TABLE tab1;
- DROP TABLE parent, parent1, child, child1;
+ DROP TABLE parent, parent1, child, child1, tab2;
));
$node_publisher->safe_psql(
'postgres', qq(
DROP PUBLICATION tab_pub;
TRUNCATE TABLE tab1;
- DROP TABLE parent, parent1, child, child1;
+ DROP TABLE parent, parent1, child, child1, tab2;
));
# ============================================