From 21f7829512cead245ef2c873b4b0669419b0e19f Mon Sep 17 00:00:00 2001 From: vignesh Date: Mon, 21 Jun 2021 08:57:58 +0530 Subject: [PATCH v7 2/2] Tests and documentation for schema level support for publication. Tests and documentation for schema level support for publication. --- doc/src/sgml/catalogs.sgml | 94 ++++++- doc/src/sgml/ref/alter_publication.sgml | 45 ++- doc/src/sgml/ref/create_publication.sgml | 45 ++- src/test/regress/expected/object_address.out | 6 +- src/test/regress/expected/publication.out | 277 ++++++++++++++++++- src/test/regress/sql/object_address.sql | 3 + src/test/regress/sql/publication.sql | 112 +++++++- src/test/subscription/t/001_rep_changes.pl | 141 +++++++++- 8 files changed, 707 insertions(+), 16 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index f517a7d4af..b87d84a2e1 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -245,6 +245,11 @@ relation to publication mapping + + pg_publication_schema + schema to publication mapping + + pg_range information about range types @@ -6171,6 +6176,28 @@ SCRAM-SHA-256$<iteration count>:&l publication instead of its own. + + + + pubtype char + + + Publication type: + a = FOR ALL TABLES publication type, + t = FOR TABLE publication type, + s = FOR SCHEMA publication type, + e = Empty publication type. + If a publication is created without specifying any of + FOR ALL TABLES, FOR TABLE and + FOR SCHEMA option, then the publication will be + created as an empty publication type. When a table or schema is added to + the publication using + ALTER PUBLICATION then the publication type + will be changed to t or s + respctively. The publication type cannot be changed in other cases. + + + @@ -6238,6 +6265,67 @@ SCRAM-SHA-256$<iteration count>:&l + + <structname>pg_publication_schema</structname> + + + pg_publication_schema + + + + The catalog pg_publication_schema contains the + mapping between schemas and publications in the database. This is a + many-to-many mapping. + + + + <structname>pg_publication_schema</structname> Columns + + + + + Column Type + + + Description + + + + + + + + oid oid + + + Row identifier + + + + + + pspubid oid + (references pg_publication.oid) + + + Reference to publication + + + + + + psnspcid oid + (references pg_namespace.oid) + + + Reference to schema + + + + +
+
+ <structname>pg_range</structname> @@ -11274,9 +11362,9 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx information about the mapping between publications and the tables they contain. Unlike the underlying catalog pg_publication_rel, - this view expands - publications defined as FOR ALL TABLES, so for such - publications there will be a row for each eligible table. + this view expands publications defined as FOR ALL TABLES + and FOR SCHEMA, so for such publications there will be a + row for each eligible table. diff --git a/doc/src/sgml/ref/alter_publication.sgml b/doc/src/sgml/ref/alter_publication.sgml index faa114b2c6..532ca2ff62 100644 --- a/doc/src/sgml/ref/alter_publication.sgml +++ b/doc/src/sgml/ref/alter_publication.sgml @@ -24,6 +24,9 @@ PostgreSQL documentation ALTER PUBLICATION name ADD TABLE [ ONLY ] table_name [ * ] [, ...] ALTER PUBLICATION name SET TABLE [ ONLY ] table_name [ * ] [, ...] ALTER PUBLICATION name DROP TABLE [ ONLY ] table_name [ * ] [, ...] +ALTER PUBLICATION name ADD SCHEMA { schema_name | CURRENT_SCHEMA } [, ...] +ALTER PUBLICATION name SET SCHEMA { schema_name | CURRENT_SCHEMA } [, ...] +ALTER PUBLICATION name DROP SCHEMA { schema_name | CURRENT_SCHEMA } [, ...] 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 @@ -50,7 +53,18 @@ ALTER PUBLICATION name RENAME TO - The fourth variant of this command listed in the synopsis can change + The fourth, fifth and sixth variants change which schemas are part of the + publication. The SET SCHEMA clause will replace the list + of schemas in the publication with the specified one. The ADD + SCHEMA and DROP SCHEMA clauses will add and + remove one or more schemas from the publication. Note that adding schemas + to a publication that is already subscribed to will require a ALTER + SUBSCRIPTION ... REFRESH PUBLICATION action on the subscribing side + in order to become effective. + + + + The seventh variant of this command listed in the synopsis can change all of the publication properties specified in . Properties not mentioned in the command retain their previous settings. @@ -97,6 +111,15 @@ ALTER PUBLICATION name RENAME TO + + schema_name + + + Name of an existing schema. + + + + SET ( publication_parameter [= value] [, ... ] ) @@ -141,6 +164,26 @@ ALTER PUBLICATION noinsert SET (publish = 'update, delete'); Add some tables to the publication: ALTER PUBLICATION mypublication ADD TABLE users, departments; + + + + Add some schemas to the publication: + +ALTER PUBLICATION sales_publication ADD SCHEMA marketing_june, sales_june; + + + + + Drop some schema from the publication: + +ALTER PUBLICATION production_quarterly_publication DROP SCHEMA production_july; + + + + + Set schema to the publication: + +ALTER PUBLICATION production_publication SET SCHEMA production_july; diff --git a/doc/src/sgml/ref/create_publication.sgml b/doc/src/sgml/ref/create_publication.sgml index ff82fbca55..60fea8debe 100644 --- a/doc/src/sgml/ref/create_publication.sgml +++ b/doc/src/sgml/ref/create_publication.sgml @@ -22,8 +22,9 @@ PostgreSQL documentation CREATE PUBLICATION name - [ FOR TABLE [ ONLY ] table_name [ * ] [, ...] - | FOR ALL TABLES ] + [ FOR TABLE [ ONLY ] table_name [ * ] [, ... ] + | FOR SCHEMA { schema_name | CURRENT_SCHEMA } [, ... ] + | FOR ALL TABLES [ WITH ( publication_parameter [= value] [, ... ] ) ] @@ -99,6 +100,17 @@ CREATE PUBLICATION name + + FOR SCHEMA + + + Marks the publication as one that replicates changes for the all tables in + the specified list of schemas, including tables created in the future. If + schema is not specified, publication will be created for CURRENT_SCHEMA. + + + + WITH ( publication_parameter [= value] [, ... ] ) @@ -153,9 +165,10 @@ CREATE PUBLICATION name Notes - If neither FOR TABLE nor FOR ALL - TABLES is specified, then the publication starts out with an - empty set of tables. That is useful if tables are to be added later. + If FOR TABLE, FOR ALL TABLES or + FOR SCHEMA is not specified, then the publication starts + out with an empty set of tables. That is useful if tables or schemas are to + be added later. @@ -170,9 +183,9 @@ CREATE PUBLICATION name - To add a table to a publication, the invoking user must have ownership - rights on the table. The FOR ALL TABLES clause requires - the invoking user to be a superuser. + To add a table/schema to a publication, the invoking user must have + ownership rights on the table/schema. The FOR ALL TABLES + clause requires the invoking user to be a superuser. @@ -222,6 +235,22 @@ CREATE PUBLICATION alltables FOR ALL TABLES; CREATE PUBLICATION insert_only FOR TABLE mydata WITH (publish = 'insert'); + + + + + Create a publication that publishes all changes for all the tables present in +production schema: + +CREATE PUBLICATION production_publication FOR SCHEMA production; + + + + + Create a publication that publishes all changes for all the tables present in +marketing and sales schemas: + +CREATE PUBLICATION sales_publication FOR SCHEMA marketing, sales; diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out index 388097a695..49ea22f427 100644 --- a/src/test/regress/expected/object_address.out +++ b/src/test/regress/expected/object_address.out @@ -45,6 +45,7 @@ CREATE TRANSFORM FOR int LANGUAGE SQL ( -- suppress warning that depends on wal_level SET client_min_messages = 'ERROR'; CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; +CREATE PUBLICATION addr_pub_schema FOR SCHEMA addr_nsp; RESET client_min_messages; CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE); WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables @@ -428,6 +429,7 @@ WITH objects (type, name, args) AS (VALUES ('access method', '{btree}', '{}'), ('publication', '{addr_pub}', '{}'), ('publication relation', '{addr_nsp, gentable}', '{addr_pub}'), + ('publication schema', '{addr_nsp}', '{addr_pub_schema}'), ('subscription', '{regress_addr_sub}', '{}'), ('statistics object', '{addr_nsp, gentable_stat}', '{}') ) @@ -490,7 +492,8 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*, subscription | | regress_addr_sub | regress_addr_sub | t publication | | addr_pub | addr_pub | t publication relation | | | addr_nsp.gentable in publication addr_pub | t -(49 rows) + publication schema | | | addr_nsp in publication addr_pub_schema | t +(50 rows) --- --- Cleanup resources @@ -502,6 +505,7 @@ drop cascades to foreign table genftable drop cascades to server integer drop cascades to user mapping for regress_addr_user on server integer DROP PUBLICATION addr_pub; +DROP PUBLICATION addr_pub_schema; DROP SUBSCRIPTION regress_addr_sub; DROP SCHEMA addr_nsp CASCADE; NOTICE: drop cascades to 14 other objects diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 28bf8daa64..bed28ef8a7 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -255,7 +255,6 @@ DROP PUBLICATION testpub2; SET ROLE regress_publication_user; REVOKE CREATE ON DATABASE regression FROM regress_publication_user2; DROP TABLE testpub_parted; -DROP VIEW testpub_view; DROP TABLE testpub_tbl1; \dRp+ testpub_default Publication testpub_default @@ -287,11 +286,287 @@ ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2; testpub_default | regress_publication_user2 | f | t | t | t | f | f | t (1 row) +-- CREATE publication with schema +CREATE SCHEMA pub_test1; +CREATE SCHEMA pub_test2; +CREATE SCHEMA pub_test3; +CREATE TABLE pub_test1.tbl1 (id serial primary key, data text); +CREATE TABLE pub_test2.tbl1 (id serial primary key, data text); +-- suppress warning that depends on wal_level +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION testpub1_forschema FOR SCHEMA pub_test1; +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + +SELECT p.pubname FROM pg_catalog.pg_publication p, pg_catalog.pg_namespace n, pg_catalog.pg_publication_schema ps WHERE n.oid = ps.psnspcid AND p.oid = ps.pspubid AND n.nspname = 'pub_test1' ORDER BY 1; + pubname +-------------------- + testpub1_forschema +(1 row) + +CREATE PUBLICATION testpub2_forschema FOR SCHEMA pub_test1, pub_test2, pub_test3; +\dRp+ testpub2_forschema + Publication testpub2_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + "pub_test3" + +SELECT p.pubname FROM pg_catalog.pg_publication p, pg_catalog.pg_namespace n, pg_catalog.pg_publication_schema ps WHERE n.oid = ps.psnspcid AND p.oid = ps.pspubid AND n.nspname = 'pub_test1' ORDER BY 1; + pubname +-------------------- + testpub1_forschema + testpub2_forschema +(2 rows) + +SELECT p.pubname FROM pg_catalog.pg_publication p, pg_catalog.pg_namespace n, pg_catalog.pg_publication_schema ps WHERE n.oid = ps.psnspcid AND p.oid = ps.pspubid AND n.nspname = 'pub_test2' ORDER BY 1; + pubname +-------------------- + testpub2_forschema +(1 row) + +SELECT p.pubname FROM pg_catalog.pg_publication p, pg_catalog.pg_namespace n, pg_catalog.pg_publication_schema ps WHERE n.oid = ps.psnspcid AND p.oid = ps.pspubid AND n.nspname = 'pub_test3' ORDER BY 1; + pubname +-------------------- + testpub2_forschema +(1 row) + +--- Check create publication on CURRENT_SCHEMA +CREATE PUBLICATION testpub3_forschema FOR SCHEMA CURRENT_SCHEMA; +RESET client_min_messages; +\dRp+ testpub3_forschema + Publication testpub3_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "public" + +SELECT p.pubname FROM pg_catalog.pg_publication p, pg_catalog.pg_namespace n, pg_catalog.pg_publication_schema ps WHERE n.oid = ps.psnspcid AND p.oid = ps.pspubid AND n.nspname = 'public' ORDER BY 1; + pubname +-------------------- + testpub3_forschema +(1 row) + +--- Check create publication on CURRENT_SCHEMA where search_path is not set +SET SEARCH_PATH=''; +CREATE PUBLICATION testpub_forschema FOR SCHEMA CURRENT_SCHEMA; +ERROR: no schema has been selected +RESET SEARCH_PATH; +--- Check create publication on a schema that does not exist +CREATE PUBLICATION testpub_forschema FOR SCHEMA non_existent_schema; +ERROR: schema "non_existent_schema" does not exist +--- Check create publication on a object which is not schema +CREATE PUBLICATION testpub1_forschema1 FOR SCHEMA testpub_view; +ERROR: schema "testpub_view" does not exist +-- Dropping the schema should reflect the change in publication +DROP SCHEMA pub_test3; +\dRp+ testpub2_forschema + Publication testpub2_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + +-- Renaming the schema should reflect the change in publication +ALTER SCHEMA pub_test1 RENAME to pub_test1_renamed; +\dRp+ testpub2_forschema + Publication testpub2_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1_renamed" + "pub_test2" + +ALTER SCHEMA pub_test1_renamed RENAME to pub_test1; +\dRp+ testpub2_forschema + Publication testpub2_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + +-- Alter publication add schema +ALTER PUBLICATION testpub1_forschema ADD SCHEMA pub_test2; +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + +-- Alter publication add CURRENT_SCHEMA +ALTER PUBLICATION testpub1_forschema ADD SCHEMA CURRENT_SCHEMA; +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + "public" + +-- Add non existent schema +ALTER PUBLICATION testpub1_forschema ADD SCHEMA non_existent_schema; +ERROR: schema "non_existent_schema" does not exist +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + "public" + +-- Add a schema which is already added to the publication +ALTER PUBLICATION testpub1_forschema ADD SCHEMA pub_test1; +ERROR: schema "pub_test1" is already member of publication "testpub1_forschema" +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + "public" + +-- Alter publication drop CURRENT_SCHEMA +ALTER PUBLICATION testpub1_forschema DROP SCHEMA CURRENT_SCHEMA; +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + +-- Alter publication drop schema +ALTER PUBLICATION testpub1_forschema DROP SCHEMA pub_test2; +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + +-- Drop schema that is not preset in the publication +ALTER PUBLICATION testpub1_forschema DROP SCHEMA pub_test2; +ERROR: schema "pub_test2" is not part of the publication +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + +-- Drop a schema that does not exist in the system +ALTER PUBLICATION testpub1_forschema DROP SCHEMA non_existent_schema; +ERROR: schema "non_existent_schema" does not exist +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + +-- Drop all schemas +ALTER PUBLICATION testpub1_forschema DROP SCHEMA pub_test1; +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +(1 row) + +-- Alter publication set schema +ALTER PUBLICATION testpub1_forschema SET SCHEMA pub_test1; +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + +-- Alter publication set multiple schema +ALTER PUBLICATION testpub1_forschema SET SCHEMA pub_test1, pub_test2; +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + +-- Alter publication set non-existent schema +ALTER PUBLICATION testpub1_forschema SET SCHEMA non_existent_schema; +ERROR: schema "non_existent_schema" does not exist +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + +-- Alter publication set it with the same schema +ALTER PUBLICATION testpub1_forschema SET SCHEMA pub_test1, pub_test2; +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "pub_test1" + "pub_test2" + +-- Alter publication set it with CURRENT_SCHEMA +ALTER PUBLICATION testpub1_forschema SET SCHEMA CURRENT_SCHEMA; +\dRp+ testpub1_forschema + Publication testpub1_forschema + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Pubtype +--------------------------+------------+---------+---------+---------+-----------+----------+--------- + regress_publication_user | f | t | t | t | t | f | s +Schemas: + "public" + +DROP VIEW testpub_view; DROP PUBLICATION testpub_default; DROP PUBLICATION testpib_ins_trunct; DROP PUBLICATION testpub_fortbl; +DROP PUBLICATION testpub1_forschema; +DROP PUBLICATION testpub2_forschema; +DROP PUBLICATION testpub3_forschema; DROP SCHEMA pub_test CASCADE; NOTICE: drop cascades to table pub_test.testpub_nopk +DROP SCHEMA pub_test1 CASCADE; +NOTICE: drop cascades to table pub_test1.tbl1 +DROP SCHEMA pub_test2 CASCADE; +NOTICE: drop cascades to table pub_test2.tbl1 RESET SESSION AUTHORIZATION; DROP ROLE regress_publication_user, regress_publication_user2; DROP ROLE regress_publication_user_dummy; diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql index 2f4f66e3e1..56d9b852fd 100644 --- a/src/test/regress/sql/object_address.sql +++ b/src/test/regress/sql/object_address.sql @@ -48,6 +48,7 @@ CREATE TRANSFORM FOR int LANGUAGE SQL ( -- suppress warning that depends on wal_level SET client_min_messages = 'ERROR'; CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; +CREATE PUBLICATION addr_pub_schema FOR SCHEMA addr_nsp; RESET client_min_messages; CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE); CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable; @@ -198,6 +199,7 @@ WITH objects (type, name, args) AS (VALUES ('access method', '{btree}', '{}'), ('publication', '{addr_pub}', '{}'), ('publication relation', '{addr_nsp, gentable}', '{addr_pub}'), + ('publication schema', '{addr_nsp}', '{addr_pub_schema}'), ('subscription', '{regress_addr_sub}', '{}'), ('statistics object', '{addr_nsp, gentable_stat}', '{}') ) @@ -215,6 +217,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*, --- DROP FOREIGN DATA WRAPPER addr_fdw CASCADE; DROP PUBLICATION addr_pub; +DROP PUBLICATION addr_pub_schema; DROP SUBSCRIPTION regress_addr_sub; DROP SCHEMA addr_nsp CASCADE; diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index d844075368..d55a6f42b3 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -148,7 +148,6 @@ SET ROLE regress_publication_user; REVOKE CREATE ON DATABASE regression FROM regress_publication_user2; DROP TABLE testpub_parted; -DROP VIEW testpub_view; DROP TABLE testpub_tbl1; \dRp+ testpub_default @@ -169,11 +168,122 @@ ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2; \dRp testpub_default +-- CREATE publication with schema +CREATE SCHEMA pub_test1; +CREATE SCHEMA pub_test2; +CREATE SCHEMA pub_test3; +CREATE TABLE pub_test1.tbl1 (id serial primary key, data text); +CREATE TABLE pub_test2.tbl1 (id serial primary key, data text); + +-- suppress warning that depends on wal_level +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION testpub1_forschema FOR SCHEMA pub_test1; +\dRp+ testpub1_forschema +SELECT p.pubname FROM pg_catalog.pg_publication p, pg_catalog.pg_namespace n, pg_catalog.pg_publication_schema ps WHERE n.oid = ps.psnspcid AND p.oid = ps.pspubid AND n.nspname = 'pub_test1' ORDER BY 1; + +CREATE PUBLICATION testpub2_forschema FOR SCHEMA pub_test1, pub_test2, pub_test3; +\dRp+ testpub2_forschema +SELECT p.pubname FROM pg_catalog.pg_publication p, pg_catalog.pg_namespace n, pg_catalog.pg_publication_schema ps WHERE n.oid = ps.psnspcid AND p.oid = ps.pspubid AND n.nspname = 'pub_test1' ORDER BY 1; +SELECT p.pubname FROM pg_catalog.pg_publication p, pg_catalog.pg_namespace n, pg_catalog.pg_publication_schema ps WHERE n.oid = ps.psnspcid AND p.oid = ps.pspubid AND n.nspname = 'pub_test2' ORDER BY 1; +SELECT p.pubname FROM pg_catalog.pg_publication p, pg_catalog.pg_namespace n, pg_catalog.pg_publication_schema ps WHERE n.oid = ps.psnspcid AND p.oid = ps.pspubid AND n.nspname = 'pub_test3' ORDER BY 1; + +--- Check create publication on CURRENT_SCHEMA +CREATE PUBLICATION testpub3_forschema FOR SCHEMA CURRENT_SCHEMA; +RESET client_min_messages; + +\dRp+ testpub3_forschema +SELECT p.pubname FROM pg_catalog.pg_publication p, pg_catalog.pg_namespace n, pg_catalog.pg_publication_schema ps WHERE n.oid = ps.psnspcid AND p.oid = ps.pspubid AND n.nspname = 'public' ORDER BY 1; + +--- Check create publication on CURRENT_SCHEMA where search_path is not set +SET SEARCH_PATH=''; +CREATE PUBLICATION testpub_forschema FOR SCHEMA CURRENT_SCHEMA; +RESET SEARCH_PATH; + +--- Check create publication on a schema that does not exist +CREATE PUBLICATION testpub_forschema FOR SCHEMA non_existent_schema; + +--- Check create publication on a object which is not schema +CREATE PUBLICATION testpub1_forschema1 FOR SCHEMA testpub_view; + +-- Dropping the schema should reflect the change in publication +DROP SCHEMA pub_test3; +\dRp+ testpub2_forschema + +-- Renaming the schema should reflect the change in publication +ALTER SCHEMA pub_test1 RENAME to pub_test1_renamed; +\dRp+ testpub2_forschema + +ALTER SCHEMA pub_test1_renamed RENAME to pub_test1; +\dRp+ testpub2_forschema + +-- Alter publication add schema +ALTER PUBLICATION testpub1_forschema ADD SCHEMA pub_test2; +\dRp+ testpub1_forschema + +-- Alter publication add CURRENT_SCHEMA +ALTER PUBLICATION testpub1_forschema ADD SCHEMA CURRENT_SCHEMA; +\dRp+ testpub1_forschema + +-- Add non existent schema +ALTER PUBLICATION testpub1_forschema ADD SCHEMA non_existent_schema; +\dRp+ testpub1_forschema + +-- Add a schema which is already added to the publication +ALTER PUBLICATION testpub1_forschema ADD SCHEMA pub_test1; +\dRp+ testpub1_forschema + +-- Alter publication drop CURRENT_SCHEMA +ALTER PUBLICATION testpub1_forschema DROP SCHEMA CURRENT_SCHEMA; +\dRp+ testpub1_forschema + +-- Alter publication drop schema +ALTER PUBLICATION testpub1_forschema DROP SCHEMA pub_test2; +\dRp+ testpub1_forschema + +-- Drop schema that is not preset in the publication +ALTER PUBLICATION testpub1_forschema DROP SCHEMA pub_test2; +\dRp+ testpub1_forschema + +-- Drop a schema that does not exist in the system +ALTER PUBLICATION testpub1_forschema DROP SCHEMA non_existent_schema; +\dRp+ testpub1_forschema + +-- Drop all schemas +ALTER PUBLICATION testpub1_forschema DROP SCHEMA pub_test1; +\dRp+ testpub1_forschema + +-- Alter publication set schema +ALTER PUBLICATION testpub1_forschema SET SCHEMA pub_test1; +\dRp+ testpub1_forschema + +-- Alter publication set multiple schema +ALTER PUBLICATION testpub1_forschema SET SCHEMA pub_test1, pub_test2; +\dRp+ testpub1_forschema + +-- Alter publication set non-existent schema +ALTER PUBLICATION testpub1_forschema SET SCHEMA non_existent_schema; +\dRp+ testpub1_forschema + +-- Alter publication set it with the same schema +ALTER PUBLICATION testpub1_forschema SET SCHEMA pub_test1, pub_test2; +\dRp+ testpub1_forschema + +-- Alter publication set it with CURRENT_SCHEMA +ALTER PUBLICATION testpub1_forschema SET SCHEMA CURRENT_SCHEMA; +\dRp+ testpub1_forschema + +DROP VIEW testpub_view; + DROP PUBLICATION testpub_default; DROP PUBLICATION testpib_ins_trunct; DROP PUBLICATION testpub_fortbl; +DROP PUBLICATION testpub1_forschema; +DROP PUBLICATION testpub2_forschema; +DROP PUBLICATION testpub3_forschema; DROP SCHEMA pub_test CASCADE; +DROP SCHEMA pub_test1 CASCADE; +DROP SCHEMA pub_test2 CASCADE; RESET SESSION AUTHORIZATION; DROP ROLE regress_publication_user, regress_publication_user2; diff --git a/src/test/subscription/t/001_rep_changes.pl b/src/test/subscription/t/001_rep_changes.pl index ca6cd2c646..617680d61c 100644 --- a/src/test/subscription/t/001_rep_changes.pl +++ b/src/test/subscription/t/001_rep_changes.pl @@ -6,7 +6,7 @@ use strict; use warnings; use PostgresNode; use TestLib; -use Test::More tests => 32; +use Test::More tests => 45; # Initialize publisher node my $node_publisher = get_new_node('publisher'); @@ -266,6 +266,145 @@ $node_publisher->safe_psql('postgres', "DROP TABLE temp2"); $node_subscriber->safe_psql('postgres', "DROP TABLE temp1"); $node_subscriber->safe_psql('postgres', "DROP TABLE temp2"); +# Test replication with publications created using FOR SCHEMA option. +# Create schemas and tables on publisher +$node_publisher->safe_psql('postgres', "CREATE SCHEMA sch1"); +$node_publisher->safe_psql('postgres', "CREATE SCHEMA sch2"); +$node_publisher->safe_psql('postgres', "CREATE SCHEMA sch3"); +$node_publisher->safe_psql('postgres', "CREATE TABLE sch1.tab1 AS SELECT generate_series(1,10) AS a"); +$node_publisher->safe_psql('postgres', "CREATE TABLE sch1.tab2 AS SELECT generate_series(1,10) AS a"); +$node_publisher->safe_psql('postgres', "CREATE TABLE sch2.tab1 AS SELECT generate_series(1,10) AS a"); +$node_publisher->safe_psql('postgres', "CREATE TABLE sch2.tab2 AS SELECT generate_series(1,10) AS a"); + +# Create schemas and tables on subscriber +$node_subscriber->safe_psql('postgres', "CREATE SCHEMA sch1"); +$node_subscriber->safe_psql('postgres', "CREATE SCHEMA sch2"); +$node_subscriber->safe_psql('postgres', "CREATE SCHEMA sch3"); +$node_subscriber->safe_psql('postgres', "CREATE TABLE sch1.tab1 (a int)"); +$node_subscriber->safe_psql('postgres', "CREATE TABLE sch1.tab2 (a int)"); +$node_subscriber->safe_psql('postgres', "CREATE TABLE sch2.tab1 (a int)"); +$node_subscriber->safe_psql('postgres', "CREATE TABLE sch2.tab2 (a int)"); + +# Setup logical replication for schema sch1 and sch2 that will only be used for +# this test +$node_publisher->safe_psql('postgres', + "CREATE PUBLICATION tap_pub_schema FOR SCHEMA sch1,sch2"); +$node_subscriber->safe_psql('postgres', + "CREATE SUBSCRIPTION tap_sub_schema CONNECTION '$publisher_connstr' PUBLICATION tap_pub_schema" + ); + +$node_publisher->wait_for_catchup('tap_sub_schema'); + +# Also wait for initial table sync to finish +$synced_query = + "SELECT count(1) = 0 FROM pg_subscription_rel WHERE srsubstate NOT IN ('r', 's');"; +$node_subscriber->poll_query_until('postgres', $synced_query) + or die "Timed out while waiting for subscriber to synchronize data"; + +# Check the schema table data is synced up. +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*), min(a), max(a) FROM sch1.tab1"); +is($result, qq(10|1|10), 'check rows on subscriber catchup'); +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*), min(a), max(a) FROM sch1.tab2"); +is($result, qq(10|1|10), 'check rows on subscriber catchup'); +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*), min(a), max(a) FROM sch2.tab1"); +is($result, qq(10|1|10), 'check rows on subscriber catchup'); +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*), min(a), max(a) FROM sch2.tab2"); +is($result, qq(10|1|10), 'check rows on subscriber catchup'); + +# Insert some data into few tables and verify that inserted data is replicated. +$node_publisher->safe_psql('postgres', "INSERT INTO sch1.tab1 VALUES(generate_series(11,20))"); +$node_publisher->safe_psql('postgres', "INSERT INTO sch2.tab1 VALUES(generate_series(11,20))"); + +$node_publisher->wait_for_catchup('tap_sub_schema'); + +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*), min(a), max(a) FROM sch1.tab1"); +is($result, qq(20|1|20), 'check rows on subscriber catchup'); +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*), min(a), max(a) FROM sch2.tab1"); +is($result, qq(20|1|20), 'check rows on subscriber catchup'); + +# Create new table in the publication schema, verify that subscriber does not get +# the new table data in the subscriber before refresh. +$node_publisher->safe_psql('postgres', "CREATE TABLE SCH1.tab3 AS SELECT generate_series(1,10) AS a"); +$node_subscriber->safe_psql('postgres', "CREATE TABLE SCH1.tab3(a INT)"); +$node_publisher->wait_for_catchup('tap_sub_schema'); +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*) FROM sch1.tab3"); +is($result, qq(0), 'check rows on subscriber catchup'); + +# Table data shsould be reflected after refreshing the publication in +# subscriber. +$node_subscriber->safe_psql('postgres', + "ALTER SUBSCRIPTION tap_sub_schema REFRESH PUBLICATION"); + +# Also wait for initial table sync to finish +$node_subscriber->poll_query_until('postgres', $synced_query) + or die "Timed out while waiting for subscriber to synchronize data"; + +$node_publisher->safe_psql('postgres', "INSERT INTO SCH1.tab3 VALUES(11)"); +$node_publisher->wait_for_catchup('tap_sub_schema'); +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*), min(a), max(a) FROM sch1.tab3"); +is($result, qq(11|1|11), 'check rows on subscriber catchup'); + +# Set the schema of a publication schema table to a non publication schema and +# verify that inserted data is not reflected by the subscriber. +$node_publisher->safe_psql('postgres', "ALTER TABLE SCH1.tab3 SET SCHEMA SCH3"); +$node_publisher->safe_psql('postgres', "INSERT INTO SCH3.tab3 VALUES(11)"); +$node_publisher->wait_for_catchup('tap_sub_schema'); +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*), min(a), max(a) FROM sch1.tab3"); +is($result, qq(11|1|11), 'check rows on subscriber catchup'); + +# Verify that the subscription relation list is updated after refresh. +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*) FROM pg_subscription_rel WHERE srsubid IN (SELECT oid FROM pg_subscription WHERE subname = 'tap_sub_schema')"); +is($result, qq(5), + 'check subscription relation status was dropped on subscriber'); +$node_subscriber->safe_psql('postgres', + "ALTER SUBSCRIPTION tap_sub_schema REFRESH PUBLICATION"); +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*) FROM pg_subscription_rel WHERE srsubid IN (SELECT oid FROM pg_subscription WHERE subname = 'tap_sub_schema')"); +is($result, qq(4), + 'check subscription relation status was dropped on subscriber'); + +# Drop table from the publication schema, verify that subscriber removes the +# table entry after refresh. +$node_publisher->safe_psql('postgres', "DROP TABLE SCH1.tab2"); +$node_publisher->wait_for_catchup('tap_sub_schema'); +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*) FROM pg_subscription_rel WHERE srsubid IN (SELECT oid FROM pg_subscription WHERE subname = 'tap_sub_schema')"); +is($result, qq(4), + 'check subscription relation status is not yet dropped on subscriber'); + +# Table should be removed from pg_subscription_rel after refreshing the +# publication in subscriber. +$node_subscriber->safe_psql('postgres', + "ALTER SUBSCRIPTION tap_sub_schema REFRESH PUBLICATION"); +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*) FROM pg_subscription_rel WHERE srsubid IN (SELECT oid FROM pg_subscription WHERE subname = 'tap_sub_schema')"); +is($result, qq(3), + 'check subscription relation status was dropped on subscriber'); + +# Drop subscription as we don't need it anymore +$node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION tap_sub_schema"); + +# Drop publications as we don't need them anymore +$node_publisher->safe_psql('postgres', "DROP PUBLICATION tap_pub_schema"); + +# Clean up the tables on both publisher and subscriber as we don't need them +$node_publisher->safe_psql('postgres', "DROP SCHEMA sch1 cascade"); +$node_publisher->safe_psql('postgres', "DROP SCHEMA sch2 cascade"); +$node_publisher->safe_psql('postgres', "DROP SCHEMA sch3 cascade"); +$node_subscriber->safe_psql('postgres', "DROP SCHEMA sch1 cascade"); +$node_subscriber->safe_psql('postgres', "DROP SCHEMA sch2 cascade"); +$node_subscriber->safe_psql('postgres', "DROP SCHEMA sch3 cascade"); + # add REPLICA IDENTITY FULL so we can update $node_publisher->safe_psql('postgres', "ALTER TABLE tab_full REPLICA IDENTITY FULL"); -- 2.25.1