Revert "Logical decoding of sequences"

This reverts a sequence of commits, implementing features related to
logical decoding and replication of sequences:

 - 0da92dc530
 - 80901b3291
 - b779d7d8fd
 - d5ed9da41d
 - a180c2b34d
 - 75b1521dae
 - 2d2232933b
 - 002c9dd97a
 - 05843b1aa4

The implementation has issues, mostly due to combining transactional and
non-transactional behavior of sequences. It's not clear how this could
be fixed, but it'll require reworking significant part of the patch.

Discussion: https://postgr.es/m/95345a19-d508-63d1-860a-f5c2f41e8d40@enterprisedb.com
This commit is contained in:
Tomas Vondra 2022-04-07 18:13:13 +02:00
parent d7ab2a9a3c
commit 2c7ea57e56
73 changed files with 605 additions and 4762 deletions

View File

@ -5,8 +5,7 @@ PGFILEDESC = "test_decoding - example of a logical decoding output plugin"
REGRESS = ddl xact rewrite toast permissions decoding_in_xact \ REGRESS = ddl xact rewrite toast permissions decoding_in_xact \
decoding_into_rel binary prepared replorigin time messages \ decoding_into_rel binary prepared replorigin time messages \
spill slot truncate stream stats twophase twophase_stream \ spill slot truncate stream stats twophase twophase_stream
sequence
ISOLATION = mxact delayed_startup ondisk_startup concurrent_ddl_dml \ ISOLATION = mxact delayed_startup ondisk_startup concurrent_ddl_dml \
oldest_xmin snapshot_transfer subxact_without_top concurrent_stream \ oldest_xmin snapshot_transfer subxact_without_top concurrent_stream \
twophase_snapshot slot_creation_error twophase_snapshot slot_creation_error

File diff suppressed because one or more lines are too long

View File

@ -58,7 +58,7 @@ SELECT pg_current_xact_id() = '0';
-- don't show yet, haven't committed -- don't show yet, haven't committed
INSERT INTO nobarf(data) VALUES('2'); INSERT INTO nobarf(data) VALUES('2');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
----------------------------------------------------------- -----------------------------------------------------------
BEGIN BEGIN
@ -68,7 +68,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
COMMIT; COMMIT;
INSERT INTO nobarf(data) VALUES('3'); INSERT INTO nobarf(data) VALUES('3');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
----------------------------------------------------------- -----------------------------------------------------------
BEGIN BEGIN

View File

@ -19,7 +19,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
CREATE TABLE somechange(id serial primary key); CREATE TABLE somechange(id serial primary key);
INSERT INTO somechange DEFAULT VALUES; INSERT INTO somechange DEFAULT VALUES;
CREATE TABLE changeresult AS CREATE TABLE changeresult AS
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT * FROM changeresult; SELECT * FROM changeresult;
data data
------------------------------------------------ ------------------------------------------------
@ -29,9 +29,9 @@ SELECT * FROM changeresult;
(3 rows) (3 rows)
INSERT INTO changeresult INSERT INTO changeresult
SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
INSERT INTO changeresult INSERT INTO changeresult
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT * FROM changeresult; SELECT * FROM changeresult;
data data
-------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------
@ -63,7 +63,7 @@ DROP TABLE somechange;
CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$ CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$
BEGIN BEGIN
RETURN QUERY RETURN QUERY
SELECT data FROM pg_logical_slot_peek_changes(slot_name, NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_peek_changes(slot_name, NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
END$$ LANGUAGE plpgsql; END$$ LANGUAGE plpgsql;
SELECT * FROM slot_changes_wrapper('regression_slot'); SELECT * FROM slot_changes_wrapper('regression_slot');
slot_changes_wrapper slot_changes_wrapper
@ -84,7 +84,7 @@ SELECT * FROM slot_changes_wrapper('regression_slot');
COMMIT COMMIT
(14 rows) (14 rows)
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN BEGIN

View File

@ -7,7 +7,7 @@ step s0init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_sl
init init
(1 row) (1 row)
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false'); step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
data data
---- ----
(0 rows) (0 rows)
@ -27,7 +27,7 @@ t
(1 row) (1 row)
step s0w: INSERT INTO do_write DEFAULT VALUES; step s0w: INSERT INTO do_write DEFAULT VALUES;
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false'); step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
data data
-------------------------------------------- --------------------------------------------
BEGIN BEGIN
@ -50,7 +50,7 @@ step s0init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_sl
init init
(1 row) (1 row)
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false'); step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
data data
---- ----
(0 rows) (0 rows)
@ -71,7 +71,7 @@ t
step s0alter: ALTER TABLE do_write ADD column ts timestamptz; step s0alter: ALTER TABLE do_write ADD column ts timestamptz;
step s0w: INSERT INTO do_write DEFAULT VALUES; step s0w: INSERT INTO do_write DEFAULT VALUES;
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false'); step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
data data
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
BEGIN BEGIN

View File

@ -35,7 +35,7 @@ init
step s2c: COMMIT; step s2c: COMMIT;
step s1insert: INSERT INTO do_write DEFAULT VALUES; step s1insert: INSERT INTO do_write DEFAULT VALUES;
step s1checkpoint: CHECKPOINT; step s1checkpoint: CHECKPOINT;
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false'); step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
data data
-------------------------------------------------------------------- --------------------------------------------------------------------
BEGIN BEGIN
@ -46,7 +46,7 @@ COMMIT
step s1insert: INSERT INTO do_write DEFAULT VALUES; step s1insert: INSERT INTO do_write DEFAULT VALUES;
step s1alter: ALTER TABLE do_write ADD COLUMN addedbys1 int; step s1alter: ALTER TABLE do_write ADD COLUMN addedbys1 int;
step s1insert: INSERT INTO do_write DEFAULT VALUES; step s1insert: INSERT INTO do_write DEFAULT VALUES;
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false'); step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
data data
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
BEGIN BEGIN

View File

@ -72,9 +72,9 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
-- origin tx -- origin tx
INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again'); INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again');
INSERT INTO target_tbl(data) INSERT INTO target_tbl(data)
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- as is normal, the insert into target_tbl shows up -- as is normal, the insert into target_tbl shows up
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN BEGIN
@ -110,7 +110,7 @@ SELECT pg_replication_origin_xact_setup('0/aabbccdd', '2013-01-01 00:00');
(1 row) (1 row)
INSERT INTO target_tbl(data) INSERT INTO target_tbl(data)
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
COMMIT; COMMIT;
-- check replication progress for the session is correct -- check replication progress for the session is correct
SELECT pg_replication_origin_session_progress(false); SELECT pg_replication_origin_session_progress(false);
@ -154,14 +154,14 @@ SELECT pg_replication_origin_progress('regress_test_decoding: regression_slot',
SELECT pg_replication_origin_session_reset(); SELECT pg_replication_origin_session_reset();
ERROR: no replication origin is configured ERROR: no replication origin is configured
-- and magically the replayed xact will be filtered! -- and magically the replayed xact will be filtered!
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
data data
------ ------
(0 rows) (0 rows)
--but new original changes still show up --but new original changes still show up
INSERT INTO origin_tbl(data) VALUES ('will be replicated'); INSERT INTO origin_tbl(data) VALUES ('will be replicated');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
data data
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
BEGIN BEGIN
@ -227,7 +227,7 @@ SELECT local_id, external_id,
1 | regress_test_decoding: regression_slot_no_lsn | f | t 1 | regress_test_decoding: regression_slot_no_lsn | f | t
(1 row) (1 row)
SELECT data FROM pg_logical_slot_get_changes('regression_slot_no_lsn', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot_no_lsn', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0');
data data
------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------
BEGIN BEGIN

View File

@ -64,7 +64,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120)); CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120));
INSERT INTO replication_example(somedata) VALUES (1); INSERT INTO replication_example(somedata) VALUES (1);
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
---------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------
BEGIN BEGIN
@ -115,7 +115,7 @@ INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (7, 5
COMMIT; COMMIT;
-- make old files go away -- make old files go away
CHECKPOINT; CHECKPOINT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN BEGIN
@ -141,7 +141,7 @@ VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; V
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (8, 6, 1); INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (8, 6, 1);
VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; VACUUM FULL iamalargetable; VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; VACUUM FULL iamalargetable;
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (9, 7, 1); INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (9, 7, 1);
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN BEGIN

View File

@ -1,325 +0,0 @@
-- predictability
SET synchronous_commit = on;
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
?column?
----------
init
(1 row)
CREATE SEQUENCE test_sequence;
-- test the sequence changes by several nextval() calls
SELECT nextval('test_sequence');
nextval
---------
1
(1 row)
SELECT nextval('test_sequence');
nextval
---------
2
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3
(1 row)
SELECT nextval('test_sequence');
nextval
---------
4
(1 row)
-- test the sequence changes by several ALTER commands
ALTER SEQUENCE test_sequence INCREMENT BY 10;
SELECT nextval('test_sequence');
nextval
---------
14
(1 row)
ALTER SEQUENCE test_sequence START WITH 3000;
ALTER SEQUENCE test_sequence MAXVALUE 10000;
ALTER SEQUENCE test_sequence RESTART WITH 4000;
SELECT nextval('test_sequence');
nextval
---------
4000
(1 row)
-- test the sequence changes by several setval() calls
SELECT setval('test_sequence', 3500);
setval
--------
3500
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3510
(1 row)
SELECT setval('test_sequence', 3500, true);
setval
--------
3500
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3510
(1 row)
SELECT setval('test_sequence', 3500, false);
setval
--------
3500
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3500
(1 row)
-- show results and drop sequence
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data
----------------------------------------------------------------------------------------
BEGIN
sequence public.test_sequence: transactional:1 last_value: 1 log_cnt: 0 is_called:0
COMMIT
sequence public.test_sequence: transactional:0 last_value: 33 log_cnt: 0 is_called:1
BEGIN
sequence public.test_sequence: transactional:1 last_value: 4 log_cnt: 0 is_called:1
COMMIT
sequence public.test_sequence: transactional:0 last_value: 334 log_cnt: 0 is_called:1
BEGIN
sequence public.test_sequence: transactional:1 last_value: 14 log_cnt: 32 is_called:1
COMMIT
BEGIN
sequence public.test_sequence: transactional:1 last_value: 14 log_cnt: 0 is_called:1
COMMIT
BEGIN
sequence public.test_sequence: transactional:1 last_value: 4000 log_cnt: 0 is_called:0
COMMIT
sequence public.test_sequence: transactional:0 last_value: 4320 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:0 last_value: 3500 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:0 last_value: 3830 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:0 last_value: 3500 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:0 last_value: 3830 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:0 last_value: 3500 log_cnt: 0 is_called:0
sequence public.test_sequence: transactional:0 last_value: 3820 log_cnt: 0 is_called:1
(24 rows)
DROP SEQUENCE test_sequence;
-- rollback on sequence creation and update
BEGIN;
CREATE SEQUENCE test_sequence;
CREATE TABLE test_table (a INT);
SELECT nextval('test_sequence');
nextval
---------
1
(1 row)
SELECT nextval('test_sequence');
nextval
---------
2
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3
(1 row)
SELECT setval('test_sequence', 3000);
setval
--------
3000
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3001
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3002
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3003
(1 row)
ALTER SEQUENCE test_sequence RESTART WITH 6000;
INSERT INTO test_table VALUES( (SELECT nextval('test_sequence')) );
SELECT nextval('test_sequence');
nextval
---------
6001
(1 row)
ROLLBACK;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data
------
(0 rows)
-- rollback on table creation with serial column
BEGIN;
CREATE TABLE test_table (a SERIAL, b INT);
INSERT INTO test_table (b) VALUES (100);
INSERT INTO test_table (b) VALUES (200);
INSERT INTO test_table (b) VALUES (300);
SELECT setval('test_table_a_seq', 3000);
setval
--------
3000
(1 row)
INSERT INTO test_table (b) VALUES (400);
INSERT INTO test_table (b) VALUES (500);
INSERT INTO test_table (b) VALUES (600);
ALTER SEQUENCE test_table_a_seq RESTART WITH 6000;
INSERT INTO test_table (b) VALUES (700);
INSERT INTO test_table (b) VALUES (800);
INSERT INTO test_table (b) VALUES (900);
ROLLBACK;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data
------
(0 rows)
-- rollback on table with serial column
CREATE TABLE test_table (a SERIAL, b INT);
BEGIN;
INSERT INTO test_table (b) VALUES (100);
INSERT INTO test_table (b) VALUES (200);
INSERT INTO test_table (b) VALUES (300);
SELECT setval('test_table_a_seq', 3000);
setval
--------
3000
(1 row)
INSERT INTO test_table (b) VALUES (400);
INSERT INTO test_table (b) VALUES (500);
INSERT INTO test_table (b) VALUES (600);
ALTER SEQUENCE test_table_a_seq RESTART WITH 6000;
INSERT INTO test_table (b) VALUES (700);
INSERT INTO test_table (b) VALUES (800);
INSERT INTO test_table (b) VALUES (900);
ROLLBACK;
-- check table and sequence values after rollback
SELECT * from test_table_a_seq;
last_value | log_cnt | is_called
------------+---------+-----------
3003 | 30 | t
(1 row)
SELECT nextval('test_table_a_seq');
nextval
---------
3004
(1 row)
DROP TABLE test_table;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data
-------------------------------------------------------------------------------------------
BEGIN
sequence public.test_table_a_seq: transactional:1 last_value: 1 log_cnt: 0 is_called:0
COMMIT
sequence public.test_table_a_seq: transactional:0 last_value: 33 log_cnt: 0 is_called:1
sequence public.test_table_a_seq: transactional:0 last_value: 3000 log_cnt: 0 is_called:1
sequence public.test_table_a_seq: transactional:0 last_value: 3033 log_cnt: 0 is_called:1
(6 rows)
-- savepoint test on table with serial column
BEGIN;
CREATE TABLE test_table (a SERIAL, b INT);
INSERT INTO test_table (b) VALUES (100);
INSERT INTO test_table (b) VALUES (200);
SAVEPOINT a;
INSERT INTO test_table (b) VALUES (300);
ROLLBACK TO SAVEPOINT a;
DROP TABLE test_table;
COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data
-----------------------------------------------------------------------------------------
BEGIN
sequence public.test_table_a_seq: transactional:1 last_value: 1 log_cnt: 0 is_called:0
sequence public.test_table_a_seq: transactional:1 last_value: 33 log_cnt: 0 is_called:1
table public.test_table: INSERT: a[integer]:1 b[integer]:100
table public.test_table: INSERT: a[integer]:2 b[integer]:200
COMMIT
(6 rows)
-- savepoint test on table with serial column
BEGIN;
CREATE SEQUENCE test_sequence;
SELECT nextval('test_sequence');
nextval
---------
1
(1 row)
SELECT setval('test_sequence', 3000);
setval
--------
3000
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3001
(1 row)
SAVEPOINT a;
ALTER SEQUENCE test_sequence START WITH 7000;
SELECT setval('test_sequence', 5000);
setval
--------
5000
(1 row)
ROLLBACK TO SAVEPOINT a;
SELECT * FROM test_sequence;
last_value | log_cnt | is_called
------------+---------+-----------
3001 | 32 | t
(1 row)
DROP SEQUENCE test_sequence;
COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data
----------------------------------------------------------------------------------------
BEGIN
sequence public.test_sequence: transactional:1 last_value: 1 log_cnt: 0 is_called:0
sequence public.test_sequence: transactional:1 last_value: 33 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:1 last_value: 3000 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:1 last_value: 3033 log_cnt: 0 is_called:1
COMMIT
(6 rows)
SELECT pg_drop_replication_slot('regression_slot');
pg_drop_replication_slot
--------------------------
(1 row)

View File

@ -95,7 +95,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot2', 'test_
(1 row) (1 row)
INSERT INTO replication_example(somedata, text) VALUES (1, 3); INSERT INTO replication_example(somedata, text) VALUES (1, 3);
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
BEGIN BEGIN
@ -107,7 +107,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'in
COMMIT COMMIT
(7 rows) (7 rows)
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
BEGIN BEGIN
@ -132,7 +132,7 @@ SELECT :'wal_lsn' = :'end_lsn';
t t
(1 row) (1 row)
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
BEGIN BEGIN
@ -140,7 +140,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'in
COMMIT COMMIT
(3 rows) (3 rows)
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
------ ------
(0 rows) (0 rows)

View File

@ -52,7 +52,7 @@ CREATE TABLE toasted_copy (
); );
ALTER TABLE toasted_copy ALTER COLUMN data SET STORAGE EXTERNAL; ALTER TABLE toasted_copy ALTER COLUMN data SET STORAGE EXTERNAL;
\copy toasted_copy FROM STDIN \copy toasted_copy FROM STDIN
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
substr substr
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN BEGIN
@ -316,7 +316,7 @@ SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several;
t t
(1 row) (1 row)
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
regexp_replace regexp_replace
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN BEGIN
@ -327,7 +327,7 @@ SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_sl
-- test update of a toasted key without changing it -- test update of a toasted key without changing it
UPDATE toasted_several SET toasted_col1 = toasted_key; UPDATE toasted_several SET toasted_col1 = toasted_key;
UPDATE toasted_several SET toasted_col2 = toasted_col1; UPDATE toasted_several SET toasted_col2 = toasted_col1;
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
regexp_replace regexp_replace
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN BEGIN
@ -350,7 +350,7 @@ UPDATE toasted_several SET toasted_col1 = toasted_col2 WHERE id = 1;
DELETE FROM toasted_several WHERE id = 1; DELETE FROM toasted_several WHERE id = 1;
COMMIT; COMMIT;
DROP TABLE toasted_several; DROP TABLE toasted_several;
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0') SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')
WHERE data NOT LIKE '%INSERT: %'; WHERE data NOT LIKE '%INSERT: %';
regexp_replace regexp_replace
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -373,7 +373,7 @@ INSERT INTO tbl1 VALUES(1, repeat('a', 4000)) ;
ALTER TABLE tbl1 ADD COLUMN id serial primary key; ALTER TABLE tbl1 ADD COLUMN id serial primary key;
INSERT INTO tbl2 VALUES(1); INSERT INTO tbl2 VALUES(1);
commit; commit;
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
substr substr
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN BEGIN

View File

@ -11,7 +11,7 @@ CREATE TABLE tab2 (a int primary key, b int);
TRUNCATE tab1; TRUNCATE tab1;
TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE; TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE;
TRUNCATE tab1, tab2; TRUNCATE tab1, tab2;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data data
------------------------------------------------------ ------------------------------------------------------
BEGIN BEGIN

View File

@ -13,7 +13,7 @@ teardown
session "s0" session "s0"
setup { SET synchronous_commit=on; } setup { SET synchronous_commit=on; }
step "s0init" {SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding');} step "s0init" {SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding');}
step "s0start" {SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');} step "s0start" {SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');}
step "s0alter" {ALTER TABLE do_write ADD column ts timestamptz; } step "s0alter" {ALTER TABLE do_write ADD column ts timestamptz; }
step "s0w" { INSERT INTO do_write DEFAULT VALUES; } step "s0w" { INSERT INTO do_write DEFAULT VALUES; }

View File

@ -16,7 +16,7 @@ session "s1"
setup { SET synchronous_commit=on; } setup { SET synchronous_commit=on; }
step "s1init" {SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding');} step "s1init" {SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding');}
step "s1start" {SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');} step "s1start" {SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');}
step "s1insert" { INSERT INTO do_write DEFAULT VALUES; } step "s1insert" { INSERT INTO do_write DEFAULT VALUES; }
step "s1checkpoint" { CHECKPOINT; } step "s1checkpoint" { CHECKPOINT; }
step "s1alter" { ALTER TABLE do_write ADD COLUMN addedbys1 int; } step "s1alter" { ALTER TABLE do_write ADD COLUMN addedbys1 int; }

View File

@ -19,7 +19,7 @@ SELECT pg_drop_replication_slot('regression_slot');
-- check that we're detecting a streaming rep slot used for logical decoding -- check that we're detecting a streaming rep slot used for logical decoding
SELECT 'init' FROM pg_create_physical_replication_slot('repl'); SELECT 'init' FROM pg_create_physical_replication_slot('repl');
SELECT data FROM pg_logical_slot_get_changes('repl', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('repl', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT pg_drop_replication_slot('repl'); SELECT pg_drop_replication_slot('repl');
@ -64,11 +64,11 @@ ALTER TABLE replication_example RENAME COLUMN text TO somenum;
INSERT INTO replication_example(somedata, somenum) VALUES (4, 1); INSERT INTO replication_example(somedata, somenum) VALUES (4, 1);
-- collect all changes -- collect all changes
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
ALTER TABLE replication_example ALTER COLUMN somenum TYPE int4 USING (somenum::int4); ALTER TABLE replication_example ALTER COLUMN somenum TYPE int4 USING (somenum::int4);
-- check that this doesn't produce any changes from the heap rewrite -- check that this doesn't produce any changes from the heap rewrite
SELECT count(data) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT count(data) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
INSERT INTO replication_example(somedata, somenum) VALUES (5, 1); INSERT INTO replication_example(somedata, somenum) VALUES (5, 1);
@ -82,7 +82,7 @@ INSERT INTO replication_example(somedata, somenum, zaphod1) VALUES (6, 4, 2);
COMMIT; COMMIT;
-- show changes -- show changes
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- ON CONFLICT DO UPDATE support -- ON CONFLICT DO UPDATE support
BEGIN; BEGIN;
@ -91,7 +91,7 @@ INSERT INTO replication_example(id, somedata, somenum) SELECT i, i, i FROM gener
COMMIT; COMMIT;
/* display results */ /* display results */
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- MERGE support -- MERGE support
BEGIN; BEGIN;
@ -113,14 +113,14 @@ CREATE TABLE tr_unique(id2 serial unique NOT NULL, data int);
INSERT INTO tr_unique(data) VALUES(10); INSERT INTO tr_unique(data) VALUES(10);
ALTER TABLE tr_unique RENAME TO tr_pkey; ALTER TABLE tr_unique RENAME TO tr_pkey;
ALTER TABLE tr_pkey ADD COLUMN id serial primary key; ALTER TABLE tr_pkey ADD COLUMN id serial primary key;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-rewrites', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-rewrites', '1');
INSERT INTO tr_pkey(data) VALUES(1); INSERT INTO tr_pkey(data) VALUES(1);
--show deletion with primary key --show deletion with primary key
DELETE FROM tr_pkey; DELETE FROM tr_pkey;
/* display results */ /* display results */
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
/* /*
* check that disk spooling works (also for logical messages) * check that disk spooling works (also for logical messages)
@ -137,7 +137,7 @@ COMMIT;
/* display results, but hide most of the output */ /* display results, but hide most of the output */
SELECT count(*), min(data), max(data) SELECT count(*), min(data), max(data)
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')
GROUP BY substring(data, 1, 24) GROUP BY substring(data, 1, 24)
ORDER BY 1,2; ORDER BY 1,2;
@ -152,7 +152,7 @@ DROP TABLE spoolme;
COMMIT; COMMIT;
SELECT data SELECT data
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')
WHERE data ~ 'UPDATE'; WHERE data ~ 'UPDATE';
-- check that a large, spooled, upsert works -- check that a large, spooled, upsert works
@ -161,7 +161,7 @@ SELECT g.i, -g.i FROM generate_series(8000, 12000) g(i)
ON CONFLICT(id) DO UPDATE SET data = EXCLUDED.data; ON CONFLICT(id) DO UPDATE SET data = EXCLUDED.data;
SELECT substring(data, 1, 29), count(*) SELECT substring(data, 1, 29), count(*)
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0') WITH ORDINALITY FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1') WITH ORDINALITY
GROUP BY 1 GROUP BY 1
ORDER BY min(ordinality); ORDER BY min(ordinality);
@ -189,7 +189,7 @@ INSERT INTO tr_sub(path) VALUES ('1-top-2-#1');
RELEASE SAVEPOINT b; RELEASE SAVEPOINT b;
COMMIT; COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- check that we handle xlog assignments correctly -- check that we handle xlog assignments correctly
BEGIN; BEGIN;
@ -218,7 +218,7 @@ RELEASE SAVEPOINT subtop;
INSERT INTO tr_sub(path) VALUES ('2-top-#1'); INSERT INTO tr_sub(path) VALUES ('2-top-#1');
COMMIT; COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- make sure rollbacked subtransactions aren't decoded -- make sure rollbacked subtransactions aren't decoded
BEGIN; BEGIN;
@ -231,7 +231,7 @@ ROLLBACK TO SAVEPOINT b;
INSERT INTO tr_sub(path) VALUES ('3-top-2-#2'); INSERT INTO tr_sub(path) VALUES ('3-top-2-#2');
COMMIT; COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- test whether a known, but not yet logged toplevel xact, followed by a -- test whether a known, but not yet logged toplevel xact, followed by a
-- subxact commit is handled correctly -- subxact commit is handled correctly
@ -250,7 +250,7 @@ INSERT INTO tr_sub(path) VALUES ('5-top-1-#1');
COMMIT; COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- check that DDL in aborted subtransactions handled correctly -- check that DDL in aborted subtransactions handled correctly
CREATE TABLE tr_sub_ddl(data int); CREATE TABLE tr_sub_ddl(data int);
@ -263,7 +263,7 @@ ALTER TABLE tr_sub_ddl ALTER COLUMN data TYPE bigint;
INSERT INTO tr_sub_ddl VALUES(43); INSERT INTO tr_sub_ddl VALUES(43);
COMMIT; COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
/* /*
@ -303,7 +303,7 @@ ALTER TABLE replication_metadata SET (user_catalog_table = false);
INSERT INTO replication_metadata(relation, options) INSERT INTO replication_metadata(relation, options)
VALUES ('zaphod', NULL); VALUES ('zaphod', NULL);
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
/* /*
* check whether we handle updates/deletes correct with & without a pkey * check whether we handle updates/deletes correct with & without a pkey
@ -414,7 +414,7 @@ UPDATE toasttable
SET toasted_col1 = (SELECT string_agg(g.i::text, '') FROM generate_series(1, 2000) g(i)) SET toasted_col1 = (SELECT string_agg(g.i::text, '') FROM generate_series(1, 2000) g(i))
WHERE id = 1; WHERE id = 1;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
INSERT INTO toasttable(toasted_col1) SELECT string_agg(g.i::text, '') FROM generate_series(1, 2000) g(i); INSERT INTO toasttable(toasted_col1) SELECT string_agg(g.i::text, '') FROM generate_series(1, 2000) g(i);
@ -426,10 +426,10 @@ WHERE id = 1;
-- make sure we decode correctly even if the toast table is gone -- make sure we decode correctly even if the toast table is gone
DROP TABLE toasttable; DROP TABLE toasttable;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- done, free logical replication slot -- done, free logical replication slot
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT pg_drop_replication_slot('regression_slot'); SELECT pg_drop_replication_slot('regression_slot');

View File

@ -32,10 +32,10 @@ BEGIN;
SELECT pg_current_xact_id() = '0'; SELECT pg_current_xact_id() = '0';
-- don't show yet, haven't committed -- don't show yet, haven't committed
INSERT INTO nobarf(data) VALUES('2'); INSERT INTO nobarf(data) VALUES('2');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
COMMIT; COMMIT;
INSERT INTO nobarf(data) VALUES('3'); INSERT INTO nobarf(data) VALUES('3');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT 'stop' FROM pg_drop_replication_slot('regression_slot'); SELECT 'stop' FROM pg_drop_replication_slot('regression_slot');

View File

@ -15,14 +15,14 @@ CREATE TABLE somechange(id serial primary key);
INSERT INTO somechange DEFAULT VALUES; INSERT INTO somechange DEFAULT VALUES;
CREATE TABLE changeresult AS CREATE TABLE changeresult AS
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT * FROM changeresult; SELECT * FROM changeresult;
INSERT INTO changeresult INSERT INTO changeresult
SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
INSERT INTO changeresult INSERT INTO changeresult
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT * FROM changeresult; SELECT * FROM changeresult;
DROP TABLE changeresult; DROP TABLE changeresult;
@ -32,11 +32,11 @@ DROP TABLE somechange;
CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$ CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$
BEGIN BEGIN
RETURN QUERY RETURN QUERY
SELECT data FROM pg_logical_slot_peek_changes(slot_name, NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_peek_changes(slot_name, NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
END$$ LANGUAGE plpgsql; END$$ LANGUAGE plpgsql;
SELECT * FROM slot_changes_wrapper('regression_slot'); SELECT * FROM slot_changes_wrapper('regression_slot');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT 'stop' FROM pg_drop_replication_slot('regression_slot'); SELECT 'stop' FROM pg_drop_replication_slot('regression_slot');

View File

@ -41,10 +41,10 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
-- origin tx -- origin tx
INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again'); INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again');
INSERT INTO target_tbl(data) INSERT INTO target_tbl(data)
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- as is normal, the insert into target_tbl shows up -- as is normal, the insert into target_tbl shows up
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
INSERT INTO origin_tbl(data) VALUES ('will be replicated, but not decoded again'); INSERT INTO origin_tbl(data) VALUES ('will be replicated, but not decoded again');
@ -60,7 +60,7 @@ BEGIN;
-- setup transaction origin -- setup transaction origin
SELECT pg_replication_origin_xact_setup('0/aabbccdd', '2013-01-01 00:00'); SELECT pg_replication_origin_xact_setup('0/aabbccdd', '2013-01-01 00:00');
INSERT INTO target_tbl(data) INSERT INTO target_tbl(data)
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
COMMIT; COMMIT;
-- check replication progress for the session is correct -- check replication progress for the session is correct
@ -79,11 +79,11 @@ SELECT pg_replication_origin_progress('regress_test_decoding: regression_slot',
SELECT pg_replication_origin_session_reset(); SELECT pg_replication_origin_session_reset();
-- and magically the replayed xact will be filtered! -- and magically the replayed xact will be filtered!
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
--but new original changes still show up --but new original changes still show up
INSERT INTO origin_tbl(data) VALUES ('will be replicated'); INSERT INTO origin_tbl(data) VALUES ('will be replicated');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
SELECT pg_drop_replication_slot('regression_slot'); SELECT pg_drop_replication_slot('regression_slot');
SELECT pg_replication_origin_drop('regress_test_decoding: regression_slot'); SELECT pg_replication_origin_drop('regress_test_decoding: regression_slot');
@ -114,7 +114,7 @@ SELECT local_id, external_id,
remote_lsn <> '0/0' AS valid_remote_lsn, remote_lsn <> '0/0' AS valid_remote_lsn,
local_lsn <> '0/0' AS valid_local_lsn local_lsn <> '0/0' AS valid_local_lsn
FROM pg_replication_origin_status; FROM pg_replication_origin_status;
SELECT data FROM pg_logical_slot_get_changes('regression_slot_no_lsn', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot_no_lsn', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0');
-- Clean up -- Clean up
SELECT pg_replication_origin_session_reset(); SELECT pg_replication_origin_session_reset();
SELECT pg_drop_replication_slot('regression_slot_no_lsn'); SELECT pg_drop_replication_slot('regression_slot_no_lsn');

View File

@ -35,7 +35,7 @@ SELECT pg_relation_size((SELECT reltoastrelid FROM pg_class WHERE oid = 'pg_shde
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding'); SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120)); CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120));
INSERT INTO replication_example(somedata) VALUES (1); INSERT INTO replication_example(somedata) VALUES (1);
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
BEGIN; BEGIN;
INSERT INTO replication_example(somedata) VALUES (2); INSERT INTO replication_example(somedata) VALUES (2);
@ -90,7 +90,7 @@ COMMIT;
-- make old files go away -- make old files go away
CHECKPOINT; CHECKPOINT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- trigger repeated rewrites of a system catalog with a toast table, -- trigger repeated rewrites of a system catalog with a toast table,
-- that previously was buggy: 20180914021046.oi7dm4ra3ot2g2kt@alap3.anarazel.de -- that previously was buggy: 20180914021046.oi7dm4ra3ot2g2kt@alap3.anarazel.de
@ -98,7 +98,7 @@ VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; V
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (8, 6, 1); INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (8, 6, 1);
VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; VACUUM FULL iamalargetable; VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; VACUUM FULL iamalargetable;
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (9, 7, 1); INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (9, 7, 1);
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT pg_drop_replication_slot('regression_slot'); SELECT pg_drop_replication_slot('regression_slot');
DROP TABLE IF EXISTS replication_example; DROP TABLE IF EXISTS replication_example;

View File

@ -1,119 +0,0 @@
-- predictability
SET synchronous_commit = on;
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
CREATE SEQUENCE test_sequence;
-- test the sequence changes by several nextval() calls
SELECT nextval('test_sequence');
SELECT nextval('test_sequence');
SELECT nextval('test_sequence');
SELECT nextval('test_sequence');
-- test the sequence changes by several ALTER commands
ALTER SEQUENCE test_sequence INCREMENT BY 10;
SELECT nextval('test_sequence');
ALTER SEQUENCE test_sequence START WITH 3000;
ALTER SEQUENCE test_sequence MAXVALUE 10000;
ALTER SEQUENCE test_sequence RESTART WITH 4000;
SELECT nextval('test_sequence');
-- test the sequence changes by several setval() calls
SELECT setval('test_sequence', 3500);
SELECT nextval('test_sequence');
SELECT setval('test_sequence', 3500, true);
SELECT nextval('test_sequence');
SELECT setval('test_sequence', 3500, false);
SELECT nextval('test_sequence');
-- show results and drop sequence
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
DROP SEQUENCE test_sequence;
-- rollback on sequence creation and update
BEGIN;
CREATE SEQUENCE test_sequence;
CREATE TABLE test_table (a INT);
SELECT nextval('test_sequence');
SELECT nextval('test_sequence');
SELECT nextval('test_sequence');
SELECT setval('test_sequence', 3000);
SELECT nextval('test_sequence');
SELECT nextval('test_sequence');
SELECT nextval('test_sequence');
ALTER SEQUENCE test_sequence RESTART WITH 6000;
INSERT INTO test_table VALUES( (SELECT nextval('test_sequence')) );
SELECT nextval('test_sequence');
ROLLBACK;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- rollback on table creation with serial column
BEGIN;
CREATE TABLE test_table (a SERIAL, b INT);
INSERT INTO test_table (b) VALUES (100);
INSERT INTO test_table (b) VALUES (200);
INSERT INTO test_table (b) VALUES (300);
SELECT setval('test_table_a_seq', 3000);
INSERT INTO test_table (b) VALUES (400);
INSERT INTO test_table (b) VALUES (500);
INSERT INTO test_table (b) VALUES (600);
ALTER SEQUENCE test_table_a_seq RESTART WITH 6000;
INSERT INTO test_table (b) VALUES (700);
INSERT INTO test_table (b) VALUES (800);
INSERT INTO test_table (b) VALUES (900);
ROLLBACK;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- rollback on table with serial column
CREATE TABLE test_table (a SERIAL, b INT);
BEGIN;
INSERT INTO test_table (b) VALUES (100);
INSERT INTO test_table (b) VALUES (200);
INSERT INTO test_table (b) VALUES (300);
SELECT setval('test_table_a_seq', 3000);
INSERT INTO test_table (b) VALUES (400);
INSERT INTO test_table (b) VALUES (500);
INSERT INTO test_table (b) VALUES (600);
ALTER SEQUENCE test_table_a_seq RESTART WITH 6000;
INSERT INTO test_table (b) VALUES (700);
INSERT INTO test_table (b) VALUES (800);
INSERT INTO test_table (b) VALUES (900);
ROLLBACK;
-- check table and sequence values after rollback
SELECT * from test_table_a_seq;
SELECT nextval('test_table_a_seq');
DROP TABLE test_table;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- savepoint test on table with serial column
BEGIN;
CREATE TABLE test_table (a SERIAL, b INT);
INSERT INTO test_table (b) VALUES (100);
INSERT INTO test_table (b) VALUES (200);
SAVEPOINT a;
INSERT INTO test_table (b) VALUES (300);
ROLLBACK TO SAVEPOINT a;
DROP TABLE test_table;
COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- savepoint test on table with serial column
BEGIN;
CREATE SEQUENCE test_sequence;
SELECT nextval('test_sequence');
SELECT setval('test_sequence', 3000);
SELECT nextval('test_sequence');
SAVEPOINT a;
ALTER SEQUENCE test_sequence START WITH 7000;
SELECT setval('test_sequence', 5000);
ROLLBACK TO SAVEPOINT a;
SELECT * FROM test_sequence;
DROP SEQUENCE test_sequence;
COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT pg_drop_replication_slot('regression_slot');

View File

@ -50,8 +50,8 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot2', 'test_
INSERT INTO replication_example(somedata, text) VALUES (1, 3); INSERT INTO replication_example(somedata, text) VALUES (1, 3);
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
INSERT INTO replication_example(somedata, text) VALUES (1, 4); INSERT INTO replication_example(somedata, text) VALUES (1, 4);
INSERT INTO replication_example(somedata, text) VALUES (1, 5); INSERT INTO replication_example(somedata, text) VALUES (1, 5);
@ -65,8 +65,8 @@ SELECT slot_name FROM pg_replication_slot_advance('regression_slot2', pg_current
SELECT :'wal_lsn' = :'end_lsn'; SELECT :'wal_lsn' = :'end_lsn';
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
DROP TABLE replication_example; DROP TABLE replication_example;

View File

@ -264,7 +264,7 @@ ALTER TABLE toasted_copy ALTER COLUMN data SET STORAGE EXTERNAL;
202 untoasted199 202 untoasted199
203 untoasted200 203 untoasted200
\. \.
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- test we can decode "old" tuples bigger than the max heap tuple size correctly -- test we can decode "old" tuples bigger than the max heap tuple size correctly
DROP TABLE IF EXISTS toasted_several; DROP TABLE IF EXISTS toasted_several;
@ -287,13 +287,13 @@ UPDATE pg_attribute SET attstorage = 'x' WHERE attrelid = 'toasted_several_pkey'
INSERT INTO toasted_several(toasted_key) VALUES(repeat('9876543210', 10000)); INSERT INTO toasted_several(toasted_key) VALUES(repeat('9876543210', 10000));
SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several; SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several;
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-- test update of a toasted key without changing it -- test update of a toasted key without changing it
UPDATE toasted_several SET toasted_col1 = toasted_key; UPDATE toasted_several SET toasted_col1 = toasted_key;
UPDATE toasted_several SET toasted_col2 = toasted_col1; UPDATE toasted_several SET toasted_col2 = toasted_col1;
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
/* /*
* update with large tuplebuf, in a transaction large enough to force to spool to disk * update with large tuplebuf, in a transaction large enough to force to spool to disk
@ -306,7 +306,7 @@ COMMIT;
DROP TABLE toasted_several; DROP TABLE toasted_several;
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0') SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')
WHERE data NOT LIKE '%INSERT: %'; WHERE data NOT LIKE '%INSERT: %';
/* /*
@ -322,6 +322,6 @@ INSERT INTO tbl1 VALUES(1, repeat('a', 4000)) ;
ALTER TABLE tbl1 ADD COLUMN id serial primary key; ALTER TABLE tbl1 ADD COLUMN id serial primary key;
INSERT INTO tbl2 VALUES(1); INSERT INTO tbl2 VALUES(1);
commit; commit;
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT pg_drop_replication_slot('regression_slot'); SELECT pg_drop_replication_slot('regression_slot');

View File

@ -10,5 +10,5 @@ TRUNCATE tab1;
TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE; TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE;
TRUNCATE tab1, tab2; TRUNCATE tab1, tab2;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT pg_drop_replication_slot('regression_slot'); SELECT pg_drop_replication_slot('regression_slot');

View File

@ -35,7 +35,6 @@ typedef struct
bool include_timestamp; bool include_timestamp;
bool skip_empty_xacts; bool skip_empty_xacts;
bool only_local; bool only_local;
bool include_sequences;
} TestDecodingData; } TestDecodingData;
/* /*
@ -77,10 +76,6 @@ static void pg_decode_message(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, XLogRecPtr message_lsn, ReorderBufferTXN *txn, XLogRecPtr message_lsn,
bool transactional, const char *prefix, bool transactional, const char *prefix,
Size sz, const char *message); Size sz, const char *message);
static void pg_decode_sequence(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, XLogRecPtr sequence_lsn,
Relation rel, bool transactional,
int64 last_value, int64 log_cnt, bool is_called);
static bool pg_decode_filter_prepare(LogicalDecodingContext *ctx, static bool pg_decode_filter_prepare(LogicalDecodingContext *ctx,
TransactionId xid, TransactionId xid,
const char *gid); const char *gid);
@ -121,10 +116,6 @@ static void pg_decode_stream_message(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, XLogRecPtr message_lsn, ReorderBufferTXN *txn, XLogRecPtr message_lsn,
bool transactional, const char *prefix, bool transactional, const char *prefix,
Size sz, const char *message); Size sz, const char *message);
static void pg_decode_stream_sequence(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, XLogRecPtr sequence_lsn,
Relation rel, bool transactional,
int64 last_value, int64 log_cnt, bool is_called);
static void pg_decode_stream_truncate(LogicalDecodingContext *ctx, static void pg_decode_stream_truncate(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, ReorderBufferTXN *txn,
int nrelations, Relation relations[], int nrelations, Relation relations[],
@ -150,7 +141,6 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
cb->filter_by_origin_cb = pg_decode_filter; cb->filter_by_origin_cb = pg_decode_filter;
cb->shutdown_cb = pg_decode_shutdown; cb->shutdown_cb = pg_decode_shutdown;
cb->message_cb = pg_decode_message; cb->message_cb = pg_decode_message;
cb->sequence_cb = pg_decode_sequence;
cb->filter_prepare_cb = pg_decode_filter_prepare; cb->filter_prepare_cb = pg_decode_filter_prepare;
cb->begin_prepare_cb = pg_decode_begin_prepare_txn; cb->begin_prepare_cb = pg_decode_begin_prepare_txn;
cb->prepare_cb = pg_decode_prepare_txn; cb->prepare_cb = pg_decode_prepare_txn;
@ -163,7 +153,6 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
cb->stream_commit_cb = pg_decode_stream_commit; cb->stream_commit_cb = pg_decode_stream_commit;
cb->stream_change_cb = pg_decode_stream_change; cb->stream_change_cb = pg_decode_stream_change;
cb->stream_message_cb = pg_decode_stream_message; cb->stream_message_cb = pg_decode_stream_message;
cb->stream_sequence_cb = pg_decode_stream_sequence;
cb->stream_truncate_cb = pg_decode_stream_truncate; cb->stream_truncate_cb = pg_decode_stream_truncate;
} }
@ -184,7 +173,6 @@ pg_decode_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
data->include_xids = true; data->include_xids = true;
data->include_timestamp = false; data->include_timestamp = false;
data->skip_empty_xacts = false; data->skip_empty_xacts = false;
data->include_sequences = true;
data->only_local = false; data->only_local = false;
ctx->output_plugin_private = data; ctx->output_plugin_private = data;
@ -277,17 +265,6 @@ pg_decode_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
errmsg("could not parse value \"%s\" for parameter \"%s\"", errmsg("could not parse value \"%s\" for parameter \"%s\"",
strVal(elem->arg), elem->defname))); strVal(elem->arg), elem->defname)));
} }
else if (strcmp(elem->defname, "include-sequences") == 0)
{
if (elem->arg == NULL)
data->include_sequences = false;
else if (!parse_bool(strVal(elem->arg), &data->include_sequences))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not parse value \"%s\" for parameter \"%s\"",
strVal(elem->arg), elem->defname)));
}
else else
{ {
ereport(ERROR, ereport(ERROR,
@ -779,38 +756,6 @@ pg_decode_message(LogicalDecodingContext *ctx,
OutputPluginWrite(ctx, true); OutputPluginWrite(ctx, true);
} }
static void
pg_decode_sequence(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn, Relation rel,
bool transactional,
int64 last_value, int64 log_cnt, bool is_called)
{
TestDecodingData *data = ctx->output_plugin_private;
TestDecodingTxnData *txndata = txn->output_plugin_private;
if (!data->include_sequences)
return;
/* output BEGIN if we haven't yet, but only for the transactional case */
if (transactional)
{
if (data->skip_empty_xacts && !txndata->xact_wrote_changes)
{
pg_output_begin(ctx, data, txn, false);
}
txndata->xact_wrote_changes = true;
}
OutputPluginPrepareWrite(ctx, true);
appendStringInfoString(ctx->out, "sequence ");
appendStringInfoString(ctx->out,
quote_qualified_identifier(get_namespace_name(get_rel_namespace(RelationGetRelid(rel))),
RelationGetRelationName(rel)));
appendStringInfo(ctx->out, ": transactional:%d last_value: " INT64_FORMAT " log_cnt: " INT64_FORMAT " is_called:%d",
transactional, last_value, log_cnt, is_called);
OutputPluginWrite(ctx, true);
}
static void static void
pg_decode_stream_start(LogicalDecodingContext *ctx, pg_decode_stream_start(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn) ReorderBufferTXN *txn)
@ -1010,38 +955,6 @@ pg_decode_stream_message(LogicalDecodingContext *ctx,
OutputPluginWrite(ctx, true); OutputPluginWrite(ctx, true);
} }
static void
pg_decode_stream_sequence(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn, Relation rel,
bool transactional,
int64 last_value, int64 log_cnt, bool is_called)
{
TestDecodingData *data = ctx->output_plugin_private;
TestDecodingTxnData *txndata = txn->output_plugin_private;
if (!data->include_sequences)
return;
/* output BEGIN if we haven't yet, but only for the transactional case */
if (transactional)
{
if (data->skip_empty_xacts && !txndata->xact_wrote_changes)
{
pg_output_begin(ctx, data, txn, false);
}
txndata->xact_wrote_changes = true;
}
OutputPluginPrepareWrite(ctx, true);
appendStringInfoString(ctx->out, "streaming sequence ");
appendStringInfoString(ctx->out,
quote_qualified_identifier(get_namespace_name(get_rel_namespace(RelationGetRelid(rel))),
RelationGetRelationName(rel)));
appendStringInfo(ctx->out, ": transactional:%d last_value: " INT64_FORMAT " log_cnt: " INT64_FORMAT " is_called:%d",
transactional, last_value, log_cnt, is_called);
OutputPluginWrite(ctx, true);
}
/* /*
* In streaming mode, we don't display the detailed information of Truncate. * In streaming mode, we don't display the detailed information of Truncate.
* See pg_decode_stream_change. * See pg_decode_stream_change.

View File

@ -6354,16 +6354,6 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
Reference to schema Reference to schema
</para></entry> </para></entry>
</row> </row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pntype</structfield> <type>char</type>
Determines which object type is included from this schema.
</para>
<para>
Reference to schema
</para></entry>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
@ -9699,11 +9689,6 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
<entry>prepared transactions</entry> <entry>prepared transactions</entry>
</row> </row>
<row>
<entry><link linkend="view-pg-publication-sequences"><structname>pg_publication_sequences</structname></link></entry>
<entry>publications and their associated sequences</entry>
</row>
<row> <row>
<entry><link linkend="view-pg-publication-tables"><structname>pg_publication_tables</structname></link></entry> <entry><link linkend="view-pg-publication-tables"><structname>pg_publication_tables</structname></link></entry>
<entry>publications and their associated tables</entry> <entry>publications and their associated tables</entry>
@ -11641,72 +11626,6 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1> </sect1>
<sect1 id="view-pg-publication-sequences">
<title><structname>pg_publication_sequences</structname></title>
<indexterm zone="view-pg-publication-sequences">
<primary>pg_publication_sequences</primary>
</indexterm>
<para>
The view <structname>pg_publication_sequences</structname> provides
information about the mapping between publications and the sequences they
contain. Unlike the underlying catalog
<link linkend="catalog-pg-publication-rel"><structname>pg_publication_rel</structname></link>,
this view expands
publications defined as <literal>FOR ALL SEQUENCES</literal>, so for such
publications there will be a row for each eligible sequence.
</para>
<table>
<title><structname>pg_publication_sequences</structname> Columns</title>
<tgroup cols="1">
<thead>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
Column Type
</para>
<para>
Description
</para></entry>
</row>
</thead>
<tbody>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pubname</structfield> <type>name</type>
(references <link linkend="catalog-pg-publication"><structname>pg_publication</structname></link>.<structfield>pubname</structfield>)
</para>
<para>
Name of publication
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>schemaname</structfield> <type>name</type>
(references <link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.<structfield>nspname</structfield>)
</para>
<para>
Name of schema containing sequence
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>sequencename</structfield> <type>name</type>
(references <link linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>relname</structfield>)
</para>
<para>
Name of sequence
</para></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="view-pg-publication-tables"> <sect1 id="view-pg-publication-tables">
<title><structname>pg_publication_tables</structname></title> <title><structname>pg_publication_tables</structname></title>

View File

@ -458,7 +458,6 @@ typedef struct OutputPluginCallbacks
LogicalDecodeTruncateCB truncate_cb; LogicalDecodeTruncateCB truncate_cb;
LogicalDecodeCommitCB commit_cb; LogicalDecodeCommitCB commit_cb;
LogicalDecodeMessageCB message_cb; LogicalDecodeMessageCB message_cb;
LogicalDecodeSequenceCB sequence_cb;
LogicalDecodeFilterByOriginCB filter_by_origin_cb; LogicalDecodeFilterByOriginCB filter_by_origin_cb;
LogicalDecodeShutdownCB shutdown_cb; LogicalDecodeShutdownCB shutdown_cb;
LogicalDecodeFilterPrepareCB filter_prepare_cb; LogicalDecodeFilterPrepareCB filter_prepare_cb;
@ -473,7 +472,6 @@ typedef struct OutputPluginCallbacks
LogicalDecodeStreamCommitCB stream_commit_cb; LogicalDecodeStreamCommitCB stream_commit_cb;
LogicalDecodeStreamChangeCB stream_change_cb; LogicalDecodeStreamChangeCB stream_change_cb;
LogicalDecodeStreamMessageCB stream_message_cb; LogicalDecodeStreamMessageCB stream_message_cb;
LogicalDecodeStreamSequenceCB stream_sequence_cb;
LogicalDecodeStreamTruncateCB stream_truncate_cb; LogicalDecodeStreamTruncateCB stream_truncate_cb;
} OutputPluginCallbacks; } OutputPluginCallbacks;
@ -483,11 +481,9 @@ typedef void (*LogicalOutputPluginInit) (struct OutputPluginCallbacks *cb);
and <function>commit_cb</function> callbacks are required, and <function>commit_cb</function> callbacks are required,
while <function>startup_cb</function>, while <function>startup_cb</function>,
<function>filter_by_origin_cb</function>, <function>truncate_cb</function>, <function>filter_by_origin_cb</function>, <function>truncate_cb</function>,
<function>sequence_cb</function>, and <function>shutdown_cb</function> are and <function>shutdown_cb</function> are optional.
optional. If <function>truncate_cb</function> is not set but a If <function>truncate_cb</function> is not set but a
<command>TRUNCATE</command> is to be decoded, the action will be ignored. <command>TRUNCATE</command> is to be decoded, the action will be ignored.
Similarly, if <function>sequence_cb</function> is not set and a sequence
change is to be decoded, the action will be ignored.
</para> </para>
<para> <para>
@ -496,8 +492,7 @@ typedef void (*LogicalOutputPluginInit) (struct OutputPluginCallbacks *cb);
<function>stream_stop_cb</function>, <function>stream_abort_cb</function>, <function>stream_stop_cb</function>, <function>stream_abort_cb</function>,
<function>stream_commit_cb</function>, <function>stream_change_cb</function>, <function>stream_commit_cb</function>, <function>stream_change_cb</function>,
and <function>stream_prepare_cb</function> and <function>stream_prepare_cb</function>
are required, while <function>stream_message_cb</function>, are required, while <function>stream_message_cb</function> and
<function>stream_sequence_cb</function>, and
<function>stream_truncate_cb</function> are optional. <function>stream_truncate_cb</function> are optional.
</para> </para>
@ -813,35 +808,6 @@ typedef void (*LogicalDecodeMessageCB) (struct LogicalDecodingContext *ctx,
</para> </para>
</sect3> </sect3>
<sect3 id="logicaldecoding-output-plugin-sequence">
<title>Sequence Callback</title>
<para>
The optional <function>sequence_cb</function> callback is called for
actions that update a sequence value.
<programlisting>
typedef void (*LogicalDecodeSequenceCB) (struct LogicalDecodingContext *ctx,
ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn,
Relation rel,
bool transactional,
int64 last_value,
int64 log_cnt,
bool is_called);
</programlisting>
The <parameter>txn</parameter> parameter contains meta information about
the transaction the sequence change is part of. Note however that for
non-transactional increments, the transaction may be either NULL or not
NULL, depending on if the transaction already has an XID assigned.
The <parameter>sequence_lsn</parameter> has the WAL location of the
sequence update. <parameter>transactional</parameter> says if the
sequence has to be replayed as part of the transaction or directly.
The <parameter>last_value</parameter>, <parameter>log_cnt</parameter> and
<parameter>is_called</parameter> parameters describe the sequence change.
</para>
</sect3>
<sect3 id="logicaldecoding-output-plugin-filter-prepare"> <sect3 id="logicaldecoding-output-plugin-filter-prepare">
<title>Prepare Filter Callback</title> <title>Prepare Filter Callback</title>
@ -1051,26 +1017,6 @@ typedef void (*LogicalDecodeStreamMessageCB) (struct LogicalDecodingContext *ctx
</para> </para>
</sect3> </sect3>
<sect3 id="logicaldecoding-output-plugin-stream-sequence">
<title>Stream Sequence Callback</title>
<para>
The optional <function>stream_sequence_cb</function> callback is called
for actions that change a sequence in a block of streamed changes
(demarcated by <function>stream_start_cb</function> and
<function>stream_stop_cb</function> calls).
<programlisting>
typedef void (*LogicalDecodeStreamSequenceCB) (struct LogicalDecodingContext *ctx,
ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn,
Relation rel,
bool transactional,
int64 last_value,
int64 log_cnt,
bool is_called);
</programlisting>
</para>
</sect3>
<sect3 id="logicaldecoding-output-plugin-stream-truncate"> <sect3 id="logicaldecoding-output-plugin-stream-truncate">
<title>Stream Truncate Callback</title> <title>Stream Truncate Callback</title>
<para> <para>
@ -1251,9 +1197,8 @@ OutputPluginWrite(ctx, true);
in-progress transactions. There are multiple required streaming callbacks in-progress transactions. There are multiple required streaming callbacks
(<function>stream_start_cb</function>, <function>stream_stop_cb</function>, (<function>stream_start_cb</function>, <function>stream_stop_cb</function>,
<function>stream_abort_cb</function>, <function>stream_commit_cb</function> <function>stream_abort_cb</function>, <function>stream_commit_cb</function>
and <function>stream_change_cb</function>) and multiple optional callbacks and <function>stream_change_cb</function>) and two optional callbacks
(<function>stream_message_cb</function>, <function>stream_sequence_cb</function>, (<function>stream_message_cb</function> and <function>stream_truncate_cb</function>).
and <function>stream_truncate_cb</function>).
Also, if streaming of two-phase commands is to be supported, then additional Also, if streaming of two-phase commands is to be supported, then additional
callbacks must be provided. (See <xref linkend="logicaldecoding-two-phase-commits"/> callbacks must be provided. (See <xref linkend="logicaldecoding-two-phase-commits"/>
for details). for details).

View File

@ -7072,125 +7072,6 @@ Relation
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="protocol-logicalrep-message-formats-Sequence">
<term>
Sequence
</term>
<listitem>
<para>
<variablelist>
<varlistentry>
<term>
Byte1('X')
</term>
<listitem>
<para>
Identifies the message as a sequence message.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Int32 (TransactionId)
</term>
<listitem>
<para>
Xid of the transaction (only present for streamed transactions).
This field is available since protocol version 2.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Int8(0)
</term>
<listitem>
<para>
Flags; currently unused.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Int64 (XLogRecPtr)
</term>
<listitem>
<para>
The LSN of the sequence increment.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
String
</term>
<listitem>
<para>
Namespace (empty string for <literal>pg_catalog</literal>).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
String
</term>
<listitem>
<para>
Relation name.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Int8
</term>
<listitem>
<para>
1 if the sequence update is transactional, 0 otherwise.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Int64
</term>
<listitem>
<para>
<structfield>last_value</structfield> value of the sequence.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Int64
</term>
<listitem>
<para>
<structfield>log_cnt</structfield> value of the sequence.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Int8
</term>
<listitem>
<para>
<structfield>is_called</structfield> value of the sequence.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</listitem>
</varlistentry>
<varlistentry id="protocol-logicalrep-message-formats-Type"> <varlistentry id="protocol-logicalrep-message-formats-Type">
<term> <term>
Type Type

View File

@ -31,9 +31,7 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
<phrase>where <replaceable class="parameter">publication_object</replaceable> is one of:</phrase> <phrase>where <replaceable class="parameter">publication_object</replaceable> is one of:</phrase>
TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ] [, ... ] TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
SEQUENCE <replaceable class="parameter">sequence_name</replaceable> [, ... ]
ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ] ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
ALL SEQUENCES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
@ -46,13 +44,13 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
</para> </para>
<para> <para>
The first three variants change which objects (tables, sequences or schemas) The first three variants change which tables/schemas are part of the
are part of the publication. The <literal>SET</literal> clause will replace publication. The <literal>SET</literal> clause will replace the list of
the list of objects in the publication with the specified list; the existing tables/schemas in the publication with the specified list; the existing
objects that were present in the publication will be removed. tables/schemas that were present in the publication will be removed. The
The <literal>ADD</literal> and <literal>DROP</literal> clauses will add and <literal>ADD</literal> and <literal>DROP</literal> clauses will add and
remove one or more objects from the publication. Note that adding objects remove one or more tables/schemas from the publication. Note that adding
to a publication that is already subscribed to will require an tables/schemas to a publication that is already subscribed to will require an
<literal>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</literal> action on the <literal>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</literal> action on the
subscribing side in order to become effective. Note also that the combination subscribing side in order to become effective. Note also that the combination
of <literal>DROP</literal> with a <literal>WHERE</literal> clause is not of <literal>DROP</literal> with a <literal>WHERE</literal> clause is not
@ -132,15 +130,6 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><replaceable class="parameter">sequence_name</replaceable></term>
<listitem>
<para>
Name of an existing sequence.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><replaceable class="parameter">schema_name</replaceable></term> <term><replaceable class="parameter">schema_name</replaceable></term>
<listitem> <listitem>

View File

@ -150,8 +150,8 @@ ALTER SUBSCRIPTION <replaceable class="parameter">name</replaceable> RENAME TO <
<listitem> <listitem>
<para> <para>
Fetch missing table information from publisher. This will start Fetch missing table information from publisher. This will start
replication of tables and sequences that were added to the subscribed-to replication of tables that were added to the subscribed-to publications
publications since <command>CREATE SUBSCRIPTION</command> or since <command>CREATE SUBSCRIPTION</command> or
the last invocation of <command>REFRESH PUBLICATION</command>. the last invocation of <command>REFRESH PUBLICATION</command>.
</para> </para>
@ -169,8 +169,8 @@ ALTER SUBSCRIPTION <replaceable class="parameter">name</replaceable> RENAME TO <
The default is <literal>true</literal>. The default is <literal>true</literal>.
</para> </para>
<para> <para>
Previously subscribed tables and sequences are not copied, even if a Previously subscribed tables are not copied, even if a table's row
table's row filter <literal>WHERE</literal> clause has since been modified. filter <literal>WHERE</literal> clause has since been modified.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -22,21 +22,14 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
CREATE PUBLICATION <replaceable class="parameter">name</replaceable> CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
[ FOR ALL <replaceable class="parameter">object_type</replaceable> [, ...] [ FOR ALL TABLES
| FOR <replaceable class="parameter">publication_object</replaceable> [, ... ] ] | FOR <replaceable class="parameter">publication_object</replaceable> [, ... ] ]
[ WITH ( <replaceable class="parameter">publication_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ WITH ( <replaceable class="parameter">publication_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
<phrase>where <replaceable class="parameter">object type</replaceable> is one of:</phrase>
TABLES
SEQUENCES
<phrase>where <replaceable class="parameter">publication_object</replaceable> is one of:</phrase> <phrase>where <replaceable class="parameter">publication_object</replaceable> is one of:</phrase>
TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ] [, ... ] TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
SEQUENCE <replaceable class="parameter">sequence_name</replaceable> [ * ] [, ... ]
ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ] ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
ALL SEQUENCES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
@ -121,43 +114,27 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>FOR SEQUENCE</literal></term>
<listitem>
<para>
Specifies a list of sequences to add to the publication.
</para>
<para>
Specifying a sequence that is part of a schema specified by <literal>FOR
ALL SEQUENCES IN SCHEMA</literal> is not supported.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><literal>FOR ALL TABLES</literal></term> <term><literal>FOR ALL TABLES</literal></term>
<term><literal>FOR ALL SEQUENCES</literal></term>
<listitem> <listitem>
<para> <para>
Marks the publication as one that replicates changes for all tables/sequences in Marks the publication as one that replicates changes for all tables in
the database, including tables/sequences created in the future. the database, including tables created in the future.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><literal>FOR ALL TABLES IN SCHEMA</literal></term> <term><literal>FOR ALL TABLES IN SCHEMA</literal></term>
<term><literal>FOR ALL SEQUENCES IN SCHEMA</literal></term>
<listitem> <listitem>
<para> <para>
Marks the publication as one that replicates changes for all sequences/tables in Marks the publication as one that replicates changes for all tables in
the specified list of schemas, including sequences/tables created in the future. the specified list of schemas, including tables created in the future.
</para> </para>
<para> <para>
Specifying a schema along with a sequence/table which belongs to the specified Specifying a schema along with a table which belongs to the specified
schema using <literal>FOR SEQUENCE</literal>/<literal>FOR TABLE</literal> is not supported. schema using <literal>FOR TABLE</literal> is not supported.
</para> </para>
<para> <para>
@ -232,9 +209,10 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
<title>Notes</title> <title>Notes</title>
<para> <para>
If <literal>FOR TABLE</literal>, <literal>FOR SEQUENCE</literal>, etc. is If <literal>FOR TABLE</literal>, <literal>FOR ALL TABLES</literal> or
not specified, then the publication starts out with an empty set of tables <literal>FOR ALL TABLES IN SCHEMA</literal> are not specified, then the
and sequences. That is useful if objects are to be added later. publication starts out with an empty set of tables. That is useful if
tables or schemas are to be added later.
</para> </para>
<para> <para>
@ -249,9 +227,10 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
</para> </para>
<para> <para>
To add a table or a sequence to a publication, the invoking user must To add a table to a publication, the invoking user must have ownership
have ownership rights on the object. The <command>FOR ALL ...</command> rights on the table. The <command>FOR ALL TABLES</command> and
clauses require the invoking user to be a superuser. <command>FOR ALL TABLES IN SCHEMA</command> clauses require the invoking
user to be a superuser.
</para> </para>
<para> <para>

View File

@ -1941,14 +1941,12 @@ get_object_address_publication_schema(List *object, bool missing_ok)
char *pubname; char *pubname;
char *schemaname; char *schemaname;
Oid schemaid; Oid schemaid;
char *objtype;
ObjectAddressSet(address, PublicationNamespaceRelationId, InvalidOid); ObjectAddressSet(address, PublicationNamespaceRelationId, InvalidOid);
/* Fetch schema name and publication name from input list */ /* Fetch schema name and publication name from input list */
schemaname = strVal(linitial(object)); schemaname = strVal(linitial(object));
pubname = strVal(lsecond(object)); pubname = strVal(lsecond(object));
objtype = strVal(lthird(object));
schemaid = get_namespace_oid(schemaname, missing_ok); schemaid = get_namespace_oid(schemaname, missing_ok);
if (!OidIsValid(schemaid)) if (!OidIsValid(schemaid))
@ -1961,12 +1959,10 @@ get_object_address_publication_schema(List *object, bool missing_ok)
/* Find the publication schema mapping in syscache */ /* Find the publication schema mapping in syscache */
address.objectId = address.objectId =
GetSysCacheOid3(PUBLICATIONNAMESPACEMAP, GetSysCacheOid2(PUBLICATIONNAMESPACEMAP,
Anum_pg_publication_namespace_oid, Anum_pg_publication_namespace_oid,
ObjectIdGetDatum(schemaid), ObjectIdGetDatum(schemaid),
ObjectIdGetDatum(pub->oid), ObjectIdGetDatum(pub->oid));
CharGetDatum(objtype[0]));
if (!OidIsValid(address.objectId) && !missing_ok) if (!OidIsValid(address.objectId) && !missing_ok)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),
@ -2247,6 +2243,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_DOMCONSTRAINT: case OBJECT_DOMCONSTRAINT:
case OBJECT_CAST: case OBJECT_CAST:
case OBJECT_USER_MAPPING: case OBJECT_USER_MAPPING:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL: case OBJECT_PUBLICATION_REL:
case OBJECT_DEFACL: case OBJECT_DEFACL:
case OBJECT_TRANSFORM: case OBJECT_TRANSFORM:
@ -2271,7 +2268,6 @@ pg_get_object_address(PG_FUNCTION_ARGS)
/* fall through to check args length */ /* fall through to check args length */
/* FALLTHROUGH */ /* FALLTHROUGH */
case OBJECT_OPERATOR: case OBJECT_OPERATOR:
case OBJECT_PUBLICATION_NAMESPACE:
if (list_length(args) != 2) if (list_length(args) != 2)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@ -2343,8 +2339,6 @@ pg_get_object_address(PG_FUNCTION_ARGS)
objnode = (Node *) list_make2(name, linitial(args)); objnode = (Node *) list_make2(name, linitial(args));
break; break;
case OBJECT_PUBLICATION_NAMESPACE: case OBJECT_PUBLICATION_NAMESPACE:
objnode = (Node *) list_make3(linitial(name), linitial(args), lsecond(args));
break;
case OBJECT_USER_MAPPING: case OBJECT_USER_MAPPING:
objnode = (Node *) list_make2(linitial(name), linitial(args)); objnode = (Node *) list_make2(linitial(name), linitial(args));
break; break;
@ -2900,12 +2894,11 @@ get_catalog_object_by_oid(Relation catalog, AttrNumber oidcol, Oid objectId)
* *
* Get publication name and schema name from the object address into pubname and * Get publication name and schema name from the object address into pubname and
* nspname. Both pubname and nspname are palloc'd strings which will be freed by * nspname. Both pubname and nspname are palloc'd strings which will be freed by
* the caller. The last parameter specifies which object type is included from * the caller.
* the schema.
*/ */
static bool static bool
getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok, getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok,
char **pubname, char **nspname, char **objtype) char **pubname, char **nspname)
{ {
HeapTuple tup; HeapTuple tup;
Form_pg_publication_namespace pnform; Form_pg_publication_namespace pnform;
@ -2941,13 +2934,6 @@ getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok,
return false; return false;
} }
/*
* The type is always a single character, but we need to pass it as a string,
* so allocate two charaters and set the first one. The second one is \0.
*/
*objtype = palloc0(2);
*objtype[0] = pnform->pntype;
ReleaseSysCache(tup); ReleaseSysCache(tup);
return true; return true;
} }
@ -3979,17 +3965,15 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
{ {
char *pubname; char *pubname;
char *nspname; char *nspname;
char *objtype;
if (!getPublicationSchemaInfo(object, missing_ok, if (!getPublicationSchemaInfo(object, missing_ok,
&pubname, &nspname, &objtype)) &pubname, &nspname))
break; break;
appendStringInfo(&buffer, _("publication of schema %s in publication %s type %s"), appendStringInfo(&buffer, _("publication of schema %s in publication %s"),
nspname, pubname, objtype); nspname, pubname);
pfree(pubname); pfree(pubname);
pfree(nspname); pfree(nspname);
pfree(objtype);
break; break;
} }
@ -5816,24 +5800,18 @@ getObjectIdentityParts(const ObjectAddress *object,
{ {
char *pubname; char *pubname;
char *nspname; char *nspname;
char *objtype;
if (!getPublicationSchemaInfo(object, missing_ok, &pubname, if (!getPublicationSchemaInfo(object, missing_ok, &pubname,
&nspname, &objtype)) &nspname))
break; break;
appendStringInfo(&buffer, "%s in publication %s type %s", appendStringInfo(&buffer, "%s in publication %s",
nspname, pubname, objtype); nspname, pubname);
if (objargs) if (objargs)
*objargs = list_make1(pubname); *objargs = list_make1(pubname);
else else
pfree(pubname); pfree(pubname);
if (objargs)
*objargs = lappend(*objargs, objtype);
else
pfree(objtype);
if (objname) if (objname)
*objname = list_make1(nspname); *objname = list_make1(nspname);
else else

View File

@ -55,10 +55,9 @@ static void publication_translate_columns(Relation targetrel, List *columns,
static void static void
check_publication_add_relation(Relation targetrel) check_publication_add_relation(Relation targetrel)
{ {
/* Must be a regular or partitioned table, or a sequence */ /* Must be a regular or partitioned table */
if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION && if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION &&
RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE && RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE)
RelationGetForm(targetrel)->relkind != RELKIND_SEQUENCE)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot add relation \"%s\" to publication", errmsg("cannot add relation \"%s\" to publication",
@ -135,8 +134,7 @@ static bool
is_publishable_class(Oid relid, Form_pg_class reltuple) is_publishable_class(Oid relid, Form_pg_class reltuple)
{ {
return (reltuple->relkind == RELKIND_RELATION || return (reltuple->relkind == RELKIND_RELATION ||
reltuple->relkind == RELKIND_PARTITIONED_TABLE || reltuple->relkind == RELKIND_PARTITIONED_TABLE) &&
reltuple->relkind == RELKIND_SEQUENCE) &&
!IsCatalogRelationOid(relid) && !IsCatalogRelationOid(relid) &&
reltuple->relpersistence == RELPERSISTENCE_PERMANENT && reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
relid >= FirstNormalObjectId; relid >= FirstNormalObjectId;
@ -181,52 +179,6 @@ filter_partitions(List *relids)
return result; return result;
} }
/*
* Check the character is a valid object type for schema publication.
*
* This recognizes either 't' for tables or 's' for sequences. Places that
* need to handle 'u' for unsupported relkinds need to do that explicitlyl
*/
static void
AssertObjectTypeValid(char objectType)
{
#ifdef USE_ASSERT_CHECKING
Assert(objectType == PUB_OBJTYPE_SEQUENCE || objectType == PUB_OBJTYPE_TABLE);
#endif
}
/*
* Determine object type matching a given a relkind value.
*/
char
pub_get_object_type_for_relkind(char relkind)
{
/* sequence maps directly to sequence relkind */
if (relkind == RELKIND_SEQUENCE)
return PUB_OBJTYPE_SEQUENCE;
/* for table, we match either regular or partitioned table */
if (relkind == RELKIND_RELATION ||
relkind == RELKIND_PARTITIONED_TABLE)
return PUB_OBJTYPE_TABLE;
return PUB_OBJTYPE_UNSUPPORTED;
}
/*
* Determine if publication object type matches the relkind.
*
* Returns true if the relation matches object type replicated by this schema,
* false otherwise.
*/
static bool
pub_object_type_matches_relkind(char objectType, char relkind)
{
AssertObjectTypeValid(objectType);
return (pub_get_object_type_for_relkind(relkind) == objectType);
}
/* /*
* Another variant of this, taking a Relation. * Another variant of this, taking a Relation.
*/ */
@ -256,7 +208,7 @@ is_schema_publication(Oid pubid)
ObjectIdGetDatum(pubid)); ObjectIdGetDatum(pubid));
scan = systable_beginscan(pubschsrel, scan = systable_beginscan(pubschsrel,
PublicationNamespacePnnspidPnpubidPntypeIndexId, PublicationNamespacePnnspidPnpubidIndexId,
true, NULL, 1, &scankey); true, NULL, 1, &scankey);
tup = systable_getnext(scan); tup = systable_getnext(scan);
result = HeapTupleIsValid(tup); result = HeapTupleIsValid(tup);
@ -364,9 +316,7 @@ GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level
} }
else else
{ {
/* we only search for ancestors of tables, so PUB_OBJTYPE_TABLE */ aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor),
PUB_OBJTYPE_TABLE);
if (list_member_oid(aschemaPubids, puboid)) if (list_member_oid(aschemaPubids, puboid))
{ {
topmost_relid = ancestor; topmost_relid = ancestor;
@ -635,7 +585,7 @@ pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols, MemoryContext mcxt)
* Insert new publication / schema mapping. * Insert new publication / schema mapping.
*/ */
ObjectAddress ObjectAddress
publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exists) publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
{ {
Relation rel; Relation rel;
HeapTuple tup; HeapTuple tup;
@ -647,8 +597,6 @@ publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exi
ObjectAddress myself, ObjectAddress myself,
referenced; referenced;
AssertObjectTypeValid(objectType);
rel = table_open(PublicationNamespaceRelationId, RowExclusiveLock); rel = table_open(PublicationNamespaceRelationId, RowExclusiveLock);
/* /*
@ -656,10 +604,9 @@ publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exi
* duplicates, it's here just to provide nicer error message in common * duplicates, it's here just to provide nicer error message in common
* case. The real protection is the unique key on the catalog. * case. The real protection is the unique key on the catalog.
*/ */
if (SearchSysCacheExists3(PUBLICATIONNAMESPACEMAP, if (SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP,
ObjectIdGetDatum(schemaid), ObjectIdGetDatum(schemaid),
ObjectIdGetDatum(pubid), ObjectIdGetDatum(pubid)))
CharGetDatum(objectType)))
{ {
table_close(rel, RowExclusiveLock); table_close(rel, RowExclusiveLock);
@ -685,8 +632,6 @@ publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exi
ObjectIdGetDatum(pubid); ObjectIdGetDatum(pubid);
values[Anum_pg_publication_namespace_pnnspid - 1] = values[Anum_pg_publication_namespace_pnnspid - 1] =
ObjectIdGetDatum(schemaid); ObjectIdGetDatum(schemaid);
values[Anum_pg_publication_namespace_pntype - 1] =
CharGetDatum(objectType);
tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
@ -712,7 +657,7 @@ publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exi
* publication_add_relation for why we need to consider all the * publication_add_relation for why we need to consider all the
* partitions. * partitions.
*/ */
schemaRels = GetSchemaPublicationRelations(schemaid, objectType, schemaRels = GetSchemaPublicationRelations(schemaid,
PUBLICATION_PART_ALL); PUBLICATION_PART_ALL);
InvalidatePublicationRels(schemaRels); InvalidatePublicationRels(schemaRels);
@ -746,14 +691,11 @@ GetRelationPublications(Oid relid)
/* /*
* Gets list of relation oids for a publication. * Gets list of relation oids for a publication.
* *
* This should only be used FOR TABLE / FOR SEQUENCE publications, the FOR * This should only be used FOR TABLE publications, the FOR ALL TABLES
* ALL TABLES / SEQUENCES should use GetAllTablesPublicationRelations() * should use GetAllTablesPublicationRelations().
* and GetAllSequencesPublicationRelations().
*
* XXX pub_partopt only matters for tables, not sequences.
*/ */
List * List *
GetPublicationRelations(Oid pubid, char objectType, PublicationPartOpt pub_partopt) GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
{ {
List *result; List *result;
Relation pubrelsrel; Relation pubrelsrel;
@ -761,8 +703,6 @@ GetPublicationRelations(Oid pubid, char objectType, PublicationPartOpt pub_parto
SysScanDesc scan; SysScanDesc scan;
HeapTuple tup; HeapTuple tup;
AssertObjectTypeValid(objectType);
/* Find all publications associated with the relation. */ /* Find all publications associated with the relation. */
pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock); pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
@ -777,29 +717,11 @@ GetPublicationRelations(Oid pubid, char objectType, PublicationPartOpt pub_parto
result = NIL; result = NIL;
while (HeapTupleIsValid(tup = systable_getnext(scan))) while (HeapTupleIsValid(tup = systable_getnext(scan)))
{ {
char relkind;
Form_pg_publication_rel pubrel; Form_pg_publication_rel pubrel;
pubrel = (Form_pg_publication_rel) GETSTRUCT(tup); pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
relkind = get_rel_relkind(pubrel->prrelid); result = GetPubPartitionOptionRelations(result, pub_partopt,
pubrel->prrelid);
/*
* If the relkind does not match the requested object type, ignore the
* relation. For example we might be interested only in sequences, so
* we ignore tables.
*/
if (!pub_object_type_matches_relkind(objectType, relkind))
continue;
/*
* We don't have partitioned sequences, so just add them to the list.
* Otherwise consider adding all child relations, if requested.
*/
if (relkind == RELKIND_SEQUENCE)
result = lappend_oid(result, pubrel->prrelid);
else
result = GetPubPartitionOptionRelations(result, pub_partopt,
pubrel->prrelid);
} }
systable_endscan(scan); systable_endscan(scan);
@ -849,43 +771,6 @@ GetAllTablesPublications(void)
return result; return result;
} }
/*
* Gets list of publication oids for publications marked as FOR ALL SEQUENCES.
*/
List *
GetAllSequencesPublications(void)
{
List *result;
Relation rel;
ScanKeyData scankey;
SysScanDesc scan;
HeapTuple tup;
/* Find all publications that are marked as for all sequences. */
rel = table_open(PublicationRelationId, AccessShareLock);
ScanKeyInit(&scankey,
Anum_pg_publication_puballsequences,
BTEqualStrategyNumber, F_BOOLEQ,
BoolGetDatum(true));
scan = systable_beginscan(rel, InvalidOid, false,
NULL, 1, &scankey);
result = NIL;
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Oid oid = ((Form_pg_publication) GETSTRUCT(tup))->oid;
result = lappend_oid(result, oid);
}
systable_endscan(scan);
table_close(rel, AccessShareLock);
return result;
}
/* /*
* Gets list of all relation published by FOR ALL TABLES publication(s). * Gets list of all relation published by FOR ALL TABLES publication(s).
* *
@ -952,38 +837,28 @@ GetAllTablesPublicationRelations(bool pubviaroot)
/* /*
* Gets the list of schema oids for a publication. * Gets the list of schema oids for a publication.
* *
* This should only be used FOR ALL TABLES IN SCHEMA and FOR ALL SEQUENCES * This should only be used FOR ALL TABLES IN SCHEMA publications.
* publications.
*
* 'objectType' determines whether to get FOR TABLE or FOR SEQUENCES schemas
*/ */
List * List *
GetPublicationSchemas(Oid pubid, char objectType) GetPublicationSchemas(Oid pubid)
{ {
List *result = NIL; List *result = NIL;
Relation pubschsrel; Relation pubschsrel;
ScanKeyData scankey[2]; ScanKeyData scankey;
SysScanDesc scan; SysScanDesc scan;
HeapTuple tup; HeapTuple tup;
AssertObjectTypeValid(objectType);
/* Find all schemas associated with the publication */ /* Find all schemas associated with the publication */
pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock); pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
ScanKeyInit(&scankey[0], ScanKeyInit(&scankey,
Anum_pg_publication_namespace_pnpubid, Anum_pg_publication_namespace_pnpubid,
BTEqualStrategyNumber, F_OIDEQ, BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(pubid)); ObjectIdGetDatum(pubid));
ScanKeyInit(&scankey[1],
Anum_pg_publication_namespace_pntype,
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(objectType));
scan = systable_beginscan(pubschsrel, scan = systable_beginscan(pubschsrel,
PublicationNamespacePnnspidPnpubidPntypeIndexId, PublicationNamespacePnnspidPnpubidIndexId,
true, NULL, 2, scankey); true, NULL, 1, &scankey);
while (HeapTupleIsValid(tup = systable_getnext(scan))) while (HeapTupleIsValid(tup = systable_getnext(scan)))
{ {
Form_pg_publication_namespace pubsch; Form_pg_publication_namespace pubsch;
@ -1001,26 +876,14 @@ GetPublicationSchemas(Oid pubid, char objectType)
/* /*
* Gets the list of publication oids associated with a specified schema. * Gets the list of publication oids associated with a specified schema.
*
* objectType specifies whether we're looking for schemas including tables or
* sequences.
*
* Note: relcache calls this for all object types, not just tables and sequences.
* Which is why we handle the PUB_OBJTYPE_UNSUPPORTED object type too.
*/ */
List * List *
GetSchemaPublications(Oid schemaid, char objectType) GetSchemaPublications(Oid schemaid)
{ {
List *result = NIL; List *result = NIL;
CatCList *pubschlist; CatCList *pubschlist;
int i; int i;
/* unsupported object type */
if (objectType == PUB_OBJTYPE_UNSUPPORTED)
return result;
AssertObjectTypeValid(objectType);
/* Find all publications associated with the schema */ /* Find all publications associated with the schema */
pubschlist = SearchSysCacheList1(PUBLICATIONNAMESPACEMAP, pubschlist = SearchSysCacheList1(PUBLICATIONNAMESPACEMAP,
ObjectIdGetDatum(schemaid)); ObjectIdGetDatum(schemaid));
@ -1028,11 +891,6 @@ GetSchemaPublications(Oid schemaid, char objectType)
{ {
HeapTuple tup = &pubschlist->members[i]->tuple; HeapTuple tup = &pubschlist->members[i]->tuple;
Oid pubid = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pnpubid; Oid pubid = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pnpubid;
char pntype = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pntype;
/* Skip schemas publishing a different object type. */
if (pntype != objectType)
continue;
result = lappend_oid(result, pubid); result = lappend_oid(result, pubid);
} }
@ -1044,13 +902,9 @@ GetSchemaPublications(Oid schemaid, char objectType)
/* /*
* Get the list of publishable relation oids for a specified schema. * Get the list of publishable relation oids for a specified schema.
*
* objectType specifies whether this is FOR ALL TABLES IN SCHEMA or FOR ALL
* SEQUENCES IN SCHEMA
*/ */
List * List *
GetSchemaPublicationRelations(Oid schemaid, char objectType, GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
PublicationPartOpt pub_partopt)
{ {
Relation classRel; Relation classRel;
ScanKeyData key[1]; ScanKeyData key[1];
@ -1059,7 +913,6 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
List *result = NIL; List *result = NIL;
Assert(OidIsValid(schemaid)); Assert(OidIsValid(schemaid));
AssertObjectTypeValid(objectType);
classRel = table_open(RelationRelationId, AccessShareLock); classRel = table_open(RelationRelationId, AccessShareLock);
@ -1080,16 +933,9 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
continue; continue;
relkind = get_rel_relkind(relid); relkind = get_rel_relkind(relid);
if (relkind == RELKIND_RELATION)
/* Skip if the relkind does not match FOR ALL TABLES / SEQUENCES. */ result = lappend_oid(result, relid);
if (!pub_object_type_matches_relkind(objectType, relkind)) else if (relkind == RELKIND_PARTITIONED_TABLE)
continue;
/*
* If the object is a partitioned table, lookup all the child relations
* (if requested). Otherwise just add the object to the list.
*/
if (relkind == RELKIND_PARTITIONED_TABLE)
{ {
List *partitionrels = NIL; List *partitionrels = NIL;
@ -1102,11 +948,7 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
pub_partopt, pub_partopt,
relForm->oid); relForm->oid);
result = list_concat_unique_oid(result, partitionrels); result = list_concat_unique_oid(result, partitionrels);
continue;
} }
/* non-partitioned tables and sequences */
result = lappend_oid(result, relid);
} }
table_endscan(scan); table_endscan(scan);
@ -1116,67 +958,27 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
/* /*
* Gets the list of all relations published by FOR ALL TABLES IN SCHEMA * Gets the list of all relations published by FOR ALL TABLES IN SCHEMA
* or FOR ALL SEQUENCES IN SCHEMA publication. * publication.
*/ */
List * List *
GetAllSchemaPublicationRelations(Oid pubid, char objectType, GetAllSchemaPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
PublicationPartOpt pub_partopt)
{ {
List *result = NIL; List *result = NIL;
List *pubschemalist = GetPublicationSchemas(pubid, objectType); List *pubschemalist = GetPublicationSchemas(pubid);
ListCell *cell; ListCell *cell;
AssertObjectTypeValid(objectType);
foreach(cell, pubschemalist) foreach(cell, pubschemalist)
{ {
Oid schemaid = lfirst_oid(cell); Oid schemaid = lfirst_oid(cell);
List *schemaRels = NIL; List *schemaRels = NIL;
schemaRels = GetSchemaPublicationRelations(schemaid, objectType, schemaRels = GetSchemaPublicationRelations(schemaid, pub_partopt);
pub_partopt);
result = list_concat(result, schemaRels); result = list_concat(result, schemaRels);
} }
return result; return result;
} }
/*
* Gets list of all relation published by FOR ALL SEQUENCES publication(s).
*/
List *
GetAllSequencesPublicationRelations(void)
{
Relation classRel;
ScanKeyData key[1];
TableScanDesc scan;
HeapTuple tuple;
List *result = NIL;
classRel = table_open(RelationRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_class_relkind,
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_SEQUENCE));
scan = table_beginscan_catalog(classRel, 1, key);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = relForm->oid;
if (is_publishable_class(relid, relForm))
result = lappend_oid(result, relid);
}
table_endscan(scan);
table_close(classRel, AccessShareLock);
return result;
}
/* /*
* Get publication using oid * Get publication using oid
* *
@ -1199,12 +1001,10 @@ GetPublication(Oid pubid)
pub->oid = pubid; pub->oid = pubid;
pub->name = pstrdup(NameStr(pubform->pubname)); pub->name = pstrdup(NameStr(pubform->pubname));
pub->alltables = pubform->puballtables; pub->alltables = pubform->puballtables;
pub->allsequences = pubform->puballsequences;
pub->pubactions.pubinsert = pubform->pubinsert; pub->pubactions.pubinsert = pubform->pubinsert;
pub->pubactions.pubupdate = pubform->pubupdate; pub->pubactions.pubupdate = pubform->pubupdate;
pub->pubactions.pubdelete = pubform->pubdelete; pub->pubactions.pubdelete = pubform->pubdelete;
pub->pubactions.pubtruncate = pubform->pubtruncate; pub->pubactions.pubtruncate = pubform->pubtruncate;
pub->pubactions.pubsequence = pubform->pubsequence;
pub->pubviaroot = pubform->pubviaroot; pub->pubviaroot = pubform->pubviaroot;
ReleaseSysCache(tup); ReleaseSysCache(tup);
@ -1315,12 +1115,10 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
*schemarelids; *schemarelids;
relids = GetPublicationRelations(publication->oid, relids = GetPublicationRelations(publication->oid,
PUB_OBJTYPE_TABLE,
publication->pubviaroot ? publication->pubviaroot ?
PUBLICATION_PART_ROOT : PUBLICATION_PART_ROOT :
PUBLICATION_PART_LEAF); PUBLICATION_PART_LEAF);
schemarelids = GetAllSchemaPublicationRelations(publication->oid, schemarelids = GetAllSchemaPublicationRelations(publication->oid,
PUB_OBJTYPE_TABLE,
publication->pubviaroot ? publication->pubviaroot ?
PUBLICATION_PART_ROOT : PUBLICATION_PART_ROOT :
PUBLICATION_PART_LEAF); PUBLICATION_PART_LEAF);
@ -1356,71 +1154,3 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
SRF_RETURN_DONE(funcctx); SRF_RETURN_DONE(funcctx);
} }
/*
* Returns Oids of sequences in a publication.
*/
Datum
pg_get_publication_sequences(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
char *pubname = text_to_cstring(PG_GETARG_TEXT_PP(0));
Publication *publication;
List *sequences;
/* stuff done only on the first call of the function */
if (SRF_IS_FIRSTCALL())
{
MemoryContext oldcontext;
/* create a function context for cross-call persistence */
funcctx = SRF_FIRSTCALL_INIT();
/* switch to memory context appropriate for multiple function calls */
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
publication = GetPublicationByName(pubname, false);
/*
* Publications support partitioned tables, although all changes are
* replicated using leaf partition identity and schema, so we only
* need those.
*/
if (publication->allsequences)
sequences = GetAllSequencesPublicationRelations();
else
{
List *relids,
*schemarelids;
relids = GetPublicationRelations(publication->oid,
PUB_OBJTYPE_SEQUENCE,
publication->pubviaroot ?
PUBLICATION_PART_ROOT :
PUBLICATION_PART_LEAF);
schemarelids = GetAllSchemaPublicationRelations(publication->oid,
PUB_OBJTYPE_SEQUENCE,
publication->pubviaroot ?
PUBLICATION_PART_ROOT :
PUBLICATION_PART_LEAF);
sequences = list_concat_unique_oid(relids, schemarelids);
}
funcctx->user_fctx = (void *) sequences;
MemoryContextSwitchTo(oldcontext);
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
sequences = (List *) funcctx->user_fctx;
if (funcctx->call_cntr < list_length(sequences))
{
Oid relid = list_nth_oid(sequences, funcctx->call_cntr);
SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid));
}
SRF_RETURN_DONE(funcctx);
}

View File

@ -374,16 +374,6 @@ CREATE VIEW pg_publication_tables AS
pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace) pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE C.oid = GPT.relid; WHERE C.oid = GPT.relid;
CREATE VIEW pg_publication_sequences AS
SELECT
P.pubname AS pubname,
N.nspname AS schemaname,
C.relname AS sequencename
FROM pg_publication P,
LATERAL pg_get_publication_sequences(P.pubname) GPS,
pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE C.oid = GPS.relid;
CREATE VIEW pg_locks AS CREATE VIEW pg_locks AS
SELECT * FROM pg_lock_status() AS L; SELECT * FROM pg_lock_status() AS L;

View File

@ -16,7 +16,6 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/htup_details.h" #include "access/htup_details.h"
#include "access/relation.h"
#include "access/table.h" #include "access/table.h"
#include "access/xact.h" #include "access/xact.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
@ -68,17 +67,15 @@ typedef struct rf_context
} rf_context; } rf_context;
static List *OpenRelIdList(List *relids); static List *OpenRelIdList(List *relids);
static List *OpenRelationList(List *rels, char objectType); static List *OpenTableList(List *tables);
static void CloseRelationList(List *rels); static void CloseTableList(List *rels);
static void LockSchemaList(List *schemalist); static void LockSchemaList(List *schemalist);
static void PublicationAddRelations(Oid pubid, List *rels, bool if_not_exists, static void PublicationAddTables(Oid pubid, List *rels, bool if_not_exists,
AlterPublicationStmt *stmt); AlterPublicationStmt *stmt);
static void PublicationDropRelations(Oid pubid, List *rels, bool missing_ok); static void PublicationDropTables(Oid pubid, List *rels, bool missing_ok);
static void PublicationAddSchemas(Oid pubid, List *schemas, char objectType, static void PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists,
bool if_not_exists, AlterPublicationStmt *stmt); AlterPublicationStmt *stmt);
static void PublicationDropSchemas(Oid pubid, List *schemas, char objectType, static void PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok);
bool missing_ok);
static void static void
parse_publication_options(ParseState *pstate, parse_publication_options(ParseState *pstate,
@ -98,7 +95,6 @@ parse_publication_options(ParseState *pstate,
pubactions->pubupdate = true; pubactions->pubupdate = true;
pubactions->pubdelete = true; pubactions->pubdelete = true;
pubactions->pubtruncate = true; pubactions->pubtruncate = true;
pubactions->pubsequence = true;
*publish_via_partition_root = false; *publish_via_partition_root = false;
/* Parse options */ /* Parse options */
@ -123,7 +119,6 @@ parse_publication_options(ParseState *pstate,
pubactions->pubupdate = false; pubactions->pubupdate = false;
pubactions->pubdelete = false; pubactions->pubdelete = false;
pubactions->pubtruncate = false; pubactions->pubtruncate = false;
pubactions->pubsequence = false;
*publish_given = true; *publish_given = true;
publish = defGetString(defel); publish = defGetString(defel);
@ -146,8 +141,6 @@ parse_publication_options(ParseState *pstate,
pubactions->pubdelete = true; pubactions->pubdelete = true;
else if (strcmp(publish_opt, "truncate") == 0) else if (strcmp(publish_opt, "truncate") == 0)
pubactions->pubtruncate = true; pubactions->pubtruncate = true;
else if (strcmp(publish_opt, "sequence") == 0)
pubactions->pubsequence = true;
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
@ -174,8 +167,7 @@ parse_publication_options(ParseState *pstate,
*/ */
static void static void
ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate, ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
List **tables, List **sequences, List **rels, List **schemas)
List **tables_schemas, List **sequences_schemas)
{ {
ListCell *cell; ListCell *cell;
PublicationObjSpec *pubobj; PublicationObjSpec *pubobj;
@ -193,22 +185,13 @@ ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
switch (pubobj->pubobjtype) switch (pubobj->pubobjtype)
{ {
case PUBLICATIONOBJ_TABLE: case PUBLICATIONOBJ_TABLE:
*tables = lappend(*tables, pubobj->pubtable); *rels = lappend(*rels, pubobj->pubtable);
break;
case PUBLICATIONOBJ_SEQUENCE:
*sequences = lappend(*sequences, pubobj->pubtable);
break; break;
case PUBLICATIONOBJ_TABLES_IN_SCHEMA: case PUBLICATIONOBJ_TABLES_IN_SCHEMA:
schemaid = get_namespace_oid(pubobj->name, false); schemaid = get_namespace_oid(pubobj->name, false);
/* Filter out duplicates if user specifies "sch1, sch1" */ /* Filter out duplicates if user specifies "sch1, sch1" */
*tables_schemas = list_append_unique_oid(*tables_schemas, schemaid); *schemas = list_append_unique_oid(*schemas, schemaid);
break;
case PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA:
schemaid = get_namespace_oid(pubobj->name, false);
/* Filter out duplicates if user specifies "sch1, sch1" */
*sequences_schemas = list_append_unique_oid(*sequences_schemas, schemaid);
break; break;
case PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA: case PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA:
search_path = fetch_search_path(false); search_path = fetch_search_path(false);
@ -221,20 +204,7 @@ ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
list_free(search_path); list_free(search_path);
/* Filter out duplicates if user specifies "sch1, sch1" */ /* Filter out duplicates if user specifies "sch1, sch1" */
*tables_schemas = list_append_unique_oid(*tables_schemas, schemaid); *schemas = list_append_unique_oid(*schemas, schemaid);
break;
case PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA:
search_path = fetch_search_path(false);
if (search_path == NIL) /* nothing valid in search_path? */
ereport(ERROR,
errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("no schema has been selected for CURRENT_SCHEMA"));
schemaid = linitial_oid(search_path);
list_free(search_path);
/* Filter out duplicates if user specifies "sch1, sch1" */
*sequences_schemas = list_append_unique_oid(*sequences_schemas, schemaid);
break; break;
default: default:
/* shouldn't happen */ /* shouldn't happen */
@ -270,14 +240,6 @@ CheckObjSchemaNotAlreadyInPublication(List *rels, List *schemaidlist,
errdetail("Table \"%s\" in schema \"%s\" is already part of the publication, adding the same schema is not supported.", errdetail("Table \"%s\" in schema \"%s\" is already part of the publication, adding the same schema is not supported.",
RelationGetRelationName(rel), RelationGetRelationName(rel),
get_namespace_name(relSchemaId))); get_namespace_name(relSchemaId)));
else if (checkobjtype == PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA)
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot add schema \"%s\" to publication",
get_namespace_name(relSchemaId)),
errdetail("Sequence \"%s\" in schema \"%s\" is already part of the publication, adding the same schema is not supported.",
RelationGetRelationName(rel),
get_namespace_name(relSchemaId)));
else if (checkobjtype == PUBLICATIONOBJ_TABLE) else if (checkobjtype == PUBLICATIONOBJ_TABLE)
ereport(ERROR, ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE), errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@ -286,14 +248,6 @@ CheckObjSchemaNotAlreadyInPublication(List *rels, List *schemaidlist,
RelationGetRelationName(rel)), RelationGetRelationName(rel)),
errdetail("Table's schema \"%s\" is already part of the publication or part of the specified schema list.", errdetail("Table's schema \"%s\" is already part of the publication or part of the specified schema list.",
get_namespace_name(relSchemaId))); get_namespace_name(relSchemaId)));
else if (checkobjtype == PUBLICATIONOBJ_SEQUENCE)
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot add relation \"%s.%s\" to publication",
get_namespace_name(relSchemaId),
RelationGetRelationName(rel)),
errdetail("Sequence's schema \"%s\" is already part of the publication or part of the specified schema list.",
get_namespace_name(relSchemaId)));
} }
} }
} }
@ -799,7 +753,6 @@ CheckPubRelationColumnList(List *tables, const char *queryString,
ObjectAddress ObjectAddress
CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt) CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
{ {
ListCell *lc;
Relation rel; Relation rel;
ObjectAddress myself; ObjectAddress myself;
Oid puboid; Oid puboid;
@ -811,23 +764,8 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
bool publish_via_partition_root_given; bool publish_via_partition_root_given;
bool publish_via_partition_root; bool publish_via_partition_root;
AclResult aclresult; AclResult aclresult;
List *tables = NIL; List *relations = NIL;
List *sequences = NIL; List *schemaidlist = NIL;
List *tables_schemaidlist = NIL;
List *sequences_schemaidlist = NIL;
bool for_all_tables = false;
bool for_all_sequences = false;
/* Translate the list of object types (represented by strings) to bool flags. */
foreach (lc, stmt->for_all_objects)
{
char *val = strVal(lfirst(lc));
if (strcmp(val, "tables") == 0)
for_all_tables = true;
else if (strcmp(val, "sequences") == 0)
for_all_sequences = true;
}
/* must have CREATE privilege on database */ /* must have CREATE privilege on database */
aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE); aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
@ -836,17 +774,11 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
get_database_name(MyDatabaseId)); get_database_name(MyDatabaseId));
/* FOR ALL TABLES requires superuser */ /* FOR ALL TABLES requires superuser */
if (for_all_tables && !superuser()) if (stmt->for_all_tables && !superuser())
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create FOR ALL TABLES publication"))); errmsg("must be superuser to create FOR ALL TABLES publication")));
/* FOR ALL SEQUENCES requires superuser */
if (for_all_sequences && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create FOR ALL SEQUENCES publication")));
rel = table_open(PublicationRelationId, RowExclusiveLock); rel = table_open(PublicationRelationId, RowExclusiveLock);
/* Check if name is used */ /* Check if name is used */
@ -878,9 +810,7 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
Anum_pg_publication_oid); Anum_pg_publication_oid);
values[Anum_pg_publication_oid - 1] = ObjectIdGetDatum(puboid); values[Anum_pg_publication_oid - 1] = ObjectIdGetDatum(puboid);
values[Anum_pg_publication_puballtables - 1] = values[Anum_pg_publication_puballtables - 1] =
BoolGetDatum(for_all_tables); BoolGetDatum(stmt->for_all_tables);
values[Anum_pg_publication_puballsequences - 1] =
BoolGetDatum(for_all_sequences);
values[Anum_pg_publication_pubinsert - 1] = values[Anum_pg_publication_pubinsert - 1] =
BoolGetDatum(pubactions.pubinsert); BoolGetDatum(pubactions.pubinsert);
values[Anum_pg_publication_pubupdate - 1] = values[Anum_pg_publication_pubupdate - 1] =
@ -889,8 +819,6 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
BoolGetDatum(pubactions.pubdelete); BoolGetDatum(pubactions.pubdelete);
values[Anum_pg_publication_pubtruncate - 1] = values[Anum_pg_publication_pubtruncate - 1] =
BoolGetDatum(pubactions.pubtruncate); BoolGetDatum(pubactions.pubtruncate);
values[Anum_pg_publication_pubsequence - 1] =
BoolGetDatum(pubactions.pubsequence);
values[Anum_pg_publication_pubviaroot - 1] = values[Anum_pg_publication_pubviaroot - 1] =
BoolGetDatum(publish_via_partition_root); BoolGetDatum(publish_via_partition_root);
@ -908,42 +836,28 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
CommandCounterIncrement(); CommandCounterIncrement();
/* Associate objects with the publication. */ /* Associate objects with the publication. */
if (for_all_tables || for_all_sequences) if (stmt->for_all_tables)
{ {
/* Invalidate relcache so that publication info is rebuilt. */ /* Invalidate relcache so that publication info is rebuilt. */
CacheInvalidateRelcacheAll(); CacheInvalidateRelcacheAll();
} }
else
/*
* If the publication might have either tables or sequences (directly or
* through a schema), process that.
*/
if (!for_all_tables || !for_all_sequences)
{ {
ObjectsInPublicationToOids(stmt->pubobjects, pstate, ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
&tables, &sequences, &schemaidlist);
&tables_schemaidlist,
&sequences_schemaidlist);
/* FOR ALL TABLES IN SCHEMA requires superuser */ /* FOR ALL TABLES IN SCHEMA requires superuser */
if (list_length(tables_schemaidlist) > 0 && !superuser()) if (list_length(schemaidlist) > 0 && !superuser())
ereport(ERROR, ereport(ERROR,
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication")); errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication"));
/* FOR ALL SEQUENCES IN SCHEMA requires superuser */ if (list_length(relations) > 0)
if (list_length(sequences_schemaidlist) > 0 && !superuser())
ereport(ERROR,
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create FOR ALL SEQUENCES IN SCHEMA publication"));
/* tables added directly */
if (list_length(tables) > 0)
{ {
List *rels; List *rels;
rels = OpenRelationList(tables, PUB_OBJTYPE_TABLE); rels = OpenTableList(relations);
CheckObjSchemaNotAlreadyInPublication(rels, tables_schemaidlist, CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
PUBLICATIONOBJ_TABLE); PUBLICATIONOBJ_TABLE);
TransformPubWhereClauses(rels, pstate->p_sourcetext, TransformPubWhereClauses(rels, pstate->p_sourcetext,
@ -952,46 +866,18 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
CheckPubRelationColumnList(rels, pstate->p_sourcetext, CheckPubRelationColumnList(rels, pstate->p_sourcetext,
publish_via_partition_root); publish_via_partition_root);
PublicationAddRelations(puboid, rels, true, NULL); PublicationAddTables(puboid, rels, true, NULL);
CloseRelationList(rels); CloseTableList(rels);
} }
/* sequences added directly */ if (list_length(schemaidlist) > 0)
if (list_length(sequences) > 0)
{
List *rels;
rels = OpenRelationList(sequences, PUB_OBJTYPE_SEQUENCE);
CheckObjSchemaNotAlreadyInPublication(rels, sequences_schemaidlist,
PUBLICATIONOBJ_SEQUENCE);
PublicationAddRelations(puboid, rels, true, NULL);
CloseRelationList(rels);
}
/* tables added through a schema */
if (list_length(tables_schemaidlist) > 0)
{ {
/* /*
* Schema lock is held until the publication is created to prevent * Schema lock is held until the publication is created to prevent
* concurrent schema deletion. * concurrent schema deletion.
*/ */
LockSchemaList(tables_schemaidlist); LockSchemaList(schemaidlist);
PublicationAddSchemas(puboid, PublicationAddSchemas(puboid, schemaidlist, true, NULL);
tables_schemaidlist, PUB_OBJTYPE_TABLE,
true, NULL);
}
/* sequences added through a schema */
if (list_length(sequences_schemaidlist) > 0)
{
/*
* Schema lock is held until the publication is created to prevent
* concurrent schema deletion.
*/
LockSchemaList(sequences_schemaidlist);
PublicationAddSchemas(puboid,
sequences_schemaidlist, PUB_OBJTYPE_SEQUENCE,
true, NULL);
} }
} }
@ -1055,7 +941,6 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
AccessShareLock); AccessShareLock);
root_relids = GetPublicationRelations(pubform->oid, root_relids = GetPublicationRelations(pubform->oid,
PUB_OBJTYPE_TABLE,
PUBLICATION_PART_ROOT); PUBLICATION_PART_ROOT);
foreach(lc, root_relids) foreach(lc, root_relids)
@ -1135,9 +1020,6 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
values[Anum_pg_publication_pubtruncate - 1] = BoolGetDatum(pubactions.pubtruncate); values[Anum_pg_publication_pubtruncate - 1] = BoolGetDatum(pubactions.pubtruncate);
replaces[Anum_pg_publication_pubtruncate - 1] = true; replaces[Anum_pg_publication_pubtruncate - 1] = true;
values[Anum_pg_publication_pubsequence - 1] = BoolGetDatum(pubactions.pubsequence);
replaces[Anum_pg_publication_pubsequence - 1] = true;
} }
if (publish_via_partition_root_given) if (publish_via_partition_root_given)
@ -1157,7 +1039,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
pubform = (Form_pg_publication) GETSTRUCT(tup); pubform = (Form_pg_publication) GETSTRUCT(tup);
/* Invalidate the relcache. */ /* Invalidate the relcache. */
if (pubform->puballtables || pubform->puballsequences) if (pubform->puballtables)
{ {
CacheInvalidateRelcacheAll(); CacheInvalidateRelcacheAll();
} }
@ -1173,7 +1055,6 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
*/ */
if (root_relids == NIL) if (root_relids == NIL)
relids = GetPublicationRelations(pubform->oid, relids = GetPublicationRelations(pubform->oid,
PUB_OBJTYPE_TABLE,
PUBLICATION_PART_ALL); PUBLICATION_PART_ALL);
else else
{ {
@ -1187,20 +1068,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
lfirst_oid(lc)); lfirst_oid(lc));
} }
/* tables */
schemarelids = GetAllSchemaPublicationRelations(pubform->oid, schemarelids = GetAllSchemaPublicationRelations(pubform->oid,
PUB_OBJTYPE_TABLE,
PUBLICATION_PART_ALL);
relids = list_concat_unique_oid(relids, schemarelids);
/* sequences */
relids = list_concat_unique_oid(relids,
GetPublicationRelations(pubform->oid,
PUB_OBJTYPE_SEQUENCE,
PUBLICATION_PART_ALL));
schemarelids = GetAllSchemaPublicationRelations(pubform->oid,
PUB_OBJTYPE_SEQUENCE,
PUBLICATION_PART_ALL); PUBLICATION_PART_ALL);
relids = list_concat_unique_oid(relids, schemarelids); relids = list_concat_unique_oid(relids, schemarelids);
@ -1255,7 +1123,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
if (!tables && stmt->action != AP_SetObjects) if (!tables && stmt->action != AP_SetObjects)
return; return;
rels = OpenRelationList(tables, PUB_OBJTYPE_TABLE); rels = OpenTableList(tables);
if (stmt->action == AP_AddObjects) if (stmt->action == AP_AddObjects)
{ {
@ -1265,9 +1133,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
* Check if the relation is member of the existing schema in the * Check if the relation is member of the existing schema in the
* publication or member of the schema list specified. * publication or member of the schema list specified.
*/ */
schemas = list_concat_copy(schemaidlist, schemas = list_concat_copy(schemaidlist, GetPublicationSchemas(pubid));
GetPublicationSchemas(pubid,
PUB_OBJTYPE_TABLE));
CheckObjSchemaNotAlreadyInPublication(rels, schemas, CheckObjSchemaNotAlreadyInPublication(rels, schemas,
PUBLICATIONOBJ_TABLE); PUBLICATIONOBJ_TABLE);
@ -1275,14 +1141,13 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
CheckPubRelationColumnList(rels, queryString, pubform->pubviaroot); CheckPubRelationColumnList(rels, queryString, pubform->pubviaroot);
PublicationAddRelations(pubid, rels, false, stmt); PublicationAddTables(pubid, rels, false, stmt);
} }
else if (stmt->action == AP_DropObjects) else if (stmt->action == AP_DropObjects)
PublicationDropRelations(pubid, rels, false); PublicationDropTables(pubid, rels, false);
else /* AP_SetObjects */ else /* AP_SetObjects */
{ {
List *oldrelids = GetPublicationRelations(pubid, List *oldrelids = GetPublicationRelations(pubid,
PUB_OBJTYPE_TABLE,
PUBLICATION_PART_ROOT); PUBLICATION_PART_ROOT);
List *delrels = NIL; List *delrels = NIL;
ListCell *oldlc; ListCell *oldlc;
@ -1401,18 +1266,18 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
} }
/* And drop them. */ /* And drop them. */
PublicationDropRelations(pubid, delrels, true); PublicationDropTables(pubid, delrels, true);
/* /*
* Don't bother calculating the difference for adding, we'll catch and * Don't bother calculating the difference for adding, we'll catch and
* skip existing ones when doing catalog update. * skip existing ones when doing catalog update.
*/ */
PublicationAddRelations(pubid, rels, true, stmt); PublicationAddTables(pubid, rels, true, stmt);
CloseRelationList(delrels); CloseTableList(delrels);
} }
CloseRelationList(rels); CloseTableList(rels);
} }
/* /*
@ -1422,8 +1287,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
*/ */
static void static void
AlterPublicationSchemas(AlterPublicationStmt *stmt, AlterPublicationSchemas(AlterPublicationStmt *stmt,
HeapTuple tup, List *schemaidlist, HeapTuple tup, List *schemaidlist)
char objectType)
{ {
Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup); Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
@ -1445,20 +1309,20 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
List *rels; List *rels;
List *reloids; List *reloids;
reloids = GetPublicationRelations(pubform->oid, objectType, PUBLICATION_PART_ROOT); reloids = GetPublicationRelations(pubform->oid, PUBLICATION_PART_ROOT);
rels = OpenRelIdList(reloids); rels = OpenRelIdList(reloids);
CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist, CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
PUBLICATIONOBJ_TABLES_IN_SCHEMA); PUBLICATIONOBJ_TABLES_IN_SCHEMA);
CloseRelationList(rels); CloseTableList(rels);
PublicationAddSchemas(pubform->oid, schemaidlist, objectType, false, stmt); PublicationAddSchemas(pubform->oid, schemaidlist, false, stmt);
} }
else if (stmt->action == AP_DropObjects) else if (stmt->action == AP_DropObjects)
PublicationDropSchemas(pubform->oid, schemaidlist, objectType, false); PublicationDropSchemas(pubform->oid, schemaidlist, false);
else /* AP_SetObjects */ else /* AP_SetObjects */
{ {
List *oldschemaids = GetPublicationSchemas(pubform->oid, objectType); List *oldschemaids = GetPublicationSchemas(pubform->oid);
List *delschemas = NIL; List *delschemas = NIL;
/* Identify which schemas should be dropped */ /* Identify which schemas should be dropped */
@ -1471,13 +1335,13 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
LockSchemaList(delschemas); LockSchemaList(delschemas);
/* And drop them */ /* And drop them */
PublicationDropSchemas(pubform->oid, delschemas, objectType, true); PublicationDropSchemas(pubform->oid, delschemas, true);
/* /*
* Don't bother calculating the difference for adding, we'll catch and * Don't bother calculating the difference for adding, we'll catch and
* skip existing ones when doing catalog update. * skip existing ones when doing catalog update.
*/ */
PublicationAddSchemas(pubform->oid, schemaidlist, objectType, true, stmt); PublicationAddSchemas(pubform->oid, schemaidlist, true, stmt);
} }
} }
@ -1487,13 +1351,12 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
*/ */
static void static void
CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup, CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
List *tables, List *tables_schemaidlist, List *tables, List *schemaidlist)
List *sequences, List *sequences_schemaidlist)
{ {
Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup); Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
if ((stmt->action == AP_AddObjects || stmt->action == AP_SetObjects) && if ((stmt->action == AP_AddObjects || stmt->action == AP_SetObjects) &&
(tables_schemaidlist || sequences_schemaidlist) && !superuser()) schemaidlist && !superuser())
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to add or set schemas"))); errmsg("must be superuser to add or set schemas")));
@ -1502,24 +1365,13 @@ CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
* Check that user is allowed to manipulate the publication tables in * Check that user is allowed to manipulate the publication tables in
* schema * schema
*/ */
if (tables_schemaidlist && pubform->puballtables) if (schemaidlist && pubform->puballtables)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("publication \"%s\" is defined as FOR ALL TABLES", errmsg("publication \"%s\" is defined as FOR ALL TABLES",
NameStr(pubform->pubname)), NameStr(pubform->pubname)),
errdetail("Tables from schema cannot be added to, dropped from, or set on FOR ALL TABLES publications."))); errdetail("Tables from schema cannot be added to, dropped from, or set on FOR ALL TABLES publications.")));
/*
* Check that user is allowed to manipulate the publication sequences in
* schema
*/
if (sequences_schemaidlist && pubform->puballsequences)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("publication \"%s\" is defined as FOR ALL SEQUENCES",
NameStr(pubform->pubname)),
errdetail("Sequences from schema cannot be added to, dropped from, or set on FOR ALL SEQUENCES publications.")));
/* Check that user is allowed to manipulate the publication tables. */ /* Check that user is allowed to manipulate the publication tables. */
if (tables && pubform->puballtables) if (tables && pubform->puballtables)
ereport(ERROR, ereport(ERROR,
@ -1527,108 +1379,6 @@ CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
errmsg("publication \"%s\" is defined as FOR ALL TABLES", errmsg("publication \"%s\" is defined as FOR ALL TABLES",
NameStr(pubform->pubname)), NameStr(pubform->pubname)),
errdetail("Tables cannot be added to or dropped from FOR ALL TABLES publications."))); errdetail("Tables cannot be added to or dropped from FOR ALL TABLES publications.")));
/* Check that user is allowed to manipulate the publication sequences. */
if (sequences && pubform->puballsequences)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("publication \"%s\" is defined as FOR ALL SEQUENCES",
NameStr(pubform->pubname)),
errdetail("Sequences cannot be added to or dropped from FOR ALL SEQUENCES publications.")));
}
/*
* Add or remove sequence to/from publication.
*/
static void
AlterPublicationSequences(AlterPublicationStmt *stmt, HeapTuple tup,
List *sequences, List *schemaidlist)
{
List *rels = NIL;
Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
Oid pubid = pubform->oid;
/*
* It is quite possible that for the SET case user has not specified any
* tables in which case we need to remove all the existing tables.
*/
if (!sequences && stmt->action != AP_SetObjects)
return;
rels = OpenRelationList(sequences, PUB_OBJTYPE_SEQUENCE);
if (stmt->action == AP_AddObjects)
{
List *schemas = NIL;
/*
* Check if the relation is member of the existing schema in the
* publication or member of the schema list specified.
*/
schemas = list_concat_copy(schemaidlist,
GetPublicationSchemas(pubid,
PUB_OBJTYPE_SEQUENCE));
CheckObjSchemaNotAlreadyInPublication(rels, schemas,
PUBLICATIONOBJ_SEQUENCE);
PublicationAddRelations(pubid, rels, false, stmt);
}
else if (stmt->action == AP_DropObjects)
PublicationDropRelations(pubid, rels, false);
else /* DEFELEM_SET */
{
List *oldrelids = GetPublicationRelations(pubid,
PUB_OBJTYPE_SEQUENCE,
PUBLICATION_PART_ROOT);
List *delrels = NIL;
ListCell *oldlc;
CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
PUBLICATIONOBJ_SEQUENCE);
/* Calculate which relations to drop. */
foreach(oldlc, oldrelids)
{
Oid oldrelid = lfirst_oid(oldlc);
ListCell *newlc;
PublicationRelInfo *oldrel;
bool found = false;
foreach(newlc, rels)
{
PublicationRelInfo *newpubrel;
newpubrel = (PublicationRelInfo *) lfirst(newlc);
if (RelationGetRelid(newpubrel->relation) == oldrelid)
{
found = true;
break;
}
}
/* Not yet in the list, open it and add to the list */
if (!found)
{
oldrel = palloc(sizeof(PublicationRelInfo));
oldrel->whereClause = NULL;
oldrel->columns = NULL;
oldrel->relation = table_open(oldrelid,
ShareUpdateExclusiveLock);
delrels = lappend(delrels, oldrel);
}
}
/* And drop them. */
PublicationDropRelations(pubid, delrels, true);
/*
* Don't bother calculating the difference for adding, we'll catch and
* skip existing ones when doing catalog update.
*/
PublicationAddRelations(pubid, rels, true, stmt);
CloseRelationList(delrels);
}
CloseRelationList(rels);
} }
/* /*
@ -1666,20 +1416,14 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
AlterPublicationOptions(pstate, stmt, rel, tup); AlterPublicationOptions(pstate, stmt, rel, tup);
else else
{ {
List *tables = NIL; List *relations = NIL;
List *sequences = NIL; List *schemaidlist = NIL;
List *tables_schemaidlist = NIL;
List *sequences_schemaidlist = NIL;
Oid pubid = pubform->oid; Oid pubid = pubform->oid;
ObjectsInPublicationToOids(stmt->pubobjects, pstate, ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
&tables, &sequences, &schemaidlist);
&tables_schemaidlist,
&sequences_schemaidlist);
CheckAlterPublication(stmt, tup, CheckAlterPublication(stmt, tup, relations, schemaidlist);
tables, tables_schemaidlist,
sequences, sequences_schemaidlist);
heap_freetuple(tup); heap_freetuple(tup);
@ -1707,16 +1451,9 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
errmsg("publication \"%s\" does not exist", errmsg("publication \"%s\" does not exist",
stmt->pubname)); stmt->pubname));
AlterPublicationTables(stmt, tup, tables, tables_schemaidlist, AlterPublicationTables(stmt, tup, relations, schemaidlist,
pstate->p_sourcetext); pstate->p_sourcetext);
AlterPublicationSchemas(stmt, tup, schemaidlist);
AlterPublicationSequences(stmt, tup, sequences, sequences_schemaidlist);
AlterPublicationSchemas(stmt, tup, tables_schemaidlist,
PUB_OBJTYPE_TABLE);
AlterPublicationSchemas(stmt, tup, sequences_schemaidlist,
PUB_OBJTYPE_SEQUENCE);
} }
/* Cleanup. */ /* Cleanup. */
@ -1784,7 +1521,7 @@ RemovePublicationById(Oid pubid)
pubform = (Form_pg_publication) GETSTRUCT(tup); pubform = (Form_pg_publication) GETSTRUCT(tup);
/* Invalidate relcache so that publication info is rebuilt. */ /* Invalidate relcache so that publication info is rebuilt. */
if (pubform->puballtables || pubform->puballsequences) if (pubform->puballtables)
CacheInvalidateRelcacheAll(); CacheInvalidateRelcacheAll();
CatalogTupleDelete(rel, &tup->t_self); CatalogTupleDelete(rel, &tup->t_self);
@ -1820,7 +1557,6 @@ RemovePublicationSchemaById(Oid psoid)
* partitions. * partitions.
*/ */
schemaRels = GetSchemaPublicationRelations(pubsch->pnnspid, schemaRels = GetSchemaPublicationRelations(pubsch->pnnspid,
pubsch->pntype,
PUBLICATION_PART_ALL); PUBLICATION_PART_ALL);
InvalidatePublicationRels(schemaRels); InvalidatePublicationRels(schemaRels);
@ -1863,10 +1599,10 @@ OpenRelIdList(List *relids)
* add them to a publication. * add them to a publication.
*/ */
static List * static List *
OpenRelationList(List *rels, char objectType) OpenTableList(List *tables)
{ {
List *relids = NIL; List *relids = NIL;
List *result = NIL; List *rels = NIL;
ListCell *lc; ListCell *lc;
List *relids_with_rf = NIL; List *relids_with_rf = NIL;
List *relids_with_collist = NIL; List *relids_with_collist = NIL;
@ -1874,35 +1610,19 @@ OpenRelationList(List *rels, char objectType)
/* /*
* Open, share-lock, and check all the explicitly-specified relations * Open, share-lock, and check all the explicitly-specified relations
*/ */
foreach(lc, rels) foreach(lc, tables)
{ {
PublicationTable *t = lfirst_node(PublicationTable, lc); PublicationTable *t = lfirst_node(PublicationTable, lc);
bool recurse = t->relation->inh; bool recurse = t->relation->inh;
Relation rel; Relation rel;
Oid myrelid; Oid myrelid;
PublicationRelInfo *pub_rel; PublicationRelInfo *pub_rel;
char myrelkind;
/* Allow query cancel in case this takes a long time */ /* Allow query cancel in case this takes a long time */
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();
rel = table_openrv(t->relation, ShareUpdateExclusiveLock); rel = table_openrv(t->relation, ShareUpdateExclusiveLock);
myrelid = RelationGetRelid(rel); myrelid = RelationGetRelid(rel);
myrelkind = get_rel_relkind(myrelid);
/*
* Make sure the relkind matches the expected object type. This may
* happen e.g. when adding a sequence using ADD TABLE or a table
* using ADD SEQUENCE).
*
* XXX We let through unsupported object types (views etc.). Those
* will be caught later in check_publication_add_relation.
*/
if (pub_get_object_type_for_relkind(myrelkind) != PUB_OBJTYPE_UNSUPPORTED &&
pub_get_object_type_for_relkind(myrelkind) != objectType)
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("object type does not match type expected by command"));
/* /*
* Filter out duplicates if user specifies "foo, foo". * Filter out duplicates if user specifies "foo, foo".
@ -1935,7 +1655,7 @@ OpenRelationList(List *rels, char objectType)
pub_rel->relation = rel; pub_rel->relation = rel;
pub_rel->whereClause = t->whereClause; pub_rel->whereClause = t->whereClause;
pub_rel->columns = t->columns; pub_rel->columns = t->columns;
result = lappend(result, pub_rel); rels = lappend(rels, pub_rel);
relids = lappend_oid(relids, myrelid); relids = lappend_oid(relids, myrelid);
if (t->whereClause) if (t->whereClause)
@ -2004,9 +1724,10 @@ OpenRelationList(List *rels, char objectType)
pub_rel->relation = rel; pub_rel->relation = rel;
/* child inherits WHERE clause from parent */ /* child inherits WHERE clause from parent */
pub_rel->whereClause = t->whereClause; pub_rel->whereClause = t->whereClause;
/* child inherits column list from parent */ /* child inherits column list from parent */
pub_rel->columns = t->columns; pub_rel->columns = t->columns;
result = lappend(result, pub_rel); rels = lappend(rels, pub_rel);
relids = lappend_oid(relids, childrelid); relids = lappend_oid(relids, childrelid);
if (t->whereClause) if (t->whereClause)
@ -2021,14 +1742,14 @@ OpenRelationList(List *rels, char objectType)
list_free(relids); list_free(relids);
list_free(relids_with_rf); list_free(relids_with_rf);
return result; return rels;
} }
/* /*
* Close all relations in the list. * Close all relations in the list.
*/ */
static void static void
CloseRelationList(List *rels) CloseTableList(List *rels)
{ {
ListCell *lc; ListCell *lc;
@ -2076,12 +1797,12 @@ LockSchemaList(List *schemalist)
* Add listed tables to the publication. * Add listed tables to the publication.
*/ */
static void static void
PublicationAddRelations(Oid pubid, List *rels, bool if_not_exists, PublicationAddTables(Oid pubid, List *rels, bool if_not_exists,
AlterPublicationStmt *stmt) AlterPublicationStmt *stmt)
{ {
ListCell *lc; ListCell *lc;
Assert(!stmt || !stmt->for_all_objects); Assert(!stmt || !stmt->for_all_tables);
foreach(lc, rels) foreach(lc, rels)
{ {
@ -2110,7 +1831,7 @@ PublicationAddRelations(Oid pubid, List *rels, bool if_not_exists,
* Remove listed tables from the publication. * Remove listed tables from the publication.
*/ */
static void static void
PublicationDropRelations(Oid pubid, List *rels, bool missing_ok) PublicationDropTables(Oid pubid, List *rels, bool missing_ok)
{ {
ObjectAddress obj; ObjectAddress obj;
ListCell *lc; ListCell *lc;
@ -2155,19 +1876,19 @@ PublicationDropRelations(Oid pubid, List *rels, bool missing_ok)
* Add listed schemas to the publication. * Add listed schemas to the publication.
*/ */
static void static void
PublicationAddSchemas(Oid pubid, List *schemas, char objectType, PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists,
bool if_not_exists, AlterPublicationStmt *stmt) AlterPublicationStmt *stmt)
{ {
ListCell *lc; ListCell *lc;
Assert(!stmt || !stmt->for_all_objects); Assert(!stmt || !stmt->for_all_tables);
foreach(lc, schemas) foreach(lc, schemas)
{ {
Oid schemaid = lfirst_oid(lc); Oid schemaid = lfirst_oid(lc);
ObjectAddress obj; ObjectAddress obj;
obj = publication_add_schema(pubid, schemaid, objectType, if_not_exists); obj = publication_add_schema(pubid, schemaid, if_not_exists);
if (stmt) if (stmt)
{ {
EventTriggerCollectSimpleCommand(obj, InvalidObjectAddress, EventTriggerCollectSimpleCommand(obj, InvalidObjectAddress,
@ -2183,7 +1904,7 @@ PublicationAddSchemas(Oid pubid, List *schemas, char objectType,
* Remove listed schemas from the publication. * Remove listed schemas from the publication.
*/ */
static void static void
PublicationDropSchemas(Oid pubid, List *schemas, char objectType, bool missing_ok) PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok)
{ {
ObjectAddress obj; ObjectAddress obj;
ListCell *lc; ListCell *lc;
@ -2193,11 +1914,10 @@ PublicationDropSchemas(Oid pubid, List *schemas, char objectType, bool missing_o
{ {
Oid schemaid = lfirst_oid(lc); Oid schemaid = lfirst_oid(lc);
psid = GetSysCacheOid3(PUBLICATIONNAMESPACEMAP, psid = GetSysCacheOid2(PUBLICATIONNAMESPACEMAP,
Anum_pg_publication_namespace_oid, Anum_pg_publication_namespace_oid,
ObjectIdGetDatum(schemaid), ObjectIdGetDatum(schemaid),
ObjectIdGetDatum(pubid), ObjectIdGetDatum(pubid));
CharGetDatum(objectType));
if (!OidIsValid(psid)) if (!OidIsValid(psid))
{ {
if (missing_ok) if (missing_ok)
@ -2252,13 +1972,6 @@ AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
NameStr(form->pubname)), NameStr(form->pubname)),
errhint("The owner of a FOR ALL TABLES publication must be a superuser."))); errhint("The owner of a FOR ALL TABLES publication must be a superuser.")));
if (form->puballsequences && !superuser_arg(newOwnerId))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to change owner of publication \"%s\"",
NameStr(form->pubname)),
errhint("The owner of a FOR ALL SEQUENCES publication must be a superuser.")));
if (!superuser_arg(newOwnerId) && is_schema_publication(form->oid)) if (!superuser_arg(newOwnerId) && is_schema_publication(form->oid))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),

View File

@ -332,160 +332,6 @@ ResetSequence(Oid seq_relid)
relation_close(seq_rel, NoLock); relation_close(seq_rel, NoLock);
} }
/*
* Update the sequence state by modifying the existing sequence data row.
*
* This keeps the same relfilenode, so the behavior is non-transactional.
*/
static void
SetSequence_non_transactional(Oid seqrelid, int64 last_value, int64 log_cnt, bool is_called)
{
SeqTable elm;
Relation seqrel;
Buffer buf;
HeapTupleData seqdatatuple;
Form_pg_sequence_data seq;
/* open and lock sequence */
init_sequence(seqrelid, &elm, &seqrel);
/* lock page' buffer and read tuple */
seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
/* check the comment above nextval_internal()'s equivalent call. */
if (RelationNeedsWAL(seqrel))
{
GetTopTransactionId();
if (XLogLogicalInfoActive())
GetCurrentTransactionId();
}
/* ready to change the on-disk (or really, in-buffer) tuple */
START_CRIT_SECTION();
seq->last_value = last_value;
seq->is_called = is_called;
seq->log_cnt = log_cnt;
MarkBufferDirty(buf);
/* XLOG stuff */
if (RelationNeedsWAL(seqrel))
{
xl_seq_rec xlrec;
XLogRecPtr recptr;
Page page = BufferGetPage(buf);
XLogBeginInsert();
XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
xlrec.node = seqrel->rd_node;
xlrec.created = false;
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
PageSetLSN(page, recptr);
}
END_CRIT_SECTION();
UnlockReleaseBuffer(buf);
/* Clear local cache so that we don't think we have cached numbers */
/* Note that we do not change the currval() state */
elm->cached = elm->last;
relation_close(seqrel, NoLock);
}
/*
* Update the sequence state by creating a new relfilenode.
*
* This creates a new relfilenode, to allow transactional behavior.
*/
static void
SetSequence_transactional(Oid seq_relid, int64 last_value, int64 log_cnt, bool is_called)
{
SeqTable elm;
Relation seqrel;
Buffer buf;
HeapTupleData seqdatatuple;
Form_pg_sequence_data seq;
HeapTuple tuple;
/* open and lock sequence */
init_sequence(seq_relid, &elm, &seqrel);
/* lock page' buffer and read tuple */
seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
/* Copy the existing sequence tuple. */
tuple = heap_copytuple(&seqdatatuple);
/* Now we're done with the old page */
UnlockReleaseBuffer(buf);
/*
* Modify the copied tuple to update the sequence state (similar to what
* ResetSequence does).
*/
seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
seq->last_value = last_value;
seq->is_called = is_called;
seq->log_cnt = log_cnt;
/*
* Create a new storage file for the sequence - this is needed for the
* transactional behavior.
*/
RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence);
/*
* Ensure sequence's relfrozenxid is at 0, since it won't contain any
* unfrozen XIDs. Same with relminmxid, since a sequence will never
* contain multixacts.
*/
Assert(seqrel->rd_rel->relfrozenxid == InvalidTransactionId);
Assert(seqrel->rd_rel->relminmxid == InvalidMultiXactId);
/*
* Insert the modified tuple into the new storage file. This does all the
* necessary WAL-logging etc.
*/
fill_seq_with_data(seqrel, tuple);
/* Clear local cache so that we don't think we have cached numbers */
/* Note that we do not change the currval() state */
elm->cached = elm->last;
relation_close(seqrel, NoLock);
}
/*
* Set a sequence to a specified internal state.
*
* The change is made transactionally, so that on failure of the current
* transaction, the sequence will be restored to its previous state.
* We do that by creating a whole new relfilenode for the sequence; so this
* works much like the rewriting forms of ALTER TABLE.
*
* Caller is assumed to have acquired AccessExclusiveLock on the sequence,
* which must not be released until end of transaction. Caller is also
* responsible for permissions checking.
*/
void
SetSequence(Oid seq_relid, bool transactional, int64 last_value, int64 log_cnt, bool is_called)
{
if (transactional)
SetSequence_transactional(seq_relid, last_value, log_cnt, is_called);
else
SetSequence_non_transactional(seq_relid, last_value, log_cnt, is_called);
}
/* /*
* Initialize a sequence's relation with the specified tuple as content * Initialize a sequence's relation with the specified tuple as content
* *
@ -552,13 +398,8 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
/* check the comment above nextval_internal()'s equivalent call. */ /* check the comment above nextval_internal()'s equivalent call. */
if (RelationNeedsWAL(rel)) if (RelationNeedsWAL(rel))
{
GetTopTransactionId(); GetTopTransactionId();
if (XLogLogicalInfoActive())
GetCurrentTransactionId();
}
START_CRIT_SECTION(); START_CRIT_SECTION();
MarkBufferDirty(buf); MarkBufferDirty(buf);
@ -578,7 +419,6 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT); XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
xlrec.node = rel->rd_node; xlrec.node = rel->rd_node;
xlrec.created = true;
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec)); XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
XLogRegisterData((char *) tuple->t_data, tuple->t_len); XLogRegisterData((char *) tuple->t_data, tuple->t_len);
@ -958,28 +798,10 @@ nextval_internal(Oid relid, bool check_permissions)
* It's sufficient to ensure the toplevel transaction has an xid, no need * It's sufficient to ensure the toplevel transaction has an xid, no need
* to assign xids subxacts, that'll already trigger an appropriate wait. * to assign xids subxacts, that'll already trigger an appropriate wait.
* (Have to do that here, so we're outside the critical section) * (Have to do that here, so we're outside the critical section)
*
* We have to ensure we have a proper XID, which will be included in
* the XLOG record by XLogRecordAssemble. Otherwise the first nextval()
* in a subxact (without any preceding changes) would get XID 0, and it
* would then be impossible to decide which top xact it belongs to.
* It'd also trigger assert in DecodeSequence. We only do that with
* wal_level=logical, though.
*
* XXX This might seem unnecessary, because if there's no XID the xact
* couldn't have done anything important yet, e.g. it could not have
* created a sequence. But that's incorrect, because of subxacts. The
* current subtransaction might not have done anything yet (thus no XID),
* but an earlier one might have created the sequence.
*/ */
if (logit && RelationNeedsWAL(seqrel)) if (logit && RelationNeedsWAL(seqrel))
{
GetTopTransactionId(); GetTopTransactionId();
if (XLogLogicalInfoActive())
GetCurrentTransactionId();
}
/* ready to change the on-disk (or really, in-buffer) tuple */ /* ready to change the on-disk (or really, in-buffer) tuple */
START_CRIT_SECTION(); START_CRIT_SECTION();
@ -1015,7 +837,6 @@ nextval_internal(Oid relid, bool check_permissions)
seq->log_cnt = 0; seq->log_cnt = 0;
xlrec.node = seqrel->rd_node; xlrec.node = seqrel->rd_node;
xlrec.created = false;
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec)); XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len); XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
@ -1181,13 +1002,8 @@ do_setval(Oid relid, int64 next, bool iscalled)
/* check the comment above nextval_internal()'s equivalent call. */ /* check the comment above nextval_internal()'s equivalent call. */
if (RelationNeedsWAL(seqrel)) if (RelationNeedsWAL(seqrel))
{
GetTopTransactionId(); GetTopTransactionId();
if (XLogLogicalInfoActive())
GetCurrentTransactionId();
}
/* ready to change the on-disk (or really, in-buffer) tuple */ /* ready to change the on-disk (or really, in-buffer) tuple */
START_CRIT_SECTION(); START_CRIT_SECTION();
@ -1208,8 +1024,6 @@ do_setval(Oid relid, int64 next, bool iscalled)
XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT); XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
xlrec.node = seqrel->rd_node; xlrec.node = seqrel->rd_node;
xlrec.created = false;
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec)); XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len); XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);

View File

@ -90,7 +90,6 @@ typedef struct SubOpts
} SubOpts; } SubOpts;
static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); static List *fetch_table_list(WalReceiverConn *wrconn, List *publications);
static List *fetch_sequence_list(WalReceiverConn *wrconn, List *publications);
static void check_duplicates_in_publist(List *publist, Datum *datums); static void check_duplicates_in_publist(List *publist, Datum *datums);
static List *merge_publications(List *oldpublist, List *newpublist, bool addpub, const char *subname); static List *merge_publications(List *oldpublist, List *newpublist, bool addpub, const char *subname);
static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err);
@ -639,9 +638,9 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
{ {
char *err; char *err;
WalReceiverConn *wrconn; WalReceiverConn *wrconn;
List *relations; List *tables;
ListCell *lc; ListCell *lc;
char sync_state; char table_state;
/* Try to connect to the publisher. */ /* Try to connect to the publisher. */
wrconn = walrcv_connect(conninfo, true, stmt->subname, &err); wrconn = walrcv_connect(conninfo, true, stmt->subname, &err);
@ -658,17 +657,14 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
* Set sync state based on if we were asked to do data copy or * Set sync state based on if we were asked to do data copy or
* not. * not.
*/ */
sync_state = opts.copy_data ? SUBREL_STATE_INIT : SUBREL_STATE_READY; table_state = opts.copy_data ? SUBREL_STATE_INIT : SUBREL_STATE_READY;
/* /*
* Get the table and sequence list from publisher and build * Get the table list from publisher and build local table status
* local relation sync status info. * info.
*/ */
relations = fetch_table_list(wrconn, publications); tables = fetch_table_list(wrconn, publications);
relations = list_concat(relations, foreach(lc, tables)
fetch_sequence_list(wrconn, publications));
foreach(lc, relations)
{ {
RangeVar *rv = (RangeVar *) lfirst(lc); RangeVar *rv = (RangeVar *) lfirst(lc);
Oid relid; Oid relid;
@ -679,7 +675,7 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
CheckSubscriptionRelkind(get_rel_relkind(relid), CheckSubscriptionRelkind(get_rel_relkind(relid),
rv->schemaname, rv->relname); rv->schemaname, rv->relname);
AddSubscriptionRelState(subid, relid, sync_state, AddSubscriptionRelState(subid, relid, table_state,
InvalidXLogRecPtr); InvalidXLogRecPtr);
} }
@ -705,12 +701,12 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
* *
* Note that if tables were specified but copy_data is false * Note that if tables were specified but copy_data is false
* then it is safe to enable two_phase up-front because those * then it is safe to enable two_phase up-front because those
* relations are already initially in READY state. When the * tables are already initially in READY state. When the
* subscription has no relations, we leave the twophase state * subscription has no tables, we leave the twophase state as
* as PENDING, to allow ALTER SUBSCRIPTION ... REFRESH * PENDING, to allow ALTER SUBSCRIPTION ... REFRESH
* PUBLICATION to work. * PUBLICATION to work.
*/ */
if (opts.twophase && !opts.copy_data && relations != NIL) if (opts.twophase && !opts.copy_data && tables != NIL)
twophase_enabled = true; twophase_enabled = true;
walrcv_create_slot(wrconn, opts.slot_name, false, twophase_enabled, walrcv_create_slot(wrconn, opts.slot_name, false, twophase_enabled,
@ -786,10 +782,8 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data,
if (validate_publications) if (validate_publications)
check_publications(wrconn, validate_publications); check_publications(wrconn, validate_publications);
/* Get the list of relations from publisher. */ /* Get the table list from publisher. */
pubrel_names = fetch_table_list(wrconn, sub->publications); pubrel_names = fetch_table_list(wrconn, sub->publications);
pubrel_names = list_concat(pubrel_names,
fetch_sequence_list(wrconn, sub->publications));
/* Get local table list. */ /* Get local table list. */
subrel_states = GetSubscriptionRelations(sub->oid); subrel_states = GetSubscriptionRelations(sub->oid);
@ -1813,75 +1807,6 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications)
return tablelist; return tablelist;
} }
/*
* Get the list of sequences which belong to specified publications on the
* publisher connection.
*/
static List *
fetch_sequence_list(WalReceiverConn *wrconn, List *publications)
{
WalRcvExecResult *res;
StringInfoData cmd;
TupleTableSlot *slot;
Oid tableRow[2] = {TEXTOID, TEXTOID};
ListCell *lc;
bool first;
List *tablelist = NIL;
Assert(list_length(publications) > 0);
initStringInfo(&cmd);
appendStringInfoString(&cmd, "SELECT DISTINCT s.schemaname, s.sequencename\n"
" FROM pg_catalog.pg_publication_sequences s\n"
" WHERE s.pubname IN (");
first = true;
foreach(lc, publications)
{
char *pubname = strVal(lfirst(lc));
if (first)
first = false;
else
appendStringInfoString(&cmd, ", ");
appendStringInfoString(&cmd, quote_literal_cstr(pubname));
}
appendStringInfoChar(&cmd, ')');
res = walrcv_exec(wrconn, cmd.data, 2, tableRow);
pfree(cmd.data);
if (res->status != WALRCV_OK_TUPLES)
ereport(ERROR,
(errmsg("could not receive list of replicated sequences from the publisher: %s",
res->err)));
/* Process sequences. */
slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple);
while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
{
char *nspname;
char *relname;
bool isnull;
RangeVar *rv;
nspname = TextDatumGetCString(slot_getattr(slot, 1, &isnull));
Assert(!isnull);
relname = TextDatumGetCString(slot_getattr(slot, 2, &isnull));
Assert(!isnull);
rv = makeRangeVar(nspname, relname, -1);
tablelist = lappend(tablelist, rv);
ExecClearTuple(slot);
}
ExecDropSingleTupleTableSlot(slot);
walrcv_clear_result(res);
return tablelist;
}
/* /*
* This is to report the connection failure while dropping replication slots. * This is to report the connection failure while dropping replication slots.
* Here, we report the WARNING for all tablesync slots so that user can drop * Here, we report the WARNING for all tablesync slots so that user can drop

View File

@ -42,7 +42,6 @@
#include "catalog/pg_inherits.h" #include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h" #include "catalog/pg_opclass.h"
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_statistic_ext.h" #include "catalog/pg_statistic_ext.h"
#include "catalog/pg_tablespace.h" #include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h" #include "catalog/pg_trigger.h"
@ -16409,14 +16408,11 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
* Check that setting the relation to a different schema won't result in a * Check that setting the relation to a different schema won't result in a
* publication having both a schema and the same schema's table, as this * publication having both a schema and the same schema's table, as this
* is not supported. * is not supported.
*
* XXX We do this for tables and sequences, but it's better to keep the two
* blocks separate, to make the strings easier to translate.
*/ */
if (stmt->objectType == OBJECT_TABLE) if (stmt->objectType == OBJECT_TABLE)
{ {
ListCell *lc; ListCell *lc;
List *schemaPubids = GetSchemaPublications(nspOid, PUB_OBJTYPE_TABLE); List *schemaPubids = GetSchemaPublications(nspOid);
List *relPubids = GetRelationPublications(RelationGetRelid(rel)); List *relPubids = GetRelationPublications(RelationGetRelid(rel));
foreach(lc, relPubids) foreach(lc, relPubids)
@ -16434,27 +16430,6 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
get_publication_name(pubid, false))); get_publication_name(pubid, false)));
} }
} }
else if (stmt->objectType == OBJECT_SEQUENCE)
{
ListCell *lc;
List *schemaPubids = GetSchemaPublications(nspOid, PUB_OBJTYPE_SEQUENCE);
List *relPubids = GetRelationPublications(RelationGetRelid(rel));
foreach(lc, relPubids)
{
Oid pubid = lfirst_oid(lc);
if (list_member_oid(schemaPubids, pubid))
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move sequence \"%s\" to schema \"%s\"",
RelationGetRelationName(rel), stmt->newschema),
errdetail("The schema \"%s\" and same schema's sequence \"%s\" cannot be part of the same publication \"%s\".",
stmt->newschema,
RelationGetRelationName(rel),
get_publication_name(pubid, false)));
}
}
/* common checks on switching namespaces */ /* common checks on switching namespaces */
CheckSetNamespace(oldNspOid, nspOid); CheckSetNamespace(oldNspOid, nspOid);

View File

@ -649,9 +649,7 @@ void
CheckSubscriptionRelkind(char relkind, const char *nspname, CheckSubscriptionRelkind(char relkind, const char *nspname,
const char *relname) const char *relname)
{ {
if (relkind != RELKIND_RELATION && if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
relkind != RELKIND_PARTITIONED_TABLE &&
relkind != RELKIND_SEQUENCE)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot use relation \"%s.%s\" as logical replication target", errmsg("cannot use relation \"%s.%s\" as logical replication target",

View File

@ -5390,7 +5390,7 @@ _copyCreatePublicationStmt(const CreatePublicationStmt *from)
COPY_STRING_FIELD(pubname); COPY_STRING_FIELD(pubname);
COPY_NODE_FIELD(options); COPY_NODE_FIELD(options);
COPY_NODE_FIELD(pubobjects); COPY_NODE_FIELD(pubobjects);
COPY_NODE_FIELD(for_all_objects); COPY_SCALAR_FIELD(for_all_tables);
return newnode; return newnode;
} }
@ -5403,7 +5403,7 @@ _copyAlterPublicationStmt(const AlterPublicationStmt *from)
COPY_STRING_FIELD(pubname); COPY_STRING_FIELD(pubname);
COPY_NODE_FIELD(options); COPY_NODE_FIELD(options);
COPY_NODE_FIELD(pubobjects); COPY_NODE_FIELD(pubobjects);
COPY_NODE_FIELD(for_all_objects); COPY_SCALAR_FIELD(for_all_tables);
COPY_SCALAR_FIELD(action); COPY_SCALAR_FIELD(action);
return newnode; return newnode;

View File

@ -2688,7 +2688,7 @@ _equalCreatePublicationStmt(const CreatePublicationStmt *a,
COMPARE_STRING_FIELD(pubname); COMPARE_STRING_FIELD(pubname);
COMPARE_NODE_FIELD(options); COMPARE_NODE_FIELD(options);
COMPARE_NODE_FIELD(pubobjects); COMPARE_NODE_FIELD(pubobjects);
COMPARE_NODE_FIELD(for_all_objects); COMPARE_SCALAR_FIELD(for_all_tables);
return true; return true;
} }
@ -2700,7 +2700,7 @@ _equalAlterPublicationStmt(const AlterPublicationStmt *a,
COMPARE_STRING_FIELD(pubname); COMPARE_STRING_FIELD(pubname);
COMPARE_NODE_FIELD(options); COMPARE_NODE_FIELD(options);
COMPARE_NODE_FIELD(pubobjects); COMPARE_NODE_FIELD(pubobjects);
COMPARE_NODE_FIELD(for_all_objects); COMPARE_SCALAR_FIELD(for_all_tables);
COMPARE_SCALAR_FIELD(action); COMPARE_SCALAR_FIELD(action);
return true; return true;

View File

@ -455,7 +455,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
transform_element_list transform_type_list transform_element_list transform_type_list
TriggerTransitions TriggerReferencing TriggerTransitions TriggerReferencing
vacuum_relation_list opt_vacuum_relation_list vacuum_relation_list opt_vacuum_relation_list
drop_option_list pub_obj_list pub_obj_type_list drop_option_list pub_obj_list
%type <node> opt_routine_body %type <node> opt_routine_body
%type <groupclause> group_clause %type <groupclause> group_clause
@ -588,7 +588,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> var_value zone_value %type <node> var_value zone_value
%type <rolespec> auth_ident RoleSpec opt_granted_by %type <rolespec> auth_ident RoleSpec opt_granted_by
%type <publicationobjectspec> PublicationObjSpec %type <publicationobjectspec> PublicationObjSpec
%type <node> pub_obj_type
%type <keyword> unreserved_keyword type_func_name_keyword %type <keyword> unreserved_keyword type_func_name_keyword
%type <keyword> col_name_keyword reserved_keyword %type <keyword> col_name_keyword reserved_keyword
@ -9863,10 +9862,13 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
* *
* CREATE PUBLICATION FOR ALL TABLES [WITH options] * CREATE PUBLICATION FOR ALL TABLES [WITH options]
* *
* CREATE PUBLICATION FOR ALL SEQUENCES [WITH options]
*
* CREATE PUBLICATION FOR pub_obj [, ...] [WITH options] * CREATE PUBLICATION FOR pub_obj [, ...] [WITH options]
* *
* pub_obj is one of:
*
* TABLE table [, ...]
* ALL TABLES IN SCHEMA schema [, ...]
*
*****************************************************************************/ *****************************************************************************/
CreatePublicationStmt: CreatePublicationStmt:
@ -9877,12 +9879,12 @@ CreatePublicationStmt:
n->options = $4; n->options = $4;
$$ = (Node *)n; $$ = (Node *)n;
} }
| CREATE PUBLICATION name FOR ALL pub_obj_type_list opt_definition | CREATE PUBLICATION name FOR ALL TABLES opt_definition
{ {
CreatePublicationStmt *n = makeNode(CreatePublicationStmt); CreatePublicationStmt *n = makeNode(CreatePublicationStmt);
n->pubname = $3; n->pubname = $3;
n->options = $7; n->options = $7;
n->for_all_objects = $6; n->for_all_tables = true;
$$ = (Node *)n; $$ = (Node *)n;
} }
| CREATE PUBLICATION name FOR pub_obj_list opt_definition | CREATE PUBLICATION name FOR pub_obj_list opt_definition
@ -9932,26 +9934,6 @@ PublicationObjSpec:
$$->pubobjtype = PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA; $$->pubobjtype = PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA;
$$->location = @5; $$->location = @5;
} }
| SEQUENCE relation_expr
{
$$ = makeNode(PublicationObjSpec);
$$->pubobjtype = PUBLICATIONOBJ_SEQUENCE;
$$->pubtable = makeNode(PublicationTable);
$$->pubtable->relation = $2;
}
| ALL SEQUENCES IN_P SCHEMA ColId
{
$$ = makeNode(PublicationObjSpec);
$$->pubobjtype = PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA;
$$->name = $5;
$$->location = @5;
}
| ALL SEQUENCES IN_P SCHEMA CURRENT_SCHEMA
{
$$ = makeNode(PublicationObjSpec);
$$->pubobjtype = PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA;
$$->location = @5;
}
| ColId opt_column_list OptWhereClause | ColId opt_column_list OptWhereClause
{ {
$$ = makeNode(PublicationObjSpec); $$ = makeNode(PublicationObjSpec);
@ -10013,19 +9995,6 @@ pub_obj_list: PublicationObjSpec
{ $$ = lappend($1, $3); } { $$ = lappend($1, $3); }
; ;
pub_obj_type: TABLES
{ $$ = (Node *) makeString("tables"); }
| SEQUENCES
{ $$ = (Node *) makeString("sequences"); }
;
pub_obj_type_list: pub_obj_type
{ $$ = list_make1($1); }
| pub_obj_type_list ',' pub_obj_type
{ $$ = lappend($1, $3); }
;
/***************************************************************************** /*****************************************************************************
* *
* ALTER PUBLICATION name SET ( options ) * ALTER PUBLICATION name SET ( options )
@ -10036,6 +10005,11 @@ pub_obj_type_list: pub_obj_type
* *
* ALTER PUBLICATION name SET pub_obj [, ...] * ALTER PUBLICATION name SET pub_obj [, ...]
* *
* pub_obj is one of:
*
* TABLE table_name [, ...]
* ALL TABLES IN SCHEMA schema_name [, ...]
*
*****************************************************************************/ *****************************************************************************/
AlterPublicationStmt: AlterPublicationStmt:
@ -18757,8 +18731,7 @@ preprocess_pubobj_list(List *pubobjspec_list, core_yyscan_t yyscanner)
if (pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION) if (pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION)
pubobj->pubobjtype = prevobjtype; pubobj->pubobjtype = prevobjtype;
if (pubobj->pubobjtype == PUBLICATIONOBJ_TABLE || if (pubobj->pubobjtype == PUBLICATIONOBJ_TABLE)
pubobj->pubobjtype == PUBLICATIONOBJ_SEQUENCE)
{ {
/* relation name or pubtable must be set for this type of object */ /* relation name or pubtable must be set for this type of object */
if (!pubobj->name && !pubobj->pubtable) if (!pubobj->name && !pubobj->pubtable)
@ -18809,30 +18782,6 @@ preprocess_pubobj_list(List *pubobjspec_list, core_yyscan_t yyscanner)
errmsg("invalid schema name at or near"), errmsg("invalid schema name at or near"),
parser_errposition(pubobj->location)); parser_errposition(pubobj->location));
} }
else if (pubobj->pubobjtype == PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA ||
pubobj->pubobjtype == PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA)
{
/* WHERE clause is not allowed on a schema object */
if (pubobj->pubtable && pubobj->pubtable->whereClause)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("WHERE clause not allowed for schema"),
parser_errposition(pubobj->location));
/*
* We can distinguish between the different type of schema
* objects based on whether name and pubtable is set.
*/
if (pubobj->name)
pubobj->pubobjtype = PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA;
else if (!pubobj->name && !pubobj->pubtable)
pubobj->pubobjtype = PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA;
else
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid schema name at or near"),
parser_errposition(pubobj->location));
}
prevobjtype = pubobj->pubobjtype; prevobjtype = pubobj->pubobjtype;
} }

View File

@ -42,7 +42,6 @@
#include "replication/reorderbuffer.h" #include "replication/reorderbuffer.h"
#include "replication/snapbuild.h" #include "replication/snapbuild.h"
#include "storage/standby.h" #include "storage/standby.h"
#include "commands/sequence.h"
/* individual record(group)'s handlers */ /* individual record(group)'s handlers */
static void DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf); static void DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
@ -64,7 +63,6 @@ static void DecodePrepare(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
/* common function to decode tuples */ /* common function to decode tuples */
static void DecodeXLogTuple(char *data, Size len, ReorderBufferTupleBuf *tup); static void DecodeXLogTuple(char *data, Size len, ReorderBufferTupleBuf *tup);
static void DecodeSeqTuple(char *data, Size len, ReorderBufferTupleBuf *tuple);
/* helper functions for decoding transactions */ /* helper functions for decoding transactions */
static inline bool FilterPrepare(LogicalDecodingContext *ctx, static inline bool FilterPrepare(LogicalDecodingContext *ctx,
@ -1252,132 +1250,3 @@ DecodeTXNNeedSkip(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
(txn_dbid != InvalidOid && txn_dbid != ctx->slot->data.database) || (txn_dbid != InvalidOid && txn_dbid != ctx->slot->data.database) ||
ctx->fast_forward || FilterByOrigin(ctx, origin_id)); ctx->fast_forward || FilterByOrigin(ctx, origin_id));
} }
/*
* DecodeSeqTuple
* decode tuple describing the sequence increment
*
* Sequences are represented as a table with a single row, which gets updated
* by nextval(). The tuple is stored in WAL right after the xl_seq_rec, so we
* simply copy it into the tuplebuf (similar to seq_redo).
*/
static void
DecodeSeqTuple(char *data, Size len, ReorderBufferTupleBuf *tuple)
{
int datalen = len - sizeof(xl_seq_rec) - SizeofHeapTupleHeader;
Assert(datalen >= 0);
tuple->tuple.t_len = datalen + SizeofHeapTupleHeader;
ItemPointerSetInvalid(&tuple->tuple.t_self);
tuple->tuple.t_tableOid = InvalidOid;
memcpy(((char *) tuple->tuple.t_data),
data + sizeof(xl_seq_rec),
SizeofHeapTupleHeader);
memcpy(((char *) tuple->tuple.t_data) + SizeofHeapTupleHeader,
data + sizeof(xl_seq_rec) + SizeofHeapTupleHeader,
datalen);
}
/*
* Handle sequence decode
*
* Decoding sequences is a bit tricky, because while most sequence actions
* are non-transactional (not subject to rollback), some need to be handled
* as transactional.
*
* By default, a sequence increment is non-transactional - we must not queue
* it in a transaction as other changes, because the transaction might get
* rolled back and we'd discard the increment. The downstream would not be
* notified about the increment, which is wrong.
*
* On the other hand, the sequence may be created in a transaction. In this
* case we *should* queue the change as other changes in the transaction,
* because we don't want to send the increments for unknown sequence to the
* plugin - it might get confused about which sequence it's related to etc.
*/
void
sequence_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
{
SnapBuild *builder = ctx->snapshot_builder;
ReorderBufferTupleBuf *tuplebuf;
RelFileNode target_node;
XLogReaderState *r = buf->record;
char *tupledata = NULL;
Size tuplelen;
Size datalen = 0;
TransactionId xid = XLogRecGetXid(r);
uint8 info = XLogRecGetInfo(buf->record) & ~XLR_INFO_MASK;
xl_seq_rec *xlrec;
Snapshot snapshot;
RepOriginId origin_id = XLogRecGetOrigin(r);
bool transactional;
/* only decode changes flagged with XLOG_SEQ_LOG */
if (info != XLOG_SEQ_LOG)
elog(ERROR, "unexpected RM_SEQ_ID record type: %u", info);
ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(r), buf->origptr);
/*
* If we don't have snapshot or we are just fast-forwarding, there is no
* point in decoding messages.
*/
if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT ||
ctx->fast_forward)
return;
/* only interested in our database */
XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
if (target_node.dbNode != ctx->slot->data.database)
return;
/* output plugin doesn't look for this origin, no need to queue */
if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
return;
tupledata = XLogRecGetData(r);
datalen = XLogRecGetDataLen(r);
tuplelen = datalen - SizeOfHeapHeader - sizeof(xl_seq_rec);
/* extract the WAL record, with "created" flag */
xlrec = (xl_seq_rec *) XLogRecGetData(r);
/* XXX how could we have sequence change without data? */
if(!datalen || !tupledata)
return;
tuplebuf = ReorderBufferGetTupleBuf(ctx->reorder, tuplelen);
DecodeSeqTuple(tupledata, datalen, tuplebuf);
/*
* Should we handle the sequence increment as transactional or not?
*
* If the sequence was created in a still-running transaction, treat
* it as transactional and queue the increments. Otherwise it needs
* to be treated as non-transactional, in which case we send it to
* the plugin right away.
*/
transactional = ReorderBufferSequenceIsTransactional(ctx->reorder,
target_node,
xlrec->created);
/* Skip the change if already processed (per the snapshot). */
if (transactional &&
!SnapBuildProcessChange(builder, xid, buf->origptr))
return;
else if (!transactional &&
(SnapBuildCurrentState(builder) != SNAPBUILD_CONSISTENT ||
SnapBuildXactNeedsSkip(builder, buf->origptr)))
return;
/* Queue the increment (or send immediately if not transactional). */
snapshot = SnapBuildGetOrBuildSnapshot(builder, xid);
ReorderBufferQueueSequence(ctx->reorder, xid, snapshot, buf->endptr,
origin_id, target_node, transactional,
xlrec->created, tuplebuf);
}

View File

@ -73,10 +73,6 @@ static void truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
static void message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, static void message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
XLogRecPtr message_lsn, bool transactional, XLogRecPtr message_lsn, bool transactional,
const char *prefix, Size message_size, const char *message); const char *prefix, Size message_size, const char *message);
static void sequence_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn, Relation rel,
bool transactional,
int64 last_value, int64 log_cnt, bool is_called);
/* streaming callbacks */ /* streaming callbacks */
static void stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, static void stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
@ -94,10 +90,6 @@ static void stream_change_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn
static void stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, static void stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
XLogRecPtr message_lsn, bool transactional, XLogRecPtr message_lsn, bool transactional,
const char *prefix, Size message_size, const char *message); const char *prefix, Size message_size, const char *message);
static void stream_sequence_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn, Relation rel,
bool transactional,
int64 last_value, int64 log_cnt, bool is_called);
static void stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, static void stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
int nrelations, Relation relations[], ReorderBufferChange *change); int nrelations, Relation relations[], ReorderBufferChange *change);
@ -226,7 +218,6 @@ StartupDecodingContext(List *output_plugin_options,
ctx->reorder->apply_truncate = truncate_cb_wrapper; ctx->reorder->apply_truncate = truncate_cb_wrapper;
ctx->reorder->commit = commit_cb_wrapper; ctx->reorder->commit = commit_cb_wrapper;
ctx->reorder->message = message_cb_wrapper; ctx->reorder->message = message_cb_wrapper;
ctx->reorder->sequence = sequence_cb_wrapper;
/* /*
* To support streaming, we require start/stop/abort/commit/change * To support streaming, we require start/stop/abort/commit/change
@ -243,7 +234,6 @@ StartupDecodingContext(List *output_plugin_options,
(ctx->callbacks.stream_commit_cb != NULL) || (ctx->callbacks.stream_commit_cb != NULL) ||
(ctx->callbacks.stream_change_cb != NULL) || (ctx->callbacks.stream_change_cb != NULL) ||
(ctx->callbacks.stream_message_cb != NULL) || (ctx->callbacks.stream_message_cb != NULL) ||
(ctx->callbacks.stream_sequence_cb != NULL) ||
(ctx->callbacks.stream_truncate_cb != NULL); (ctx->callbacks.stream_truncate_cb != NULL);
/* /*
@ -261,7 +251,6 @@ StartupDecodingContext(List *output_plugin_options,
ctx->reorder->stream_commit = stream_commit_cb_wrapper; ctx->reorder->stream_commit = stream_commit_cb_wrapper;
ctx->reorder->stream_change = stream_change_cb_wrapper; ctx->reorder->stream_change = stream_change_cb_wrapper;
ctx->reorder->stream_message = stream_message_cb_wrapper; ctx->reorder->stream_message = stream_message_cb_wrapper;
ctx->reorder->stream_sequence = stream_sequence_cb_wrapper;
ctx->reorder->stream_truncate = stream_truncate_cb_wrapper; ctx->reorder->stream_truncate = stream_truncate_cb_wrapper;
@ -1216,42 +1205,6 @@ message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
error_context_stack = errcallback.previous; error_context_stack = errcallback.previous;
} }
static void
sequence_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn, Relation rel, bool transactional,
int64 last_value, int64 log_cnt, bool is_called)
{
LogicalDecodingContext *ctx = cache->private_data;
LogicalErrorCallbackState state;
ErrorContextCallback errcallback;
Assert(!ctx->fast_forward);
if (ctx->callbacks.sequence_cb == NULL)
return;
/* Push callback + info on the error context stack */
state.ctx = ctx;
state.callback_name = "sequence";
state.report_location = sequence_lsn;
errcallback.callback = output_plugin_error_callback;
errcallback.arg = (void *) &state;
errcallback.previous = error_context_stack;
error_context_stack = &errcallback;
/* set output state */
ctx->accept_writes = true;
ctx->write_xid = txn != NULL ? txn->xid : InvalidTransactionId;
ctx->write_location = sequence_lsn;
/* do the actual work: call callback */
ctx->callbacks.sequence_cb(ctx, txn, sequence_lsn, rel, transactional,
last_value, log_cnt, is_called);
/* Pop the error context stack */
error_context_stack = errcallback.previous;
}
static void static void
stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
XLogRecPtr first_lsn) XLogRecPtr first_lsn)
@ -1557,47 +1510,6 @@ stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
error_context_stack = errcallback.previous; error_context_stack = errcallback.previous;
} }
static void
stream_sequence_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn, Relation rel,
bool transactional,
int64 last_value, int64 log_cnt, bool is_called)
{
LogicalDecodingContext *ctx = cache->private_data;
LogicalErrorCallbackState state;
ErrorContextCallback errcallback;
Assert(!ctx->fast_forward);
/* We're only supposed to call this when streaming is supported. */
Assert(ctx->streaming);
/* this callback is optional */
if (ctx->callbacks.stream_sequence_cb == NULL)
return;
/* Push callback + info on the error context stack */
state.ctx = ctx;
state.callback_name = "stream_sequence";
state.report_location = sequence_lsn;
errcallback.callback = output_plugin_error_callback;
errcallback.arg = (void *) &state;
errcallback.previous = error_context_stack;
error_context_stack = &errcallback;
/* set output state */
ctx->accept_writes = true;
ctx->write_xid = txn != NULL ? txn->xid : InvalidTransactionId;
ctx->write_location = sequence_lsn;
/* do the actual work: call callback */
ctx->callbacks.sequence_cb(ctx, txn, sequence_lsn, rel, transactional,
last_value, log_cnt, is_called);
/* Pop the error context stack */
error_context_stack = errcallback.previous;
}
static void static void
stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn, stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
int nrelations, Relation relations[], int nrelations, Relation relations[],

View File

@ -662,56 +662,6 @@ logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
pq_sendbytes(out, message, sz); pq_sendbytes(out, message, sz);
} }
/*
* Write SEQUENCE to stream
*/
void
logicalrep_write_sequence(StringInfo out, Relation rel, TransactionId xid,
XLogRecPtr lsn, bool transactional,
int64 last_value, int64 log_cnt, bool is_called)
{
uint8 flags = 0;
char *relname;
pq_sendbyte(out, LOGICAL_REP_MSG_SEQUENCE);
/* transaction ID (if not valid, we're not streaming) */
if (TransactionIdIsValid(xid))
pq_sendint32(out, xid);
pq_sendint8(out, flags);
pq_sendint64(out, lsn);
logicalrep_write_namespace(out, RelationGetNamespace(rel));
relname = RelationGetRelationName(rel);
pq_sendstring(out, relname);
pq_sendint8(out, transactional);
pq_sendint64(out, last_value);
pq_sendint64(out, log_cnt);
pq_sendint8(out, is_called);
}
/*
* Read SEQUENCE from the stream.
*/
void
logicalrep_read_sequence(StringInfo in, LogicalRepSequence *seqdata)
{
/* XXX skipping flags and lsn */
pq_getmsgint(in, 1);
pq_getmsgint64(in);
/* Read relation name from stream */
seqdata->nspname = pstrdup(logicalrep_read_namespace(in));
seqdata->seqname = pstrdup(pq_getmsgstring(in));
seqdata->transactional = pq_getmsgint(in, 1);
seqdata->last_value = pq_getmsgint64(in);
seqdata->log_cnt = pq_getmsgint64(in);
seqdata->is_called = pq_getmsgint(in, 1);
}
/* /*
* Write relation description to the output stream. * Write relation description to the output stream.
*/ */
@ -1286,8 +1236,6 @@ logicalrep_message_type(LogicalRepMsgType action)
return "STREAM ABORT"; return "STREAM ABORT";
case LOGICAL_REP_MSG_STREAM_PREPARE: case LOGICAL_REP_MSG_STREAM_PREPARE:
return "STREAM PREPARE"; return "STREAM PREPARE";
case LOGICAL_REP_MSG_SEQUENCE:
return "SEQUENCE";
} }
elog(ERROR, "invalid logical replication message type \"%c\"", action); elog(ERROR, "invalid logical replication message type \"%c\"", action);

View File

@ -77,40 +77,6 @@
* a bit more memory to the oldest subtransactions, because it's likely * a bit more memory to the oldest subtransactions, because it's likely
* they are the source for the next sequence of changes. * they are the source for the next sequence of changes.
* *
* When decoding sequences, we differentiate between a sequences created
* in a (running) transaction, and sequences created in other (already
* committed) transactions. Changes for sequences created in the same
* top-level transaction are treated as "transactional" i.e. just like
* any other change from that transaction (and discarded in case of a
* rollback). Changes for sequences created earlier are treated as not
* transactional - are processed immediately, as if performed outside
* any transaction (and thus not rolled back).
*
* This mixed behavior is necessary - sequences are non-transactional
* (e.g. ROLLBACK does not undo the sequence increments). But for new
* sequences, we need to handle them in a transactional way, because if
* we ever get some DDL support, the sequence won't exist until the
* transaction gets applied. So we need to ensure the increments don't
* happen until the sequence gets created.
*
* To differentiate which sequences are "old" and which were created
* in a still-running transaction, we track sequences created in running
* transactions in a hash table. Sequences are identified by relfilenode,
* and we track XID of the (sub)transaction that created it. This means
* that if a transaction does something that changes the relfilenode
* (like an alter / reset of a sequence), the new relfilenode will be
* treated as if created in the transaction. The list of sequences gets
* discarded when the transaction completes (commit/rollback).
*
* We don't use the XID to check if it's the same top-level transaction.
* It's enough to know it was created in an in-progress transaction,
* and we know it must be the current one because otherwise it wouldn't
* see the sequence object.
*
* The XID may be valid even for non-transactional sequences - we simply
* keep the XID logged to WAL, it's up to the reorderbuffer to decide if
* the increment is transactional.
*
* ------------------------------------------------------------------------- * -------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
@ -125,7 +91,6 @@
#include "access/xact.h" #include "access/xact.h"
#include "access/xlog_internal.h" #include "access/xlog_internal.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "commands/sequence.h"
#include "lib/binaryheap.h" #include "lib/binaryheap.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
@ -151,13 +116,6 @@ typedef struct ReorderBufferTXNByIdEnt
ReorderBufferTXN *txn; ReorderBufferTXN *txn;
} ReorderBufferTXNByIdEnt; } ReorderBufferTXNByIdEnt;
/* entry for hash table we use to track sequences created in running xacts */
typedef struct ReorderBufferSequenceEnt
{
RelFileNode rnode;
TransactionId xid;
} ReorderBufferSequenceEnt;
/* data structures for (relfilenode, ctid) => (cmin, cmax) mapping */ /* data structures for (relfilenode, ctid) => (cmin, cmax) mapping */
typedef struct ReorderBufferTupleCidKey typedef struct ReorderBufferTupleCidKey
{ {
@ -388,14 +346,6 @@ ReorderBufferAllocate(void)
buffer->by_txn = hash_create("ReorderBufferByXid", 1000, &hash_ctl, buffer->by_txn = hash_create("ReorderBufferByXid", 1000, &hash_ctl,
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
/* hash table of sequences, mapping relfilenode to XID of transaction */
hash_ctl.keysize = sizeof(RelFileNode);
hash_ctl.entrysize = sizeof(ReorderBufferSequenceEnt);
hash_ctl.hcxt = buffer->context;
buffer->sequences = hash_create("ReorderBufferSequenceHash", 1000, &hash_ctl,
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
buffer->by_txn_last_xid = InvalidTransactionId; buffer->by_txn_last_xid = InvalidTransactionId;
buffer->by_txn_last_txn = NULL; buffer->by_txn_last_txn = NULL;
@ -582,13 +532,6 @@ ReorderBufferReturnChange(ReorderBuffer *rb, ReorderBufferChange *change,
change->data.truncate.relids = NULL; change->data.truncate.relids = NULL;
} }
break; break;
case REORDER_BUFFER_CHANGE_SEQUENCE:
if (change->data.sequence.tuple)
{
ReorderBufferReturnTupleBuf(rb, change->data.sequence.tuple);
change->data.sequence.tuple = NULL;
}
break;
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM: case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT: case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID: case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
@ -923,230 +866,6 @@ ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
} }
} }
/*
* Treat the sequence increment as transactional?
*
* The hash table tracks all sequences created in in-progress transactions,
* so we simply do a lookup (the sequence is identified by relfilende). If
* we find a match, the increment should be handled as transactional.
*/
bool
ReorderBufferSequenceIsTransactional(ReorderBuffer *rb,
RelFileNode rnode, bool created)
{
bool found = false;
if (created)
return true;
hash_search(rb->sequences,
(void *) &rnode,
HASH_FIND,
&found);
return found;
}
/*
* Cleanup sequences created in in-progress transactions.
*
* There's no way to search by XID, so we simply do a seqscan of all
* the entries in the hash table. Hopefully there are only a couple
* entries in most cases - people generally don't create many new
* sequences over and over.
*/
static void
ReorderBufferSequenceCleanup(ReorderBuffer *rb, TransactionId xid)
{
HASH_SEQ_STATUS scan_status;
ReorderBufferSequenceEnt *ent;
hash_seq_init(&scan_status, rb->sequences);
while ((ent = (ReorderBufferSequenceEnt *) hash_seq_search(&scan_status)) != NULL)
{
/* skip sequences not from this transaction */
if (ent->xid != xid)
continue;
(void) hash_search(rb->sequences,
(void *) &(ent->rnode),
HASH_REMOVE, NULL);
}
}
/*
* A transactional sequence increment is queued to be processed upon commit
* and a non-transactional increment gets processed immediately.
*
* A sequence update may be both transactional and non-transactional. When
* created in a running transaction, treat it as transactional and queue
* the change in it. Otherwise treat it as non-transactional, so that we
* don't forget the increment in case of a rollback.
*/
void
ReorderBufferQueueSequence(ReorderBuffer *rb, TransactionId xid,
Snapshot snapshot, XLogRecPtr lsn, RepOriginId origin_id,
RelFileNode rnode, bool transactional, bool created,
ReorderBufferTupleBuf *tuplebuf)
{
/*
* Change needs to be handled as transactional, because the sequence was
* created in a transaction that is still running. In that case all the
* changes need to be queued in that transaction, we must not send them
* to the downstream until the transaction commits.
*
* There's a bit of a trouble with subtransactions - we can't queue it
* into the subxact, because it might be rolled back and we'd lose the
* increment. We need to queue it into the same (sub)xact that created
* the sequence, which is why we track the XID in the hash table.
*/
if (transactional)
{
MemoryContext oldcontext;
ReorderBufferChange *change;
/* lookup sequence by relfilenode */
ReorderBufferSequenceEnt *ent;
bool found;
/* transactional changes require a transaction */
Assert(xid != InvalidTransactionId);
/* search the lookup table (we ignore the return value, found is enough) */
ent = hash_search(rb->sequences,
(void *) &rnode,
created ? HASH_ENTER : HASH_FIND,
&found);
/*
* If this is the "create" increment, we must not have found any
* pre-existing entry in the hash table (i.e. there must not be
* any conflicting sequence).
*/
Assert(!(created && found));
/* But we must have either created or found an existing entry. */
Assert(created || found);
/*
* When creating the sequence, remember the XID of the transaction
* that created id.
*/
if (created)
ent->xid = xid;
/* XXX Maybe check that we're still in the same top-level xact? */
/* OK, allocate and queue the change */
oldcontext = MemoryContextSwitchTo(rb->context);
change = ReorderBufferGetChange(rb);
change->action = REORDER_BUFFER_CHANGE_SEQUENCE;
change->origin_id = origin_id;
memcpy(&change->data.sequence.relnode, &rnode, sizeof(RelFileNode));
change->data.sequence.tuple = tuplebuf;
/* add it to the same subxact that created the sequence */
ReorderBufferQueueChange(rb, ent->xid, lsn, change, false);
MemoryContextSwitchTo(oldcontext);
}
else
{
/*
* This increment is for a sequence that was not created in any
* running transaction, so we treat it as non-transactional and
* just send it to the output plugin directly.
*/
ReorderBufferTXN *txn = NULL;
volatile Snapshot snapshot_now = snapshot;
bool using_subtxn;
#ifdef USE_ASSERT_CHECKING
/* All "creates" have to be handled as transactional. */
Assert(!created);
/* Make sure the sequence is not in the hash table. */
{
bool found;
hash_search(rb->sequences,
(void *) &rnode,
HASH_FIND, &found);
Assert(!found);
}
#endif
if (xid != InvalidTransactionId)
txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
/* setup snapshot to allow catalog access */
SetupHistoricSnapshot(snapshot_now, NULL);
/*
* Decoding needs access to syscaches et al., which in turn use
* heavyweight locks and such. Thus we need to have enough state around to
* keep track of those. The easiest way is to simply use a transaction
* internally. That also allows us to easily enforce that nothing writes
* to the database by checking for xid assignments.
*
* When we're called via the SQL SRF there's already a transaction
* started, so start an explicit subtransaction there.
*/
using_subtxn = IsTransactionOrTransactionBlock();
PG_TRY();
{
Relation relation;
HeapTuple tuple;
Form_pg_sequence_data seq;
Oid reloid;
if (using_subtxn)
BeginInternalSubTransaction("sequence");
else
StartTransactionCommand();
reloid = RelidByRelfilenode(rnode.spcNode, rnode.relNode);
if (reloid == InvalidOid)
elog(ERROR, "could not map filenode \"%s\" to relation OID",
relpathperm(rnode,
MAIN_FORKNUM));
relation = RelationIdGetRelation(reloid);
tuple = &tuplebuf->tuple;
seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
rb->sequence(rb, txn, lsn, relation, transactional,
seq->last_value, seq->log_cnt, seq->is_called);
RelationClose(relation);
TeardownHistoricSnapshot(false);
AbortCurrentTransaction();
if (using_subtxn)
RollbackAndReleaseCurrentSubTransaction();
}
PG_CATCH();
{
TeardownHistoricSnapshot(true);
AbortCurrentTransaction();
if (using_subtxn)
RollbackAndReleaseCurrentSubTransaction();
PG_RE_THROW();
}
PG_END_TRY();
}
}
/* /*
* AssertTXNLsnOrder * AssertTXNLsnOrder
* Verify LSN ordering of transaction lists in the reorderbuffer * Verify LSN ordering of transaction lists in the reorderbuffer
@ -1823,9 +1542,6 @@ ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
&found); &found);
Assert(found); Assert(found);
/* Remove sequences created in this transaction (if any). */
ReorderBufferSequenceCleanup(rb, txn->xid);
/* remove entries spilled to disk */ /* remove entries spilled to disk */
if (rbtxn_is_serialized(txn)) if (rbtxn_is_serialized(txn))
ReorderBufferRestoreCleanup(rb, txn); ReorderBufferRestoreCleanup(rb, txn);
@ -2241,29 +1957,6 @@ ReorderBufferApplyMessage(ReorderBuffer *rb, ReorderBufferTXN *txn,
change->data.msg.message); change->data.msg.message);
} }
/*
* Helper function for ReorderBufferProcessTXN for applying sequences.
*/
static inline void
ReorderBufferApplySequence(ReorderBuffer *rb, ReorderBufferTXN *txn,
Relation relation, ReorderBufferChange *change,
bool streaming)
{
HeapTuple tuple;
Form_pg_sequence_data seq;
tuple = &change->data.sequence.tuple->tuple;
seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
/* Only ever called from ReorderBufferApplySequence, so transational. */
if (streaming)
rb->stream_sequence(rb, txn, change->lsn, relation, true,
seq->last_value, seq->log_cnt, seq->is_called);
else
rb->sequence(rb, txn, change->lsn, relation, true,
seq->last_value, seq->log_cnt, seq->is_called);
}
/* /*
* Function to store the command id and snapshot at the end of the current * Function to store the command id and snapshot at the end of the current
* stream so that we can reuse the same while sending the next stream. * stream so that we can reuse the same while sending the next stream.
@ -2706,31 +2399,6 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID: case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
elog(ERROR, "tuplecid value in changequeue"); elog(ERROR, "tuplecid value in changequeue");
break; break;
case REORDER_BUFFER_CHANGE_SEQUENCE:
Assert(snapshot_now);
reloid = RelidByRelfilenode(change->data.sequence.relnode.spcNode,
change->data.sequence.relnode.relNode);
if (reloid == InvalidOid)
elog(ERROR, "could not map filenode \"%s\" to relation OID",
relpathperm(change->data.sequence.relnode,
MAIN_FORKNUM));
relation = RelationIdGetRelation(reloid);
if (!RelationIsValid(relation))
elog(ERROR, "could not open relation with OID %u (for filenode \"%s\")",
reloid,
relpathperm(change->data.sequence.relnode,
MAIN_FORKNUM));
if (RelationIsLogicallyLogged(relation))
ReorderBufferApplySequence(rb, txn, relation, change, streaming);
RelationClose(relation);
break;
} }
} }
@ -4115,39 +3783,6 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
memcpy(data, change->data.truncate.relids, size); memcpy(data, change->data.truncate.relids, size);
data += size; data += size;
break;
}
case REORDER_BUFFER_CHANGE_SEQUENCE:
{
char *data;
ReorderBufferTupleBuf *tup;
Size len = 0;
tup = change->data.sequence.tuple;
if (tup)
{
sz += sizeof(HeapTupleData);
len = tup->tuple.t_len;
sz += len;
}
/* make sure we have enough space */
ReorderBufferSerializeReserve(rb, sz);
data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
/* might have been reallocated above */
ondisk = (ReorderBufferDiskChange *) rb->outbuf;
if (len)
{
memcpy(data, &tup->tuple, sizeof(HeapTupleData));
data += sizeof(HeapTupleData);
memcpy(data, tup->tuple.t_data, len);
data += len;
}
break; break;
} }
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM: case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
@ -4412,22 +4047,6 @@ ReorderBufferChangeSize(ReorderBufferChange *change)
{ {
sz += sizeof(Oid) * change->data.truncate.nrelids; sz += sizeof(Oid) * change->data.truncate.nrelids;
break;
}
case REORDER_BUFFER_CHANGE_SEQUENCE:
{
ReorderBufferTupleBuf *tup;
Size len = 0;
tup = change->data.sequence.tuple;
if (tup)
{
sz += sizeof(HeapTupleData);
len = tup->tuple.t_len;
sz += len;
}
break; break;
} }
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM: case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
@ -4729,30 +4348,6 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
break; break;
} }
case REORDER_BUFFER_CHANGE_SEQUENCE:
if (change->data.sequence.tuple)
{
uint32 tuplelen = ((HeapTuple) data)->t_len;
change->data.sequence.tuple =
ReorderBufferGetTupleBuf(rb, tuplelen - SizeofHeapTupleHeader);
/* restore ->tuple */
memcpy(&change->data.sequence.tuple->tuple, data,
sizeof(HeapTupleData));
data += sizeof(HeapTupleData);
/* reset t_data pointer into the new tuplebuf */
change->data.sequence.tuple->tuple.t_data =
ReorderBufferTupleBufData(change->data.sequence.tuple);
/* restore tuple data itself */
memcpy(change->data.sequence.tuple->tuple.t_data, data, tuplelen);
data += tuplelen;
}
break;
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM: case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT: case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID: case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:

View File

@ -100,7 +100,6 @@
#include "catalog/pg_subscription_rel.h" #include "catalog/pg_subscription_rel.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/copy.h" #include "commands/copy.h"
#include "commands/sequence.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "pgstat.h" #include "pgstat.h"
@ -1137,95 +1136,6 @@ copy_table(Relation rel)
logicalrep_rel_close(relmapentry, NoLock); logicalrep_rel_close(relmapentry, NoLock);
} }
/*
* Fetch sequence data (current state) from the remote node.
*/
static void
fetch_sequence_data(char *nspname, char *relname,
int64 *last_value, int64 *log_cnt, bool *is_called)
{
WalRcvExecResult *res;
StringInfoData cmd;
TupleTableSlot *slot;
Oid tableRow[3] = {INT8OID, INT8OID, BOOLOID};
initStringInfo(&cmd);
appendStringInfo(&cmd, "SELECT last_value, log_cnt, is_called\n"
" FROM %s", quote_qualified_identifier(nspname, relname));
res = walrcv_exec(LogRepWorkerWalRcvConn, cmd.data, 3, tableRow);
pfree(cmd.data);
if (res->status != WALRCV_OK_TUPLES)
ereport(ERROR,
(errmsg("could not receive list of replicated tables from the publisher: %s",
res->err)));
/* Process the sequence. */
slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple);
while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
{
bool isnull;
*last_value = DatumGetInt64(slot_getattr(slot, 1, &isnull));
Assert(!isnull);
*log_cnt = DatumGetInt64(slot_getattr(slot, 2, &isnull));
Assert(!isnull);
*is_called = DatumGetBool(slot_getattr(slot, 3, &isnull));
Assert(!isnull);
ExecClearTuple(slot);
}
ExecDropSingleTupleTableSlot(slot);
walrcv_clear_result(res);
}
/*
* Copy existing data of a sequence from publisher.
*
* Caller is responsible for locking the local relation.
*/
static void
copy_sequence(Relation rel)
{
LogicalRepRelMapEntry *relmapentry;
LogicalRepRelation lrel;
List *qual = NIL;
StringInfoData cmd;
int64 last_value = 0,
log_cnt = 0;
bool is_called = 0;
/* Get the publisher relation info. */
fetch_remote_table_info(get_namespace_name(RelationGetNamespace(rel)),
RelationGetRelationName(rel), &lrel, &qual);
/* sequences don't have row filters */
Assert(!qual);
/* Put the relation into relmap. */
logicalrep_relmap_update(&lrel);
/* Map the publisher relation to local one. */
relmapentry = logicalrep_rel_open(lrel.remoteid, NoLock);
Assert(rel == relmapentry->localrel);
/* Start copy on the publisher. */
initStringInfo(&cmd);
Assert(lrel.relkind == RELKIND_SEQUENCE);
fetch_sequence_data(lrel.nspname, lrel.relname, &last_value, &log_cnt, &is_called);
/* tablesync sets the sequences in non-transactional way */
SetSequence(RelationGetRelid(rel), false, last_value, log_cnt, is_called);
logicalrep_rel_close(relmapentry, NoLock);
}
/* /*
* Determine the tablesync slot name. * Determine the tablesync slot name.
* *
@ -1487,21 +1397,10 @@ LogicalRepSyncTableStart(XLogRecPtr *origin_startpos)
originname))); originname)));
} }
/* Do the right action depending on the relation kind. */ /* Now do the initial data copy */
if (get_rel_relkind(RelationGetRelid(rel)) == RELKIND_SEQUENCE) PushActiveSnapshot(GetTransactionSnapshot());
{ copy_table(rel);
/* Now do the initial sequence copy */ PopActiveSnapshot();
PushActiveSnapshot(GetTransactionSnapshot());
copy_sequence(rel);
PopActiveSnapshot();
}
else
{
/* Now do the initial data copy */
PushActiveSnapshot(GetTransactionSnapshot());
copy_table(rel);
PopActiveSnapshot();
}
res = walrcv_exec(LogRepWorkerWalRcvConn, "COMMIT", 0, NULL); res = walrcv_exec(LogRepWorkerWalRcvConn, "COMMIT", 0, NULL);
if (res->status != WALRCV_OK_COMMAND) if (res->status != WALRCV_OK_COMMAND)

View File

@ -143,7 +143,6 @@
#include "catalog/pg_subscription.h" #include "catalog/pg_subscription.h"
#include "catalog/pg_subscription_rel.h" #include "catalog/pg_subscription_rel.h"
#include "catalog/pg_tablespace.h" #include "catalog/pg_tablespace.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
#include "commands/tablespace.h" #include "commands/tablespace.h"
#include "commands/trigger.h" #include "commands/trigger.h"
@ -1144,57 +1143,6 @@ apply_handle_origin(StringInfo s)
errmsg_internal("ORIGIN message sent out of order"))); errmsg_internal("ORIGIN message sent out of order")));
} }
/*
* Handle SEQUENCE message.
*/
static void
apply_handle_sequence(StringInfo s)
{
LogicalRepSequence seq;
Oid relid;
if (handle_streamed_transaction(LOGICAL_REP_MSG_SEQUENCE, s))
return;
logicalrep_read_sequence(s, &seq);
/*
* Non-transactional sequence updates should not be part of a remote
* transaction. There should not be any running transaction.
*/
Assert((!seq.transactional) || in_remote_transaction);
Assert(!(!seq.transactional && in_remote_transaction));
Assert(!(!seq.transactional && IsTransactionState()));
/*
* Make sure we're in a transaction (needed by SetSequence). For
* non-transactional updates we're guaranteed to start a new one,
* and we'll commit it at the end.
*/
if (!IsTransactionState())
{
StartTransactionCommand();
maybe_reread_subscription();
}
relid = RangeVarGetRelid(makeRangeVar(seq.nspname,
seq.seqname, -1),
RowExclusiveLock, false);
/* lock the sequence in AccessExclusiveLock, as expected by SetSequence */
LockRelationOid(relid, AccessExclusiveLock);
/* apply the sequence change */
SetSequence(relid, seq.transactional, seq.last_value, seq.log_cnt, seq.is_called);
/*
* Commit the per-stream transaction (we only do this when not in
* remote transaction, i.e. for non-transactional sequence updates.
*/
if (!in_remote_transaction)
CommitTransactionCommand();
}
/* /*
* Handle STREAM START message. * Handle STREAM START message.
*/ */
@ -2563,10 +2511,6 @@ apply_dispatch(StringInfo s)
*/ */
break; break;
case LOGICAL_REP_MSG_SEQUENCE:
apply_handle_sequence(s);
return;
case LOGICAL_REP_MSG_STREAM_START: case LOGICAL_REP_MSG_STREAM_START:
apply_handle_stream_start(s); apply_handle_stream_start(s);
break; break;

View File

@ -15,7 +15,6 @@
#include "access/tupconvert.h" #include "access/tupconvert.h"
#include "catalog/partition.h" #include "catalog/partition.h"
#include "catalog/pg_publication.h" #include "catalog/pg_publication.h"
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_publication_rel.h" #include "catalog/pg_publication_rel.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "executor/executor.h" #include "executor/executor.h"
@ -55,10 +54,6 @@ static void pgoutput_message(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, XLogRecPtr message_lsn, ReorderBufferTXN *txn, XLogRecPtr message_lsn,
bool transactional, const char *prefix, bool transactional, const char *prefix,
Size sz, const char *message); Size sz, const char *message);
static void pgoutput_sequence(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, XLogRecPtr sequence_lsn,
Relation relation, bool transactional,
int64 last_value, int64 log_cnt, bool is_called);
static bool pgoutput_origin_filter(LogicalDecodingContext *ctx, static bool pgoutput_origin_filter(LogicalDecodingContext *ctx,
RepOriginId origin_id); RepOriginId origin_id);
static void pgoutput_begin_prepare_txn(LogicalDecodingContext *ctx, static void pgoutput_begin_prepare_txn(LogicalDecodingContext *ctx,
@ -260,7 +255,6 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
cb->change_cb = pgoutput_change; cb->change_cb = pgoutput_change;
cb->truncate_cb = pgoutput_truncate; cb->truncate_cb = pgoutput_truncate;
cb->message_cb = pgoutput_message; cb->message_cb = pgoutput_message;
cb->sequence_cb = pgoutput_sequence;
cb->commit_cb = pgoutput_commit_txn; cb->commit_cb = pgoutput_commit_txn;
cb->begin_prepare_cb = pgoutput_begin_prepare_txn; cb->begin_prepare_cb = pgoutput_begin_prepare_txn;
@ -277,7 +271,6 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
cb->stream_commit_cb = pgoutput_stream_commit; cb->stream_commit_cb = pgoutput_stream_commit;
cb->stream_change_cb = pgoutput_change; cb->stream_change_cb = pgoutput_change;
cb->stream_message_cb = pgoutput_message; cb->stream_message_cb = pgoutput_message;
cb->stream_sequence_cb = pgoutput_sequence;
cb->stream_truncate_cb = pgoutput_truncate; cb->stream_truncate_cb = pgoutput_truncate;
/* transaction streaming - two-phase commit */ /* transaction streaming - two-phase commit */
cb->stream_prepare_cb = pgoutput_stream_prepare_txn; cb->stream_prepare_cb = pgoutput_stream_prepare_txn;
@ -291,7 +284,6 @@ parse_output_parameters(List *options, PGOutputData *data)
bool publication_names_given = false; bool publication_names_given = false;
bool binary_option_given = false; bool binary_option_given = false;
bool messages_option_given = false; bool messages_option_given = false;
bool sequences_option_given = false;
bool streaming_given = false; bool streaming_given = false;
bool two_phase_option_given = false; bool two_phase_option_given = false;
@ -299,7 +291,6 @@ parse_output_parameters(List *options, PGOutputData *data)
data->streaming = false; data->streaming = false;
data->messages = false; data->messages = false;
data->two_phase = false; data->two_phase = false;
data->sequences = true;
foreach(lc, options) foreach(lc, options)
{ {
@ -368,16 +359,6 @@ parse_output_parameters(List *options, PGOutputData *data)
data->messages = defGetBoolean(defel); data->messages = defGetBoolean(defel);
} }
else if (strcmp(defel->defname, "sequences") == 0)
{
if (sequences_option_given)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
sequences_option_given = true;
data->sequences = defGetBoolean(defel);
}
else if (strcmp(defel->defname, "streaming") == 0) else if (strcmp(defel->defname, "streaming") == 0)
{ {
if (streaming_given) if (streaming_given)
@ -1709,64 +1690,6 @@ pgoutput_message(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
OutputPluginWrite(ctx, true); OutputPluginWrite(ctx, true);
} }
static void
pgoutput_sequence(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, XLogRecPtr sequence_lsn,
Relation relation, bool transactional,
int64 last_value, int64 log_cnt, bool is_called)
{
PGOutputData *data = (PGOutputData *) ctx->output_plugin_private;
TransactionId xid = InvalidTransactionId;
RelationSyncEntry *relentry;
if (!data->sequences)
return;
if (!is_publishable_relation(relation))
return;
/*
* Remember the xid for the message in streaming mode. See
* pgoutput_change.
*/
if (in_streaming)
xid = txn->xid;
relentry = get_rel_sync_entry(data, relation);
/*
* First check the sequence filter.
*
* We handle just REORDER_BUFFER_CHANGE_SEQUENCE here.
*/
if (!relentry->pubactions.pubsequence)
return;
/*
* Output BEGIN if we haven't yet. Avoid for non-transactional
* sequence changes.
*/
if (transactional)
{
PGOutputTxnData *txndata = (PGOutputTxnData *) txn->output_plugin_private;
/* Send BEGIN if we haven't yet */
if (txndata && !txndata->sent_begin_txn)
pgoutput_send_begin(ctx, txn);
}
OutputPluginPrepareWrite(ctx, true);
logicalrep_write_sequence(ctx->out,
relation,
xid,
sequence_lsn,
transactional,
last_value,
log_cnt,
is_called);
OutputPluginWrite(ctx, true);
}
/* /*
* Currently we always forward. * Currently we always forward.
*/ */
@ -2052,8 +1975,7 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
entry->schema_sent = false; entry->schema_sent = false;
entry->streamed_txns = NIL; entry->streamed_txns = NIL;
entry->pubactions.pubinsert = entry->pubactions.pubupdate = entry->pubactions.pubinsert = entry->pubactions.pubupdate =
entry->pubactions.pubdelete = entry->pubactions.pubtruncate = entry->pubactions.pubdelete = entry->pubactions.pubtruncate = false;
entry->pubactions.pubsequence = false;
entry->new_slot = NULL; entry->new_slot = NULL;
entry->old_slot = NULL; entry->old_slot = NULL;
memset(entry->exprstate, 0, sizeof(entry->exprstate)); memset(entry->exprstate, 0, sizeof(entry->exprstate));
@ -2068,18 +1990,18 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
{ {
Oid schemaId = get_rel_namespace(relid); Oid schemaId = get_rel_namespace(relid);
List *pubids = GetRelationPublications(relid); List *pubids = GetRelationPublications(relid);
char relkind = get_rel_relkind(relid);
char objectType = pub_get_object_type_for_relkind(relkind);
/* /*
* We don't acquire a lock on the namespace system table as we build * We don't acquire a lock on the namespace system table as we build
* the cache entry using a historic snapshot and all the later changes * the cache entry using a historic snapshot and all the later changes
* are absorbed while decoding WAL. * are absorbed while decoding WAL.
*/ */
List *schemaPubids = GetSchemaPublications(schemaId, objectType); List *schemaPubids = GetSchemaPublications(schemaId);
ListCell *lc; ListCell *lc;
Oid publish_as_relid = relid; Oid publish_as_relid = relid;
int publish_ancestor_level = 0; int publish_ancestor_level = 0;
bool am_partition = get_rel_relispartition(relid); bool am_partition = get_rel_relispartition(relid);
char relkind = get_rel_relkind(relid);
List *rel_publications = NIL; List *rel_publications = NIL;
/* Reload publications if needed before use. */ /* Reload publications if needed before use. */
@ -2111,7 +2033,6 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
entry->pubactions.pubupdate = false; entry->pubactions.pubupdate = false;
entry->pubactions.pubdelete = false; entry->pubactions.pubdelete = false;
entry->pubactions.pubtruncate = false; entry->pubactions.pubtruncate = false;
entry->pubactions.pubsequence = false;
/* /*
* Tuple slots cleanups. (Will be rebuilt later if needed). * Tuple slots cleanups. (Will be rebuilt later if needed).
@ -2159,11 +2080,9 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
/* /*
* If this is a FOR ALL TABLES publication, pick the partition root * If this is a FOR ALL TABLES publication, pick the partition root
* and set the ancestor level accordingly. If this is a FOR ALL * and set the ancestor level accordingly.
* SEQUENCES publication, we publish it too but we don't need to
* pick the partition root etc.
*/ */
if (pub->alltables || pub->allsequences) if (pub->alltables)
{ {
publish = true; publish = true;
if (pub->pubviaroot && am_partition) if (pub->pubviaroot && am_partition)
@ -2227,7 +2146,6 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
entry->pubactions.pubupdate |= pub->pubactions.pubupdate; entry->pubactions.pubupdate |= pub->pubactions.pubupdate;
entry->pubactions.pubdelete |= pub->pubactions.pubdelete; entry->pubactions.pubdelete |= pub->pubactions.pubdelete;
entry->pubactions.pubtruncate |= pub->pubactions.pubtruncate; entry->pubactions.pubtruncate |= pub->pubactions.pubtruncate;
entry->pubactions.pubsequence |= pub->pubactions.pubsequence;
/* /*
* We want to publish the changes as the top-most ancestor * We want to publish the changes as the top-most ancestor

View File

@ -56,7 +56,6 @@
#include "catalog/pg_opclass.h" #include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h" #include "catalog/pg_proc.h"
#include "catalog/pg_publication.h" #include "catalog/pg_publication.h"
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_rewrite.h" #include "catalog/pg_rewrite.h"
#include "catalog/pg_shseclabel.h" #include "catalog/pg_shseclabel.h"
#include "catalog/pg_statistic_ext.h" #include "catalog/pg_statistic_ext.h"
@ -5568,8 +5567,6 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
Oid schemaid; Oid schemaid;
List *ancestors = NIL; List *ancestors = NIL;
Oid relid = RelationGetRelid(relation); Oid relid = RelationGetRelid(relation);
char relkind = relation->rd_rel->relkind;
char objType;
/* /*
* If not publishable, it publishes no actions. (pgoutput_change() will * If not publishable, it publishes no actions. (pgoutput_change() will
@ -5600,15 +5597,8 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
/* Fetch the publication membership info. */ /* Fetch the publication membership info. */
puboids = GetRelationPublications(relid); puboids = GetRelationPublications(relid);
schemaid = RelationGetNamespace(relation); schemaid = RelationGetNamespace(relation);
objType = pub_get_object_type_for_relkind(relkind); puboids = list_concat_unique_oid(puboids, GetSchemaPublications(schemaid));
puboids = list_concat_unique_oid(puboids,
GetSchemaPublications(schemaid, objType));
/*
* If this is a partion (and thus a table), lookup all ancestors and track
* all publications them too.
*/
if (relation->rd_rel->relispartition) if (relation->rd_rel->relispartition)
{ {
/* Add publications that the ancestors are in too. */ /* Add publications that the ancestors are in too. */
@ -5620,23 +5610,12 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
puboids = list_concat_unique_oid(puboids, puboids = list_concat_unique_oid(puboids,
GetRelationPublications(ancestor)); GetRelationPublications(ancestor));
/* include all publications publishing schema of all ancestors */
schemaid = get_rel_namespace(ancestor); schemaid = get_rel_namespace(ancestor);
puboids = list_concat_unique_oid(puboids, puboids = list_concat_unique_oid(puboids,
GetSchemaPublications(schemaid, GetSchemaPublications(schemaid));
PUB_OBJTYPE_TABLE));
} }
} }
puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
/*
* Consider also FOR ALL TABLES and FOR ALL SEQUENCES publications,
* depending on the relkind of the relation.
*/
if (relation->rd_rel->relkind == RELKIND_SEQUENCE)
puboids = list_concat_unique_oid(puboids, GetAllSequencesPublications());
else
puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
foreach(lc, puboids) foreach(lc, puboids)
{ {
@ -5655,7 +5634,6 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
pubdesc->pubactions.pubupdate |= pubform->pubupdate; pubdesc->pubactions.pubupdate |= pubform->pubupdate;
pubdesc->pubactions.pubdelete |= pubform->pubdelete; pubdesc->pubactions.pubdelete |= pubform->pubdelete;
pubdesc->pubactions.pubtruncate |= pubform->pubtruncate; pubdesc->pubactions.pubtruncate |= pubform->pubtruncate;
pubdesc->pubactions.pubsequence |= pubform->pubsequence;
/* /*
* Check if all columns referenced in the filter expression are part of * Check if all columns referenced in the filter expression are part of

View File

@ -653,12 +653,12 @@ static const struct cachedesc cacheinfo[] = {
64 64
}, },
{PublicationNamespaceRelationId, /* PUBLICATIONNAMESPACEMAP */ {PublicationNamespaceRelationId, /* PUBLICATIONNAMESPACEMAP */
PublicationNamespacePnnspidPnpubidPntypeIndexId, PublicationNamespacePnnspidPnpubidIndexId,
3, 2,
{ {
Anum_pg_publication_namespace_pnnspid, Anum_pg_publication_namespace_pnnspid,
Anum_pg_publication_namespace_pnpubid, Anum_pg_publication_namespace_pnpubid,
Anum_pg_publication_namespace_pntype, 0,
0 0
}, },
64 64

View File

@ -3814,12 +3814,10 @@ getPublications(Archive *fout, int *numPublications)
int i_pubname; int i_pubname;
int i_pubowner; int i_pubowner;
int i_puballtables; int i_puballtables;
int i_puballsequences;
int i_pubinsert; int i_pubinsert;
int i_pubupdate; int i_pubupdate;
int i_pubdelete; int i_pubdelete;
int i_pubtruncate; int i_pubtruncate;
int i_pubsequence;
int i_pubviaroot; int i_pubviaroot;
int i, int i,
ntups; ntups;
@ -3835,29 +3833,23 @@ getPublications(Archive *fout, int *numPublications)
resetPQExpBuffer(query); resetPQExpBuffer(query);
/* Get the publications. */ /* Get the publications. */
if (fout->remoteVersion >= 150000) if (fout->remoteVersion >= 130000)
appendPQExpBuffer(query, appendPQExpBuffer(query,
"SELECT p.tableoid, p.oid, p.pubname, " "SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, " "p.pubowner, "
"p.puballtables, p.puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubsequence, p.pubviaroot " "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot "
"FROM pg_publication p");
else if (fout->remoteVersion >= 130000)
appendPQExpBuffer(query,
"SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, "
"p.puballtables, false AS puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubsequence, p.pubviaroot "
"FROM pg_publication p"); "FROM pg_publication p");
else if (fout->remoteVersion >= 110000) else if (fout->remoteVersion >= 110000)
appendPQExpBuffer(query, appendPQExpBuffer(query,
"SELECT p.tableoid, p.oid, p.pubname, " "SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, " "p.pubowner, "
"p.puballtables, false AS puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubsequence, false AS pubviaroot " "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot "
"FROM pg_publication p"); "FROM pg_publication p");
else else
appendPQExpBuffer(query, appendPQExpBuffer(query,
"SELECT p.tableoid, p.oid, p.pubname, " "SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, " "p.pubowner, "
"p.puballtables, false AS puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubsequence, false AS pubviaroot " "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot "
"FROM pg_publication p"); "FROM pg_publication p");
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@ -3869,12 +3861,10 @@ getPublications(Archive *fout, int *numPublications)
i_pubname = PQfnumber(res, "pubname"); i_pubname = PQfnumber(res, "pubname");
i_pubowner = PQfnumber(res, "pubowner"); i_pubowner = PQfnumber(res, "pubowner");
i_puballtables = PQfnumber(res, "puballtables"); i_puballtables = PQfnumber(res, "puballtables");
i_puballsequences = PQfnumber(res, "puballsequences");
i_pubinsert = PQfnumber(res, "pubinsert"); i_pubinsert = PQfnumber(res, "pubinsert");
i_pubupdate = PQfnumber(res, "pubupdate"); i_pubupdate = PQfnumber(res, "pubupdate");
i_pubdelete = PQfnumber(res, "pubdelete"); i_pubdelete = PQfnumber(res, "pubdelete");
i_pubtruncate = PQfnumber(res, "pubtruncate"); i_pubtruncate = PQfnumber(res, "pubtruncate");
i_pubsequence = PQfnumber(res, "pubsequence");
i_pubviaroot = PQfnumber(res, "pubviaroot"); i_pubviaroot = PQfnumber(res, "pubviaroot");
pubinfo = pg_malloc(ntups * sizeof(PublicationInfo)); pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
@ -3890,8 +3880,6 @@ getPublications(Archive *fout, int *numPublications)
pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner)); pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
pubinfo[i].puballtables = pubinfo[i].puballtables =
(strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0); (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
pubinfo[i].puballsequences =
(strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
pubinfo[i].pubinsert = pubinfo[i].pubinsert =
(strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0); (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
pubinfo[i].pubupdate = pubinfo[i].pubupdate =
@ -3900,8 +3888,6 @@ getPublications(Archive *fout, int *numPublications)
(strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0); (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
pubinfo[i].pubtruncate = pubinfo[i].pubtruncate =
(strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0); (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
pubinfo[i].pubsequence =
(strcmp(PQgetvalue(res, i, i_pubsequence), "t") == 0);
pubinfo[i].pubviaroot = pubinfo[i].pubviaroot =
(strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0); (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
@ -3947,9 +3933,6 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
if (pubinfo->puballtables) if (pubinfo->puballtables)
appendPQExpBufferStr(query, " FOR ALL TABLES"); appendPQExpBufferStr(query, " FOR ALL TABLES");
if (pubinfo->puballsequences)
appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
appendPQExpBufferStr(query, " WITH (publish = '"); appendPQExpBufferStr(query, " WITH (publish = '");
if (pubinfo->pubinsert) if (pubinfo->pubinsert)
{ {
@ -3984,15 +3967,6 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
first = false; first = false;
} }
if (pubinfo->pubsequence)
{
if (!first)
appendPQExpBufferStr(query, ", ");
appendPQExpBufferStr(query, "sequence");
first = false;
}
appendPQExpBufferStr(query, "'"); appendPQExpBufferStr(query, "'");
if (pubinfo->pubviaroot) if (pubinfo->pubviaroot)
@ -4039,7 +4013,6 @@ getPublicationNamespaces(Archive *fout)
int i_oid; int i_oid;
int i_pnpubid; int i_pnpubid;
int i_pnnspid; int i_pnnspid;
int i_pntype;
int i, int i,
j, j,
ntups; ntups;
@ -4051,7 +4024,7 @@ getPublicationNamespaces(Archive *fout)
/* Collect all publication membership info. */ /* Collect all publication membership info. */
appendPQExpBufferStr(query, appendPQExpBufferStr(query,
"SELECT tableoid, oid, pnpubid, pnnspid, pntype " "SELECT tableoid, oid, pnpubid, pnnspid "
"FROM pg_catalog.pg_publication_namespace"); "FROM pg_catalog.pg_publication_namespace");
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@ -4061,7 +4034,6 @@ getPublicationNamespaces(Archive *fout)
i_oid = PQfnumber(res, "oid"); i_oid = PQfnumber(res, "oid");
i_pnpubid = PQfnumber(res, "pnpubid"); i_pnpubid = PQfnumber(res, "pnpubid");
i_pnnspid = PQfnumber(res, "pnnspid"); i_pnnspid = PQfnumber(res, "pnnspid");
i_pntype = PQfnumber(res, "pntype");
/* this allocation may be more than we need */ /* this allocation may be more than we need */
pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo)); pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
@ -4071,7 +4043,6 @@ getPublicationNamespaces(Archive *fout)
{ {
Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid)); Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid)); Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
char pntype = PQgetvalue(res, i, i_pntype)[0];
PublicationInfo *pubinfo; PublicationInfo *pubinfo;
NamespaceInfo *nspinfo; NamespaceInfo *nspinfo;
@ -4103,7 +4074,6 @@ getPublicationNamespaces(Archive *fout)
pubsinfo[j].dobj.name = nspinfo->dobj.name; pubsinfo[j].dobj.name = nspinfo->dobj.name;
pubsinfo[j].publication = pubinfo; pubsinfo[j].publication = pubinfo;
pubsinfo[j].pubschema = nspinfo; pubsinfo[j].pubschema = nspinfo;
pubsinfo[j].pubtype = pntype;
/* Decide whether we want to dump it */ /* Decide whether we want to dump it */
selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout); selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
@ -4269,11 +4239,7 @@ dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
query = createPQExpBuffer(); query = createPQExpBuffer();
appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name)); appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
appendPQExpBuffer(query, "ADD ALL TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
if (pubsinfo->pubtype == 't')
appendPQExpBuffer(query, "ADD ALL TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
else
appendPQExpBuffer(query, "ADD ALL SEQUENCES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
/* /*
* There is no point in creating drop query as the drop is done by schema * There is no point in creating drop query as the drop is done by schema
@ -4306,7 +4272,6 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
TableInfo *tbinfo = pubrinfo->pubtable; TableInfo *tbinfo = pubrinfo->pubtable;
PQExpBuffer query; PQExpBuffer query;
char *tag; char *tag;
char *description;
/* Do nothing in data-only dump */ /* Do nothing in data-only dump */
if (dopt->dataOnly) if (dopt->dataOnly)
@ -4316,19 +4281,8 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
query = createPQExpBuffer(); query = createPQExpBuffer();
if (tbinfo->relkind == RELKIND_SEQUENCE) appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
{ fmtId(pubinfo->dobj.name));
appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD SEQUENCE",
fmtId(pubinfo->dobj.name));
description = "PUBLICATION SEQUENCE";
}
else
{
appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
fmtId(pubinfo->dobj.name));
description = "PUBLICATION TABLE";
}
appendPQExpBuffer(query, " %s", appendPQExpBuffer(query, " %s",
fmtQualifiedDumpable(tbinfo)); fmtQualifiedDumpable(tbinfo));
@ -4357,7 +4311,7 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
ARCHIVE_OPTS(.tag = tag, ARCHIVE_OPTS(.tag = tag,
.namespace = tbinfo->dobj.namespace->dobj.name, .namespace = tbinfo->dobj.namespace->dobj.name,
.owner = pubinfo->rolname, .owner = pubinfo->rolname,
.description = description, .description = "PUBLICATION TABLE",
.section = SECTION_POST_DATA, .section = SECTION_POST_DATA,
.createStmt = query->data)); .createStmt = query->data));

View File

@ -615,12 +615,10 @@ typedef struct _PublicationInfo
DumpableObject dobj; DumpableObject dobj;
const char *rolname; const char *rolname;
bool puballtables; bool puballtables;
bool puballsequences;
bool pubinsert; bool pubinsert;
bool pubupdate; bool pubupdate;
bool pubdelete; bool pubdelete;
bool pubtruncate; bool pubtruncate;
bool pubsequence;
bool pubviaroot; bool pubviaroot;
} PublicationInfo; } PublicationInfo;
@ -646,7 +644,6 @@ typedef struct _PublicationSchemaInfo
DumpableObject dobj; DumpableObject dobj;
PublicationInfo *publication; PublicationInfo *publication;
NamespaceInfo *pubschema; NamespaceInfo *pubschema;
char pubtype;
} PublicationSchemaInfo; } PublicationSchemaInfo;
/* /*

View File

@ -2420,7 +2420,7 @@ my %tests = (
create_order => 50, create_order => 50,
create_sql => 'CREATE PUBLICATION pub1;', create_sql => 'CREATE PUBLICATION pub1;',
regexp => qr/^ regexp => qr/^
\QCREATE PUBLICATION pub1 WITH (publish = 'insert, update, delete, truncate, sequence');\E \QCREATE PUBLICATION pub1 WITH (publish = 'insert, update, delete, truncate');\E
/xm, /xm,
like => { %full_runs, section_post_data => 1, }, like => { %full_runs, section_post_data => 1, },
}, },
@ -2440,27 +2440,16 @@ my %tests = (
create_order => 50, create_order => 50,
create_sql => 'CREATE PUBLICATION pub3;', create_sql => 'CREATE PUBLICATION pub3;',
regexp => qr/^ regexp => qr/^
\QCREATE PUBLICATION pub3 WITH (publish = 'insert, update, delete, truncate, sequence');\E \QCREATE PUBLICATION pub3 WITH (publish = 'insert, update, delete, truncate');\E
/xm, /xm,
like => { %full_runs, section_post_data => 1, }, like => { %full_runs, section_post_data => 1, },
}, },
'CREATE PUBLICATION pub4' => { 'CREATE PUBLICATION pub4' => {
create_order => 50, create_order => 50,
create_sql => 'CREATE PUBLICATION pub4 create_sql => 'CREATE PUBLICATION pub4;',
FOR ALL SEQUENCES
WITH (publish = \'\');',
regexp => qr/^ regexp => qr/^
\QCREATE PUBLICATION pub4 FOR ALL SEQUENCES WITH (publish = '');\E \QCREATE PUBLICATION pub4 WITH (publish = 'insert, update, delete, truncate');\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
'CREATE PUBLICATION pub5' => {
create_order => 50,
create_sql => 'CREATE PUBLICATION pub5;',
regexp => qr/^
\QCREATE PUBLICATION pub5 WITH (publish = 'insert, update, delete, truncate, sequence');\E
/xm, /xm,
like => { %full_runs, section_post_data => 1, }, like => { %full_runs, section_post_data => 1, },
}, },
@ -2569,27 +2558,6 @@ my %tests = (
unlike => { exclude_dump_test_schema => 1, }, unlike => { exclude_dump_test_schema => 1, },
}, },
'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA dump_test' => {
create_order => 51,
create_sql =>
'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA dump_test;',
regexp => qr/^
\QALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA dump_test;\E
/xm,
like => { %full_runs, section_post_data => 1, },
unlike => { exclude_dump_test_schema => 1, },
},
'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA public' => {
create_order => 52,
create_sql =>
'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA public;',
regexp => qr/^
\QALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA public;\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
'CREATE SCHEMA public' => { 'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m, regexp => qr/^CREATE SCHEMA public;/m,

View File

@ -1633,19 +1633,28 @@ describeOneTableDetails(const char *schemaname,
if (tableinfo.relkind == RELKIND_SEQUENCE) if (tableinfo.relkind == RELKIND_SEQUENCE)
{ {
PGresult *result = NULL; PGresult *result = NULL;
printQueryOpt myopt = pset.popt;
char *footers[2] = {NULL, NULL};
if (pset.sversion >= 100000) if (pset.sversion >= 100000)
{ {
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
"SELECT pg_catalog.format_type(seqtypid, NULL),\n" "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
" seqstart,\n" " seqstart AS \"%s\",\n"
" seqmin,\n" " seqmin AS \"%s\",\n"
" seqmax,\n" " seqmax AS \"%s\",\n"
" seqincrement,\n" " seqincrement AS \"%s\",\n"
" CASE WHEN seqcycle THEN '%s' ELSE '%s' END,\n" " CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
" seqcache\n", " seqcache AS \"%s\"\n",
gettext_noop("Type"),
gettext_noop("Start"),
gettext_noop("Minimum"),
gettext_noop("Maximum"),
gettext_noop("Increment"),
gettext_noop("yes"), gettext_noop("yes"),
gettext_noop("no")); gettext_noop("no"),
gettext_noop("Cycles?"),
gettext_noop("Cache"));
appendPQExpBuffer(&buf, appendPQExpBuffer(&buf,
"FROM pg_catalog.pg_sequence\n" "FROM pg_catalog.pg_sequence\n"
"WHERE seqrelid = '%s';", "WHERE seqrelid = '%s';",
@ -1654,15 +1663,22 @@ describeOneTableDetails(const char *schemaname,
else else
{ {
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
"SELECT 'bigint',\n" "SELECT 'bigint' AS \"%s\",\n"
" start_value,\n" " start_value AS \"%s\",\n"
" min_value,\n" " min_value AS \"%s\",\n"
" max_value,\n" " max_value AS \"%s\",\n"
" increment_by,\n" " increment_by AS \"%s\",\n"
" CASE WHEN is_cycled THEN '%s' ELSE '%s' END,\n" " CASE WHEN is_cycled THEN '%s' ELSE '%s' END AS \"%s\",\n"
" cache_value\n", " cache_value AS \"%s\"\n",
gettext_noop("Type"),
gettext_noop("Start"),
gettext_noop("Minimum"),
gettext_noop("Maximum"),
gettext_noop("Increment"),
gettext_noop("yes"), gettext_noop("yes"),
gettext_noop("no")); gettext_noop("no"),
gettext_noop("Cycles?"),
gettext_noop("Cache"));
appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname)); appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname));
/* must be separate because fmtId isn't reentrant */ /* must be separate because fmtId isn't reentrant */
appendPQExpBuffer(&buf, ".%s;", fmtId(relationname)); appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
@ -1672,57 +1688,6 @@ describeOneTableDetails(const char *schemaname,
if (!res) if (!res)
goto error_return; goto error_return;
numrows = PQntuples(res);
/* XXX reset to use expanded output for sequences (maybe we should
* keep this disabled, just like for tables?) */
myopt.expanded = pset.popt.topt.expanded;
printTableInit(&cont, &myopt, title.data, 7, numrows);
printTableInitialized = true;
if (tableinfo.relpersistence == 'u')
printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
schemaname, relationname);
else
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
schemaname, relationname);
printTableAddHeader(&cont, gettext_noop("Type"), true, 'l');
printTableAddHeader(&cont, gettext_noop("Start"), true, 'r');
printTableAddHeader(&cont, gettext_noop("Minimum"), true, 'r');
printTableAddHeader(&cont, gettext_noop("Maximum"), true, 'r');
printTableAddHeader(&cont, gettext_noop("Increment"), true, 'r');
printTableAddHeader(&cont, gettext_noop("Cycles?"), true, 'l');
printTableAddHeader(&cont, gettext_noop("Cache"), true, 'r');
/* Generate table cells to be printed */
for (i = 0; i < numrows; i++)
{
/* Type */
printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
/* Start */
printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false);
/* Minimum */
printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
/* Maximum */
printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
/* Increment */
printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
/* Cycles? */
printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
/* Cache */
printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
}
/* Footer information about a sequence */
/* Get the column that owns this sequence */ /* Get the column that owns this sequence */
printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||" printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
"\n pg_catalog.quote_ident(relname) || '.' ||" "\n pg_catalog.quote_ident(relname) || '.' ||"
@ -1754,63 +1719,33 @@ describeOneTableDetails(const char *schemaname,
switch (PQgetvalue(result, 0, 1)[0]) switch (PQgetvalue(result, 0, 1)[0])
{ {
case 'a': case 'a':
printTableAddFooter(&cont, footers[0] = psprintf(_("Owned by: %s"),
psprintf(_("Owned by: %s"), PQgetvalue(result, 0, 0));
PQgetvalue(result, 0, 0)));
break; break;
case 'i': case 'i':
printTableAddFooter(&cont, footers[0] = psprintf(_("Sequence for identity column: %s"),
psprintf(_("Sequence for identity column: %s"), PQgetvalue(result, 0, 0));
PQgetvalue(result, 0, 0)));
break; break;
} }
} }
PQclear(result); PQclear(result);
/* print any publications */ if (tableinfo.relpersistence == 'u')
if (pset.sversion >= 150000) printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
{ schemaname, relationname);
int tuples = 0; else
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
schemaname, relationname);
printfPQExpBuffer(&buf, myopt.footers = footers;
"SELECT pubname\n" myopt.topt.default_footer = false;
"FROM pg_catalog.pg_publication p\n" myopt.title = title.data;
" JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n" myopt.translate_header = true;
" JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
"WHERE pc.oid ='%s' and pn.pntype = 's' and pg_catalog.pg_relation_is_publishable('%s')\n"
"UNION\n"
"SELECT pubname\n"
"FROM pg_catalog.pg_publication p\n"
" JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
"WHERE pr.prrelid = '%s'\n"
"UNION\n"
"SELECT pubname\n"
"FROM pg_catalog.pg_publication p\n"
"WHERE p.puballsequences AND pg_catalog.pg_relation_is_publishable('%s')\n"
"ORDER BY 1;",
oid, oid, oid, oid);
result = PSQLexec(buf.data); printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
if (tuples > 0) if (footers[0])
printTableAddFooter(&cont, _("Publications:")); free(footers[0]);
/* Might be an empty set - that's ok */
for (i = 0; i < tuples; i++)
{
printfPQExpBuffer(&buf, " \"%s\"",
PQgetvalue(result, i, 0));
printTableAddFooter(&cont, buf.data);
}
PQclear(result);
}
printTable(&cont, pset.queryFout, false, pset.logfile);
retval = true; retval = true;
goto error_return; /* not an error, just return early */ goto error_return; /* not an error, just return early */
@ -2037,11 +1972,6 @@ describeOneTableDetails(const char *schemaname,
for (i = 0; i < cols; i++) for (i = 0; i < cols; i++)
printTableAddHeader(&cont, headers[i], true, 'l'); printTableAddHeader(&cont, headers[i], true, 'l');
res = PSQLexec(buf.data);
if (!res)
goto error_return;
numrows = PQntuples(res);
/* Generate table cells to be printed */ /* Generate table cells to be printed */
for (i = 0; i < numrows; i++) for (i = 0; i < numrows; i++)
{ {
@ -2968,7 +2898,7 @@ describeOneTableDetails(const char *schemaname,
"FROM pg_catalog.pg_publication p\n" "FROM pg_catalog.pg_publication p\n"
" JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n" " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
" JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n" " JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
"WHERE pc.oid ='%s' and pn.pntype = 't' and pg_catalog.pg_relation_is_publishable('%s')\n" "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
"UNION\n" "UNION\n"
"SELECT pubname\n" "SELECT pubname\n"
" , pg_get_expr(pr.prqual, c.oid)\n" " , pg_get_expr(pr.prqual, c.oid)\n"
@ -4872,7 +4802,7 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
int i; int i;
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
"SELECT pubname, (CASE WHEN pntype = 't' THEN 'tables' ELSE 'sequences' END) AS pubtype\n" "SELECT pubname \n"
"FROM pg_catalog.pg_publication p\n" "FROM pg_catalog.pg_publication p\n"
" JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n" " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
" JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n" " JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n"
@ -4901,9 +4831,8 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
/* Might be an empty set - that's ok */ /* Might be an empty set - that's ok */
for (i = 0; i < pub_schema_tuples; i++) for (i = 0; i < pub_schema_tuples; i++)
{ {
printfPQExpBuffer(&buf, " \"%s\" (%s)", printfPQExpBuffer(&buf, " \"%s\"",
PQgetvalue(result, i, 0), PQgetvalue(result, i, 0));
PQgetvalue(result, i, 1));
footers[i + 1] = pg_strdup(buf.data); footers[i + 1] = pg_strdup(buf.data);
} }
@ -5908,7 +5837,7 @@ listPublications(const char *pattern)
PQExpBufferData buf; PQExpBufferData buf;
PGresult *res; PGresult *res;
printQueryOpt myopt = pset.popt; printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false}; static const bool translate_columns[] = {false, false, false, false, false, false, false, false};
if (pset.sversion < 100000) if (pset.sversion < 100000)
{ {
@ -5922,45 +5851,23 @@ listPublications(const char *pattern)
initPQExpBuffer(&buf); initPQExpBuffer(&buf);
if (pset.sversion >= 150000) printfPQExpBuffer(&buf,
printfPQExpBuffer(&buf, "SELECT pubname AS \"%s\",\n"
"SELECT pubname AS \"%s\",\n" " pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
" pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n" " puballtables AS \"%s\",\n"
" puballtables AS \"%s\",\n" " pubinsert AS \"%s\",\n"
" puballsequences AS \"%s\",\n" " pubupdate AS \"%s\",\n"
" pubinsert AS \"%s\",\n" " pubdelete AS \"%s\"",
" pubupdate AS \"%s\",\n" gettext_noop("Name"),
" pubdelete AS \"%s\"", gettext_noop("Owner"),
gettext_noop("Name"), gettext_noop("All tables"),
gettext_noop("Owner"), gettext_noop("Inserts"),
gettext_noop("All tables"), gettext_noop("Updates"),
gettext_noop("All sequences"), gettext_noop("Deletes"));
gettext_noop("Inserts"),
gettext_noop("Updates"),
gettext_noop("Deletes"));
else
printfPQExpBuffer(&buf,
"SELECT pubname AS \"%s\",\n"
" pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
" puballtables AS \"%s\",\n"
" pubinsert AS \"%s\",\n"
" pubupdate AS \"%s\",\n"
" pubdelete AS \"%s\"",
gettext_noop("Name"),
gettext_noop("Owner"),
gettext_noop("All tables"),
gettext_noop("Inserts"),
gettext_noop("Updates"),
gettext_noop("Deletes"));
if (pset.sversion >= 110000) if (pset.sversion >= 110000)
appendPQExpBuffer(&buf, appendPQExpBuffer(&buf,
",\n pubtruncate AS \"%s\"", ",\n pubtruncate AS \"%s\"",
gettext_noop("Truncates")); gettext_noop("Truncates"));
if (pset.sversion >= 150000)
appendPQExpBuffer(&buf,
",\n pubsequence AS \"%s\"",
gettext_noop("Sequences"));
if (pset.sversion >= 130000) if (pset.sversion >= 130000)
appendPQExpBuffer(&buf, appendPQExpBuffer(&buf,
",\n pubviaroot AS \"%s\"", ",\n pubviaroot AS \"%s\"",
@ -6050,7 +5957,6 @@ describePublications(const char *pattern)
PGresult *res; PGresult *res;
bool has_pubtruncate; bool has_pubtruncate;
bool has_pubviaroot; bool has_pubviaroot;
bool has_pubsequence;
PQExpBufferData title; PQExpBufferData title;
printTableContent cont; printTableContent cont;
@ -6067,7 +5973,6 @@ describePublications(const char *pattern)
has_pubtruncate = (pset.sversion >= 110000); has_pubtruncate = (pset.sversion >= 110000);
has_pubviaroot = (pset.sversion >= 130000); has_pubviaroot = (pset.sversion >= 130000);
has_pubsequence = (pset.sversion >= 150000);
initPQExpBuffer(&buf); initPQExpBuffer(&buf);
@ -6075,17 +5980,12 @@ describePublications(const char *pattern)
"SELECT oid, pubname,\n" "SELECT oid, pubname,\n"
" pg_catalog.pg_get_userbyid(pubowner) AS owner,\n" " pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
" puballtables, pubinsert, pubupdate, pubdelete"); " puballtables, pubinsert, pubupdate, pubdelete");
if (has_pubtruncate) if (has_pubtruncate)
appendPQExpBufferStr(&buf, appendPQExpBufferStr(&buf,
", pubtruncate"); ", pubtruncate");
if (has_pubviaroot) if (has_pubviaroot)
appendPQExpBufferStr(&buf, appendPQExpBufferStr(&buf,
", pubviaroot"); ", pubviaroot");
if (has_pubsequence)
appendPQExpBufferStr(&buf,
", puballsequences, pubsequence");
appendPQExpBufferStr(&buf, appendPQExpBufferStr(&buf,
"\nFROM pg_catalog.pg_publication\n"); "\nFROM pg_catalog.pg_publication\n");
@ -6126,7 +6026,6 @@ describePublications(const char *pattern)
char *pubid = PQgetvalue(res, i, 0); char *pubid = PQgetvalue(res, i, 0);
char *pubname = PQgetvalue(res, i, 1); char *pubname = PQgetvalue(res, i, 1);
bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0; bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
bool puballsequences = strcmp(PQgetvalue(res, i, 9), "t") == 0;
printTableOpt myopt = pset.popt.topt; printTableOpt myopt = pset.popt.topt;
if (has_pubtruncate) if (has_pubtruncate)
@ -6134,43 +6033,29 @@ describePublications(const char *pattern)
if (has_pubviaroot) if (has_pubviaroot)
ncols++; ncols++;
/* sequences have two extra columns (puballsequences, pubsequences) */
if (has_pubsequence)
ncols += 2;
initPQExpBuffer(&title); initPQExpBuffer(&title);
printfPQExpBuffer(&title, _("Publication %s"), pubname); printfPQExpBuffer(&title, _("Publication %s"), pubname);
printTableInit(&cont, &myopt, title.data, ncols, nrows); printTableInit(&cont, &myopt, title.data, ncols, nrows);
printTableAddHeader(&cont, gettext_noop("Owner"), true, align); printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
printTableAddHeader(&cont, gettext_noop("All tables"), true, align); printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
if (has_pubsequence)
printTableAddHeader(&cont, gettext_noop("All sequences"), true, align);
printTableAddHeader(&cont, gettext_noop("Inserts"), true, align); printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
printTableAddHeader(&cont, gettext_noop("Updates"), true, align); printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
printTableAddHeader(&cont, gettext_noop("Deletes"), true, align); printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
if (has_pubtruncate) if (has_pubtruncate)
printTableAddHeader(&cont, gettext_noop("Truncates"), true, align); printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
if (has_pubsequence)
printTableAddHeader(&cont, gettext_noop("Sequences"), true, align);
if (has_pubviaroot) if (has_pubviaroot)
printTableAddHeader(&cont, gettext_noop("Via root"), true, align); printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false); /* owner */ printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false); /* all tables */ printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
if (has_pubsequence) printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false); /* all sequences */ printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false); /* insert */
printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false); /* update */
printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false); /* delete */
if (has_pubtruncate) if (has_pubtruncate)
printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false); /* truncate */ printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
if (has_pubsequence)
printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false); /* sequence */
if (has_pubviaroot) if (has_pubviaroot)
printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false); /* via root */ printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
if (!puballtables) if (!puballtables)
{ {
@ -6201,7 +6086,6 @@ describePublications(const char *pattern)
"WHERE c.relnamespace = n.oid\n" "WHERE c.relnamespace = n.oid\n"
" AND c.oid = pr.prrelid\n" " AND c.oid = pr.prrelid\n"
" AND pr.prpubid = '%s'\n" " AND pr.prpubid = '%s'\n"
" AND c.relkind != 'S'\n" /* exclude sequences */
"ORDER BY 1,2", pubid); "ORDER BY 1,2", pubid);
if (!addFooterToPublicationDesc(&buf, "Tables:", false, &cont)) if (!addFooterToPublicationDesc(&buf, "Tables:", false, &cont))
goto error_return; goto error_return;
@ -6213,7 +6097,7 @@ describePublications(const char *pattern)
"SELECT n.nspname\n" "SELECT n.nspname\n"
"FROM pg_catalog.pg_namespace n\n" "FROM pg_catalog.pg_namespace n\n"
" JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n" " JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
"WHERE pn.pnpubid = '%s' AND pn.pntype = 't'\n" "WHERE pn.pnpubid = '%s'\n"
"ORDER BY 1", pubid); "ORDER BY 1", pubid);
if (!addFooterToPublicationDesc(&buf, "Tables from schemas:", if (!addFooterToPublicationDesc(&buf, "Tables from schemas:",
true, &cont)) true, &cont))
@ -6221,37 +6105,6 @@ describePublications(const char *pattern)
} }
} }
if (!puballsequences)
{
/* Get the sequences for the specified publication */
printfPQExpBuffer(&buf,
"SELECT n.nspname, c.relname, NULL, NULL\n"
"FROM pg_catalog.pg_class c,\n"
" pg_catalog.pg_namespace n,\n"
" pg_catalog.pg_publication_rel pr\n"
"WHERE c.relnamespace = n.oid\n"
" AND c.oid = pr.prrelid\n"
" AND pr.prpubid = '%s'\n"
" AND c.relkind = 'S'\n" /* only sequences */
"ORDER BY 1,2", pubid);
if (!addFooterToPublicationDesc(&buf, "Sequences:", false, &cont))
goto error_return;
if (pset.sversion >= 150000)
{
/* Get the schemas for the specified publication */
printfPQExpBuffer(&buf,
"SELECT n.nspname\n"
"FROM pg_catalog.pg_namespace n\n"
" JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
"WHERE pn.pnpubid = '%s' AND pn.pntype = 's'\n"
"ORDER BY 1", pubid);
if (!addFooterToPublicationDesc(&buf, "Sequences from schemas:",
true, &cont))
goto error_return;
}
}
printTable(&cont, pset.queryFout, false, pset.logfile); printTable(&cont, pset.queryFout, false, pset.logfile);
printTableCleanup(&cont); printTableCleanup(&cont);

View File

@ -1827,15 +1827,11 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "SET"); COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "SET");
/* ALTER PUBLICATION <name> ADD */ /* ALTER PUBLICATION <name> ADD */
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD")) else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD"))
COMPLETE_WITH("ALL TABLES IN SCHEMA", "ALL SEQUENCES IN SCHEMA", "TABLE", "SEQUENCE"); COMPLETE_WITH("ALL TABLES IN SCHEMA", "TABLE");
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") || else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") ||
(HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") && (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
ends_with(prev_wd, ','))) ends_with(prev_wd, ',')))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "SEQUENCE") ||
(HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "SEQUENCE") &&
ends_with(prev_wd, ',')))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
/* /*
* "ALTER PUBLICATION <name> SET TABLE <name> WHERE (" - complete with * "ALTER PUBLICATION <name> SET TABLE <name> WHERE (" - complete with
* table attributes * table attributes
@ -1854,11 +1850,11 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH(","); COMPLETE_WITH(",");
/* ALTER PUBLICATION <name> DROP */ /* ALTER PUBLICATION <name> DROP */
else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP")) else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP"))
COMPLETE_WITH("ALL TABLES IN SCHEMA", "ALL SEQUENCES IN SCHEMA", "TABLE", "SEQUENCE"); COMPLETE_WITH("ALL TABLES IN SCHEMA", "TABLE");
/* ALTER PUBLICATION <name> SET */ /* ALTER PUBLICATION <name> SET */
else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET")) else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET"))
COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "ALL SEQUENCES IN SCHEMA", "TABLE", "SEQUENCE"); COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "TABLE");
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES|SEQUENCES", "IN", "SCHEMA")) else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES", "IN", "SCHEMA"))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
" AND nspname NOT LIKE E'pg\\\\_%%'", " AND nspname NOT LIKE E'pg\\\\_%%'",
"CURRENT_SCHEMA"); "CURRENT_SCHEMA");
@ -2988,27 +2984,21 @@ psql_completion(const char *text, int start, int end)
/* CREATE PUBLICATION */ /* CREATE PUBLICATION */
else if (Matches("CREATE", "PUBLICATION", MatchAny)) else if (Matches("CREATE", "PUBLICATION", MatchAny))
COMPLETE_WITH("FOR TABLE", "FOR ALL TABLES", "FOR ALL TABLES IN SCHEMA", COMPLETE_WITH("FOR TABLE", "FOR ALL TABLES", "FOR ALL TABLES IN SCHEMA", "WITH (");
"FOR SEQUENCE", "FOR ALL SEQUENCES", "FOR ALL SEQUENCES IN SCHEMA",
"WITH (");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR")) else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR"))
COMPLETE_WITH("TABLE", "ALL TABLES", "ALL TABLES IN SCHEMA", COMPLETE_WITH("TABLE", "ALL TABLES", "ALL TABLES IN SCHEMA");
"SEQUENCE", "ALL SEQUENCES", "ALL SEQUENCES IN SCHEMA");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL")) else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL"))
COMPLETE_WITH("TABLES", "TABLES IN SCHEMA", "SEQUENCES", "SEQUENCES IN SCHEMA"); COMPLETE_WITH("TABLES", "TABLES IN SCHEMA");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES|SEQUENCES")) else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES"))
COMPLETE_WITH("IN SCHEMA", "WITH ("); COMPLETE_WITH("IN SCHEMA", "WITH (");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE|SEQUENCE", MatchAny) && !ends_with(prev_wd, ',')) else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE", MatchAny) && !ends_with(prev_wd, ','))
COMPLETE_WITH("WHERE (", "WITH ("); COMPLETE_WITH("WHERE (", "WITH (");
/* Complete "CREATE PUBLICATION <name> FOR TABLE" with "<table>, ..." */ /* Complete "CREATE PUBLICATION <name> FOR TABLE" with "<table>, ..." */
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE")) else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* Complete "CREATE PUBLICATION <name> FOR SEQUENCE" with "<sequence>, ..." */
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "SEQUENCE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
/* /*
* "CREATE PUBLICATION <name> FOR TABLE|SEQUENCE <name> WHERE (" - complete with * "CREATE PUBLICATION <name> FOR TABLE <name> WHERE (" - complete with
* table attributes * table attributes
*/ */
else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE")) else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE"))
@ -3019,14 +3009,14 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH(" WITH ("); COMPLETE_WITH(" WITH (");
/* /*
* Complete "CREATE PUBLICATION <name> FOR ALL TABLES|SEQUENCES IN SCHEMA <schema>, * Complete "CREATE PUBLICATION <name> FOR ALL TABLES IN SCHEMA <schema>,
* ..." * ..."
*/ */
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES|SEQUENCES", "IN", "SCHEMA")) else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "IN", "SCHEMA"))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
" AND nspname NOT LIKE E'pg\\\\_%%'", " AND nspname NOT LIKE E'pg\\\\_%%'",
"CURRENT_SCHEMA"); "CURRENT_SCHEMA");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES|SEQUENCES", "IN", "SCHEMA", MatchAny) && (!ends_with(prev_wd, ','))) else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "IN", "SCHEMA", MatchAny) && (!ends_with(prev_wd, ',')))
COMPLETE_WITH("WITH ("); COMPLETE_WITH("WITH (");
/* Complete "CREATE PUBLICATION <name> [...] WITH" */ /* Complete "CREATE PUBLICATION <name> [...] WITH" */
else if (HeadMatches("CREATE", "PUBLICATION") && TailMatches("WITH", "(")) else if (HeadMatches("CREATE", "PUBLICATION") && TailMatches("WITH", "("))

View File

@ -40,7 +40,7 @@ PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, btree_xlog
PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL) PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL)
PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL) PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL)
PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL) PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL)
PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, sequence_decode) PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, NULL)
PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL) PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL)
PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL) PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL)
PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL) PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL)

View File

@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 202204074 #define CATALOG_VERSION_NO 202204075
#endif #endif

View File

@ -11658,11 +11658,6 @@
provolatile => 's', prorettype => 'oid', proargtypes => 'text', provolatile => 's', prorettype => 'oid', proargtypes => 'text',
proallargtypes => '{text,oid}', proargmodes => '{i,o}', proallargtypes => '{text,oid}', proargmodes => '{i,o}',
proargnames => '{pubname,relid}', prosrc => 'pg_get_publication_tables' }, proargnames => '{pubname,relid}', prosrc => 'pg_get_publication_tables' },
{ oid => '8000', descr => 'get OIDs of sequences in a publication',
proname => 'pg_get_publication_sequences', prorows => '1000', proretset => 't',
provolatile => 's', prorettype => 'oid', proargtypes => 'text',
proallargtypes => '{text,oid}', proargmodes => '{i,o}',
proargnames => '{pubname,relid}', prosrc => 'pg_get_publication_sequences' },
{ oid => '6121', { oid => '6121',
descr => 'returns whether a relation can be part of a publication', descr => 'returns whether a relation can be part of a publication',
proname => 'pg_relation_is_publishable', provolatile => 's', proname => 'pg_relation_is_publishable', provolatile => 's',

View File

@ -40,12 +40,6 @@ CATALOG(pg_publication,6104,PublicationRelationId)
*/ */
bool puballtables; bool puballtables;
/*
* indicates that this is special publication which should encompass all
* sequences in the database (except for the unlogged and temp ones)
*/
bool puballsequences;
/* true if inserts are published */ /* true if inserts are published */
bool pubinsert; bool pubinsert;
@ -58,9 +52,6 @@ CATALOG(pg_publication,6104,PublicationRelationId)
/* true if truncates are published */ /* true if truncates are published */
bool pubtruncate; bool pubtruncate;
/* true if sequences are published */
bool pubsequence;
/* true if partition changes are published using root schema */ /* true if partition changes are published using root schema */
bool pubviaroot; bool pubviaroot;
} FormData_pg_publication; } FormData_pg_publication;
@ -81,7 +72,6 @@ typedef struct PublicationActions
bool pubupdate; bool pubupdate;
bool pubdelete; bool pubdelete;
bool pubtruncate; bool pubtruncate;
bool pubsequence;
} PublicationActions; } PublicationActions;
typedef struct PublicationDesc typedef struct PublicationDesc
@ -109,7 +99,6 @@ typedef struct Publication
Oid oid; Oid oid;
char *name; char *name;
bool alltables; bool alltables;
bool allsequences;
bool pubviaroot; bool pubviaroot;
PublicationActions pubactions; PublicationActions pubactions;
} Publication; } Publication;
@ -141,16 +130,14 @@ typedef enum PublicationPartOpt
PUBLICATION_PART_ALL, PUBLICATION_PART_ALL,
} PublicationPartOpt; } PublicationPartOpt;
extern List *GetPublicationRelations(Oid pubid, char objectType, extern List *GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt);
PublicationPartOpt pub_partopt);
extern List *GetAllTablesPublications(void); extern List *GetAllTablesPublications(void);
extern List *GetAllTablesPublicationRelations(bool pubviaroot); extern List *GetAllTablesPublicationRelations(bool pubviaroot);
extern void GetActionsInPublication(Oid pubid, PublicationActions *actions); extern List *GetPublicationSchemas(Oid pubid);
extern List *GetPublicationSchemas(Oid pubid, char objectType); extern List *GetSchemaPublications(Oid schemaid);
extern List *GetSchemaPublications(Oid schemaid, char objectType); extern List *GetSchemaPublicationRelations(Oid schemaid,
extern List *GetSchemaPublicationRelations(Oid schemaid, char objectType,
PublicationPartOpt pub_partopt); PublicationPartOpt pub_partopt);
extern List *GetAllSchemaPublicationRelations(Oid puboid, char objectType, extern List *GetAllSchemaPublicationRelations(Oid puboid,
PublicationPartOpt pub_partopt); PublicationPartOpt pub_partopt);
extern List *GetPubPartitionOptionRelations(List *result, extern List *GetPubPartitionOptionRelations(List *result,
PublicationPartOpt pub_partopt, PublicationPartOpt pub_partopt,
@ -158,15 +145,11 @@ extern List *GetPubPartitionOptionRelations(List *result,
extern Oid GetTopMostAncestorInPublication(Oid puboid, List *ancestors, extern Oid GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
int *ancestor_level); int *ancestor_level);
extern List *GetAllSequencesPublications(void);
extern List *GetAllSequencesPublicationRelations(void);
extern bool is_publishable_relation(Relation rel); extern bool is_publishable_relation(Relation rel);
extern bool is_schema_publication(Oid pubid); extern bool is_schema_publication(Oid pubid);
extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri, extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri,
bool if_not_exists); bool if_not_exists);
extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid, extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid,
char objectType,
bool if_not_exists); bool if_not_exists);
extern Bitmapset *pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols, extern Bitmapset *pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols,

View File

@ -32,7 +32,6 @@ CATALOG(pg_publication_namespace,8901,PublicationNamespaceRelationId)
Oid oid; /* oid */ Oid oid; /* oid */
Oid pnpubid BKI_LOOKUP(pg_publication); /* Oid of the publication */ Oid pnpubid BKI_LOOKUP(pg_publication); /* Oid of the publication */
Oid pnnspid BKI_LOOKUP(pg_namespace); /* Oid of the schema */ Oid pnnspid BKI_LOOKUP(pg_namespace); /* Oid of the schema */
char pntype; /* object type to include */
} FormData_pg_publication_namespace; } FormData_pg_publication_namespace;
/* ---------------- /* ----------------
@ -43,13 +42,6 @@ CATALOG(pg_publication_namespace,8901,PublicationNamespaceRelationId)
typedef FormData_pg_publication_namespace *Form_pg_publication_namespace; typedef FormData_pg_publication_namespace *Form_pg_publication_namespace;
DECLARE_UNIQUE_INDEX_PKEY(pg_publication_namespace_oid_index, 8902, PublicationNamespaceObjectIndexId, on pg_publication_namespace using btree(oid oid_ops)); DECLARE_UNIQUE_INDEX_PKEY(pg_publication_namespace_oid_index, 8902, PublicationNamespaceObjectIndexId, on pg_publication_namespace using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_publication_namespace_pnnspid_pnpubid_pntype_index, 8903, PublicationNamespacePnnspidPnpubidPntypeIndexId, on pg_publication_namespace using btree(pnnspid oid_ops, pnpubid oid_ops, pntype char_ops)); DECLARE_UNIQUE_INDEX(pg_publication_namespace_pnnspid_pnpubid_index, 8903, PublicationNamespacePnnspidPnpubidIndexId, on pg_publication_namespace using btree(pnnspid oid_ops, pnpubid oid_ops));
/* object type to include from a schema, maps to relkind */
#define PUB_OBJTYPE_TABLE 't' /* table (regular or partitioned) */
#define PUB_OBJTYPE_SEQUENCE 's' /* sequence object */
#define PUB_OBJTYPE_UNSUPPORTED 'u' /* used for non-replicated types */
extern char pub_get_object_type_for_relkind(char relkind);
#endif /* PG_PUBLICATION_NAMESPACE_H */ #endif /* PG_PUBLICATION_NAMESPACE_H */

View File

@ -48,7 +48,6 @@ typedef FormData_pg_sequence_data *Form_pg_sequence_data;
typedef struct xl_seq_rec typedef struct xl_seq_rec
{ {
RelFileNode node; RelFileNode node;
bool created; /* creates a new relfilenode (CREATE/ALTER) */
/* SEQUENCE TUPLE DATA FOLLOWS AT THE END */ /* SEQUENCE TUPLE DATA FOLLOWS AT THE END */
} xl_seq_rec; } xl_seq_rec;
@ -61,7 +60,6 @@ extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
extern void SequenceChangePersistence(Oid relid, char newrelpersistence); extern void SequenceChangePersistence(Oid relid, char newrelpersistence);
extern void DeleteSequenceTuple(Oid relid); extern void DeleteSequenceTuple(Oid relid);
extern void ResetSequence(Oid seq_relid); extern void ResetSequence(Oid seq_relid);
extern void SetSequence(Oid seq_relid, bool transactional, int64 last_value, int64 log_cnt, bool is_called);
extern void ResetSequenceCaches(void); extern void ResetSequenceCaches(void);
extern void seq_redo(XLogReaderState *rptr); extern void seq_redo(XLogReaderState *rptr);

View File

@ -4013,10 +4013,6 @@ typedef enum PublicationObjSpecType
PUBLICATIONOBJ_TABLES_IN_SCHEMA, /* All tables in schema */ PUBLICATIONOBJ_TABLES_IN_SCHEMA, /* All tables in schema */
PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA, /* All tables in first element of PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA, /* All tables in first element of
* search_path */ * search_path */
PUBLICATIONOBJ_SEQUENCE, /* Sequence type */
PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA, /* Sequences in schema type */
PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA, /* Get the first element of
* search_path */
PUBLICATIONOBJ_CONTINUATION /* Continuation of previous type */ PUBLICATIONOBJ_CONTINUATION /* Continuation of previous type */
} PublicationObjSpecType; } PublicationObjSpecType;
@ -4035,7 +4031,7 @@ typedef struct CreatePublicationStmt
char *pubname; /* Name of the publication */ char *pubname; /* Name of the publication */
List *options; /* List of DefElem nodes */ List *options; /* List of DefElem nodes */
List *pubobjects; /* Optional list of publication objects */ List *pubobjects; /* Optional list of publication objects */
List *for_all_objects; /* Special publication for all objects in db */ bool for_all_tables; /* Special publication for all tables in db */
} CreatePublicationStmt; } CreatePublicationStmt;
typedef enum AlterPublicationAction typedef enum AlterPublicationAction
@ -4058,7 +4054,7 @@ typedef struct AlterPublicationStmt
* objects. * objects.
*/ */
List *pubobjects; /* Optional list of publication objects */ List *pubobjects; /* Optional list of publication objects */
List *for_all_objects; /* Special publication for all objects in db */ bool for_all_tables; /* Special publication for all tables in db */
AlterPublicationAction action; /* What action to perform with the given AlterPublicationAction action; /* What action to perform with the given
* objects */ * objects */
} AlterPublicationStmt; } AlterPublicationStmt;

View File

@ -27,7 +27,6 @@ extern void heap2_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
extern void xact_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf); extern void xact_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
extern void standby_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf); extern void standby_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
extern void logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf); extern void logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
extern void sequence_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
extern void LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, extern void LogicalDecodingProcessRecord(LogicalDecodingContext *ctx,
XLogReaderState *record); XLogReaderState *record);

View File

@ -61,7 +61,6 @@ typedef enum LogicalRepMsgType
LOGICAL_REP_MSG_RELATION = 'R', LOGICAL_REP_MSG_RELATION = 'R',
LOGICAL_REP_MSG_TYPE = 'Y', LOGICAL_REP_MSG_TYPE = 'Y',
LOGICAL_REP_MSG_MESSAGE = 'M', LOGICAL_REP_MSG_MESSAGE = 'M',
LOGICAL_REP_MSG_SEQUENCE = 'Q',
LOGICAL_REP_MSG_BEGIN_PREPARE = 'b', LOGICAL_REP_MSG_BEGIN_PREPARE = 'b',
LOGICAL_REP_MSG_PREPARE = 'P', LOGICAL_REP_MSG_PREPARE = 'P',
LOGICAL_REP_MSG_COMMIT_PREPARED = 'K', LOGICAL_REP_MSG_COMMIT_PREPARED = 'K',
@ -119,18 +118,6 @@ typedef struct LogicalRepTyp
char *typname; /* name of the remote type */ char *typname; /* name of the remote type */
} LogicalRepTyp; } LogicalRepTyp;
/* Sequence info */
typedef struct LogicalRepSequence
{
Oid remoteid; /* unique id of the remote sequence */
char *nspname; /* schema name of remote sequence */
char *seqname; /* name of the remote sequence */
bool transactional;
int64 last_value;
int64 log_cnt;
bool is_called;
} LogicalRepSequence;
/* Transaction info */ /* Transaction info */
typedef struct LogicalRepBeginData typedef struct LogicalRepBeginData
{ {
@ -243,12 +230,6 @@ extern List *logicalrep_read_truncate(StringInfo in,
bool *cascade, bool *restart_seqs); bool *cascade, bool *restart_seqs);
extern void logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn, extern void logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
bool transactional, const char *prefix, Size sz, const char *message); bool transactional, const char *prefix, Size sz, const char *message);
extern void logicalrep_write_sequence(StringInfo out, Relation rel,
TransactionId xid, XLogRecPtr lsn,
bool transactional,
int64 last_value, int64 log_cnt,
bool is_called);
extern void logicalrep_read_sequence(StringInfo in, LogicalRepSequence *seqdata);
extern void logicalrep_write_rel(StringInfo out, TransactionId xid, extern void logicalrep_write_rel(StringInfo out, TransactionId xid,
Relation rel, Bitmapset *columns); Relation rel, Bitmapset *columns);
extern LogicalRepRelation *logicalrep_read_rel(StringInfo in); extern LogicalRepRelation *logicalrep_read_rel(StringInfo in);

View File

@ -88,18 +88,6 @@ typedef void (*LogicalDecodeMessageCB) (struct LogicalDecodingContext *ctx,
Size message_size, Size message_size,
const char *message); const char *message);
/*
* Called for the generic logical decoding sequences.
*/
typedef void (*LogicalDecodeSequenceCB) (struct LogicalDecodingContext *ctx,
ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn,
Relation rel,
bool transactional,
int64 last_value,
int64 log_cnt,
bool is_called);
/* /*
* Filter changes by origin. * Filter changes by origin.
*/ */
@ -211,19 +199,6 @@ typedef void (*LogicalDecodeStreamMessageCB) (struct LogicalDecodingContext *ctx
Size message_size, Size message_size,
const char *message); const char *message);
/*
* Called for the streaming generic logical decoding sequences from in-progress
* transactions.
*/
typedef void (*LogicalDecodeStreamSequenceCB) (struct LogicalDecodingContext *ctx,
ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn,
Relation rel,
bool transactional,
int64 last_value,
int64 log_cnt,
bool is_called);
/* /*
* Callback for streaming truncates from in-progress transactions. * Callback for streaming truncates from in-progress transactions.
*/ */
@ -244,7 +219,6 @@ typedef struct OutputPluginCallbacks
LogicalDecodeTruncateCB truncate_cb; LogicalDecodeTruncateCB truncate_cb;
LogicalDecodeCommitCB commit_cb; LogicalDecodeCommitCB commit_cb;
LogicalDecodeMessageCB message_cb; LogicalDecodeMessageCB message_cb;
LogicalDecodeSequenceCB sequence_cb;
LogicalDecodeFilterByOriginCB filter_by_origin_cb; LogicalDecodeFilterByOriginCB filter_by_origin_cb;
LogicalDecodeShutdownCB shutdown_cb; LogicalDecodeShutdownCB shutdown_cb;
@ -263,7 +237,6 @@ typedef struct OutputPluginCallbacks
LogicalDecodeStreamCommitCB stream_commit_cb; LogicalDecodeStreamCommitCB stream_commit_cb;
LogicalDecodeStreamChangeCB stream_change_cb; LogicalDecodeStreamChangeCB stream_change_cb;
LogicalDecodeStreamMessageCB stream_message_cb; LogicalDecodeStreamMessageCB stream_message_cb;
LogicalDecodeStreamSequenceCB stream_sequence_cb;
LogicalDecodeStreamTruncateCB stream_truncate_cb; LogicalDecodeStreamTruncateCB stream_truncate_cb;
} OutputPluginCallbacks; } OutputPluginCallbacks;

View File

@ -29,7 +29,6 @@ typedef struct PGOutputData
bool streaming; bool streaming;
bool messages; bool messages;
bool two_phase; bool two_phase;
bool sequences;
} PGOutputData; } PGOutputData;
#endif /* PGOUTPUT_H */ #endif /* PGOUTPUT_H */

View File

@ -64,8 +64,7 @@ typedef enum ReorderBufferChangeType
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT,
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM,
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT,
REORDER_BUFFER_CHANGE_TRUNCATE, REORDER_BUFFER_CHANGE_TRUNCATE
REORDER_BUFFER_CHANGE_SEQUENCE
} ReorderBufferChangeType; } ReorderBufferChangeType;
/* forward declaration */ /* forward declaration */
@ -159,13 +158,6 @@ typedef struct ReorderBufferChange
uint32 ninvalidations; /* Number of messages */ uint32 ninvalidations; /* Number of messages */
SharedInvalidationMessage *invalidations; /* invalidation message */ SharedInvalidationMessage *invalidations; /* invalidation message */
} inval; } inval;
/* Context data for Sequence changes */
struct
{
RelFileNode relnode;
ReorderBufferTupleBuf *tuple;
} sequence;
} data; } data;
/* /*
@ -438,15 +430,6 @@ typedef void (*ReorderBufferMessageCB) (ReorderBuffer *rb,
const char *prefix, Size sz, const char *prefix, Size sz,
const char *message); const char *message);
/* sequence callback signature */
typedef void (*ReorderBufferSequenceCB) (ReorderBuffer *rb,
ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn,
Relation rel,
bool transactional,
int64 last_value, int64 log_cnt,
bool is_called);
/* begin prepare callback signature */ /* begin prepare callback signature */
typedef void (*ReorderBufferBeginPrepareCB) (ReorderBuffer *rb, typedef void (*ReorderBufferBeginPrepareCB) (ReorderBuffer *rb,
ReorderBufferTXN *txn); ReorderBufferTXN *txn);
@ -513,15 +496,6 @@ typedef void (*ReorderBufferStreamMessageCB) (
const char *prefix, Size sz, const char *prefix, Size sz,
const char *message); const char *message);
/* stream sequence callback signature */
typedef void (*ReorderBufferStreamSequenceCB) (ReorderBuffer *rb,
ReorderBufferTXN *txn,
XLogRecPtr sequence_lsn,
Relation rel,
bool transactional,
int64 last_value, int64 log_cnt,
bool is_called);
/* stream truncate callback signature */ /* stream truncate callback signature */
typedef void (*ReorderBufferStreamTruncateCB) ( typedef void (*ReorderBufferStreamTruncateCB) (
ReorderBuffer *rb, ReorderBuffer *rb,
@ -537,12 +511,6 @@ struct ReorderBuffer
*/ */
HTAB *by_txn; HTAB *by_txn;
/*
* relfilenode => XID lookup table for sequences created in a transaction
* (also includes altered sequences, which assigns new relfilenode)
*/
HTAB *sequences;
/* /*
* Transactions that could be a toplevel xact, ordered by LSN of the first * Transactions that could be a toplevel xact, ordered by LSN of the first
* record bearing that xid. * record bearing that xid.
@ -573,7 +541,6 @@ struct ReorderBuffer
ReorderBufferApplyTruncateCB apply_truncate; ReorderBufferApplyTruncateCB apply_truncate;
ReorderBufferCommitCB commit; ReorderBufferCommitCB commit;
ReorderBufferMessageCB message; ReorderBufferMessageCB message;
ReorderBufferSequenceCB sequence;
/* /*
* Callbacks to be called when streaming a transaction at prepare time. * Callbacks to be called when streaming a transaction at prepare time.
@ -593,7 +560,6 @@ struct ReorderBuffer
ReorderBufferStreamCommitCB stream_commit; ReorderBufferStreamCommitCB stream_commit;
ReorderBufferStreamChangeCB stream_change; ReorderBufferStreamChangeCB stream_change;
ReorderBufferStreamMessageCB stream_message; ReorderBufferStreamMessageCB stream_message;
ReorderBufferStreamSequenceCB stream_sequence;
ReorderBufferStreamTruncateCB stream_truncate; ReorderBufferStreamTruncateCB stream_truncate;
/* /*
@ -669,10 +635,6 @@ void ReorderBufferQueueChange(ReorderBuffer *, TransactionId,
void ReorderBufferQueueMessage(ReorderBuffer *, TransactionId, Snapshot snapshot, XLogRecPtr lsn, void ReorderBufferQueueMessage(ReorderBuffer *, TransactionId, Snapshot snapshot, XLogRecPtr lsn,
bool transactional, const char *prefix, bool transactional, const char *prefix,
Size message_size, const char *message); Size message_size, const char *message);
void ReorderBufferQueueSequence(ReorderBuffer *rb, TransactionId xid,
Snapshot snapshot, XLogRecPtr lsn, RepOriginId origin_id,
RelFileNode rnode, bool transactional, bool created,
ReorderBufferTupleBuf *tuplebuf);
void ReorderBufferCommit(ReorderBuffer *, TransactionId, void ReorderBufferCommit(ReorderBuffer *, TransactionId,
XLogRecPtr commit_lsn, XLogRecPtr end_lsn, XLogRecPtr commit_lsn, XLogRecPtr end_lsn,
TimestampTz commit_time, RepOriginId origin_id, XLogRecPtr origin_lsn); TimestampTz commit_time, RepOriginId origin_id, XLogRecPtr origin_lsn);
@ -720,7 +682,4 @@ void ReorderBufferSetRestartPoint(ReorderBuffer *, XLogRecPtr ptr);
void StartupReorderBuffer(void); void StartupReorderBuffer(void);
bool ReorderBufferSequenceIsTransactional(ReorderBuffer *rb,
RelFileNode rnode, bool created);
#endif #endif

View File

@ -46,7 +46,6 @@ CREATE TRANSFORM FOR int LANGUAGE SQL (
SET client_min_messages = 'ERROR'; SET client_min_messages = 'ERROR';
CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable;
CREATE PUBLICATION addr_pub_schema FOR ALL TABLES IN SCHEMA addr_nsp; CREATE PUBLICATION addr_pub_schema FOR ALL TABLES IN SCHEMA addr_nsp;
CREATE PUBLICATION addr_pub_schema2 FOR ALL SEQUENCES IN SCHEMA addr_nsp;
RESET client_min_messages; RESET client_min_messages;
CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE); 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 WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables
@ -429,8 +428,7 @@ WITH objects (type, name, args) AS (VALUES
('transform', '{int}', '{sql}'), ('transform', '{int}', '{sql}'),
('access method', '{btree}', '{}'), ('access method', '{btree}', '{}'),
('publication', '{addr_pub}', '{}'), ('publication', '{addr_pub}', '{}'),
('publication namespace', '{addr_nsp}', '{addr_pub_schema,t}'), ('publication namespace', '{addr_nsp}', '{addr_pub_schema}'),
('publication namespace', '{addr_nsp}', '{addr_pub_schema2,s}'),
('publication relation', '{addr_nsp, gentable}', '{addr_pub}'), ('publication relation', '{addr_nsp, gentable}', '{addr_pub}'),
('subscription', '{regress_addr_sub}', '{}'), ('subscription', '{regress_addr_sub}', '{}'),
('statistics object', '{addr_nsp, gentable_stat}', '{}') ('statistics object', '{addr_nsp, gentable_stat}', '{}')
@ -494,9 +492,8 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*,
subscription | | regress_addr_sub | regress_addr_sub | t subscription | | regress_addr_sub | regress_addr_sub | t
publication | | addr_pub | addr_pub | t publication | | addr_pub | addr_pub | t
publication relation | | | addr_nsp.gentable in publication addr_pub | t publication relation | | | addr_nsp.gentable in publication addr_pub | t
publication namespace | | | addr_nsp in publication addr_pub_schema type t | t publication namespace | | | addr_nsp in publication addr_pub_schema | t
publication namespace | | | addr_nsp in publication addr_pub_schema2 type s | t (50 rows)
(51 rows)
--- ---
--- Cleanup resources --- Cleanup resources
@ -509,7 +506,6 @@ drop cascades to server integer
drop cascades to user mapping for regress_addr_user on server integer drop cascades to user mapping for regress_addr_user on server integer
DROP PUBLICATION addr_pub; DROP PUBLICATION addr_pub;
DROP PUBLICATION addr_pub_schema; DROP PUBLICATION addr_pub_schema;
DROP PUBLICATION addr_pub_schema2;
DROP SUBSCRIPTION regress_addr_sub; DROP SUBSCRIPTION regress_addr_sub;
DROP SCHEMA addr_nsp CASCADE; DROP SCHEMA addr_nsp CASCADE;
NOTICE: drop cascades to 14 other objects NOTICE: drop cascades to 14 other objects

File diff suppressed because it is too large Load Diff

View File

@ -1435,14 +1435,6 @@ pg_prepared_xacts| SELECT p.transaction,
FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid) FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid)
LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid)))
LEFT JOIN pg_database d ON ((p.dbid = d.oid))); LEFT JOIN pg_database d ON ((p.dbid = d.oid)));
pg_publication_sequences| SELECT p.pubname,
n.nspname AS schemaname,
c.relname AS sequencename
FROM pg_publication p,
LATERAL pg_get_publication_sequences((p.pubname)::text) gps(relid),
(pg_class c
JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE (c.oid = gps.relid);
pg_publication_tables| SELECT p.pubname, pg_publication_tables| SELECT p.pubname,
n.nspname AS schemaname, n.nspname AS schemaname,
c.relname AS tablename c.relname AS tablename

View File

@ -49,7 +49,6 @@ CREATE TRANSFORM FOR int LANGUAGE SQL (
SET client_min_messages = 'ERROR'; SET client_min_messages = 'ERROR';
CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable;
CREATE PUBLICATION addr_pub_schema FOR ALL TABLES IN SCHEMA addr_nsp; CREATE PUBLICATION addr_pub_schema FOR ALL TABLES IN SCHEMA addr_nsp;
CREATE PUBLICATION addr_pub_schema2 FOR ALL SEQUENCES IN SCHEMA addr_nsp;
RESET client_min_messages; RESET client_min_messages;
CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE); 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; CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable;
@ -199,8 +198,7 @@ WITH objects (type, name, args) AS (VALUES
('transform', '{int}', '{sql}'), ('transform', '{int}', '{sql}'),
('access method', '{btree}', '{}'), ('access method', '{btree}', '{}'),
('publication', '{addr_pub}', '{}'), ('publication', '{addr_pub}', '{}'),
('publication namespace', '{addr_nsp}', '{addr_pub_schema,t}'), ('publication namespace', '{addr_nsp}', '{addr_pub_schema}'),
('publication namespace', '{addr_nsp}', '{addr_pub_schema2,s}'),
('publication relation', '{addr_nsp, gentable}', '{addr_pub}'), ('publication relation', '{addr_nsp, gentable}', '{addr_pub}'),
('subscription', '{regress_addr_sub}', '{}'), ('subscription', '{regress_addr_sub}', '{}'),
('statistics object', '{addr_nsp, gentable_stat}', '{}') ('statistics object', '{addr_nsp, gentable_stat}', '{}')
@ -220,7 +218,6 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*,
DROP FOREIGN DATA WRAPPER addr_fdw CASCADE; DROP FOREIGN DATA WRAPPER addr_fdw CASCADE;
DROP PUBLICATION addr_pub; DROP PUBLICATION addr_pub;
DROP PUBLICATION addr_pub_schema; DROP PUBLICATION addr_pub_schema;
DROP PUBLICATION addr_pub_schema2;
DROP SUBSCRIPTION regress_addr_sub; DROP SUBSCRIPTION regress_addr_sub;
DROP SCHEMA addr_nsp CASCADE; DROP SCHEMA addr_nsp CASCADE;

View File

@ -27,7 +27,7 @@ CREATE PUBLICATION testpub_xxx WITH (publish_via_partition_root = 'true', publis
\dRp \dRp
ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete, sequence'); ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete');
\dRp \dRp
@ -46,8 +46,6 @@ ALTER PUBLICATION testpub_foralltables SET (publish = 'insert, update');
CREATE TABLE testpub_tbl2 (id serial primary key, data text); CREATE TABLE testpub_tbl2 (id serial primary key, data text);
-- fail - can't add to for all tables publication -- fail - can't add to for all tables publication
ALTER PUBLICATION testpub_foralltables ADD TABLE testpub_tbl2; ALTER PUBLICATION testpub_foralltables ADD TABLE testpub_tbl2;
-- fail - can't add a table using ADD SEQUENCE command
ALTER PUBLICATION testpub_foralltables ADD SEQUENCE testpub_tbl2;
-- fail - can't drop from all tables publication -- fail - can't drop from all tables publication
ALTER PUBLICATION testpub_foralltables DROP TABLE testpub_tbl2; ALTER PUBLICATION testpub_foralltables DROP TABLE testpub_tbl2;
-- fail - can't add to for all tables publication -- fail - can't add to for all tables publication
@ -106,199 +104,6 @@ RESET client_min_messages;
DROP TABLE testpub_tbl3, testpub_tbl3a; DROP TABLE testpub_tbl3, testpub_tbl3a;
DROP PUBLICATION testpub3, testpub4; DROP PUBLICATION testpub3, testpub4;
--- adding sequences
CREATE SEQUENCE testpub_seq0;
CREATE SEQUENCE pub_test.testpub_seq1;
SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_forallsequences FOR ALL SEQUENCES WITH (publish = 'sequence');
RESET client_min_messages;
ALTER PUBLICATION testpub_forallsequences SET (publish = 'insert, sequence');
CREATE SEQUENCE testpub_seq2;
-- fail - can't add to for all sequences publication
ALTER PUBLICATION testpub_forallsequences ADD SEQUENCE testpub_seq2;
-- fail - can't drop from all sequences publication
ALTER PUBLICATION testpub_forallsequences DROP SEQUENCE testpub_seq2;
-- fail - can't add to for all sequences publication
ALTER PUBLICATION testpub_forallsequences SET SEQUENCE pub_test.testpub_seq1;
-- fail - can't add schema to 'FOR ALL SEQUENCES' publication
ALTER PUBLICATION testpub_forallsequences ADD ALL SEQUENCES IN SCHEMA pub_test;
-- fail - can't drop schema from 'FOR ALL SEQUENCES' publication
ALTER PUBLICATION testpub_forallsequences DROP ALL SEQUENCES IN SCHEMA pub_test;
-- fail - can't set schema to 'FOR ALL SEQUENCES' publication
ALTER PUBLICATION testpub_forallsequences SET ALL SEQUENCES IN SCHEMA pub_test;
SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_forsequence FOR SEQUENCE testpub_seq0;
RESET client_min_messages;
-- should be able to add schema to 'FOR SEQUENCE' publication
ALTER PUBLICATION testpub_forsequence ADD ALL SEQUENCES IN SCHEMA pub_test;
\dRp+ testpub_forsequence
-- fail - can't add sequence from the schema we already added
ALTER PUBLICATION testpub_forsequence ADD SEQUENCE pub_test.testpub_seq1;
-- fail - can't add sequence using ADD TABLE command
ALTER PUBLICATION testpub_forsequence ADD TABLE pub_test.testpub_seq1;
-- should be able to drop schema from 'FOR SEQUENCE' publication
ALTER PUBLICATION testpub_forsequence DROP ALL SEQUENCES IN SCHEMA pub_test;
\dRp+ testpub_forsequence
-- should be able to set schema to 'FOR SEQUENCE' publication
ALTER PUBLICATION testpub_forsequence SET ALL SEQUENCES IN SCHEMA pub_test;
\dRp+ testpub_forsequence
SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_forschema FOR ALL SEQUENCES IN SCHEMA pub_test;
RESET client_min_messages;
-- fail - can't create publication with schema and sequence of the same schema
CREATE PUBLICATION testpub_for_seq_schema FOR ALL SEQUENCES IN SCHEMA pub_test, SEQUENCE pub_test.testpub_seq1;
-- fail - can't add a sequence of the same schema to the schema publication
ALTER PUBLICATION testpub_forschema ADD SEQUENCE pub_test.testpub_seq1;
-- fail - can't drop a sequence from the schema publication which isn't in the
-- publication
ALTER PUBLICATION testpub_forschema DROP SEQUENCE pub_test.testpub_seq1;
-- should be able to set sequence to schema publication
ALTER PUBLICATION testpub_forschema SET SEQUENCE pub_test.testpub_seq1;
\dRp+ testpub_forschema
SELECT pubname, puballtables, puballsequences FROM pg_publication WHERE pubname = 'testpub_forallsequences';
\d+ pub_test.testpub_seq1
\dRp+ testpub_forallsequences
DROP SEQUENCE testpub_seq0, pub_test.testpub_seq1, testpub_seq2;
DROP PUBLICATION testpub_forallsequences, testpub_forsequence, testpub_forschema;
-- publication testing multiple sequences at the same time
CREATE SEQUENCE testpub_seq1;
CREATE SEQUENCE testpub_seq2;
SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_multi FOR SEQUENCE testpub_seq1, testpub_seq2;
RESET client_min_messages;
\dRp+ testpub_multi
DROP PUBLICATION testpub_multi;
DROP SEQUENCE testpub_seq1;
DROP SEQUENCE testpub_seq2;
-- Publication mixing tables and sequences
SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_mix;
RESET client_min_messages;
CREATE SEQUENCE testpub_seq1;
CREATE SEQUENCE pub_test.testpub_seq2;
ALTER PUBLICATION testpub_mix ADD SEQUENCE testpub_seq1, TABLE testpub_tbl1;
\dRp+ testpub_mix
ALTER PUBLICATION testpub_mix ADD ALL SEQUENCES IN SCHEMA pub_test, ALL TABLES IN SCHEMA pub_test;
\dRp+ testpub_mix
ALTER PUBLICATION testpub_mix DROP ALL SEQUENCES IN SCHEMA pub_test;
\dRp+ testpub_mix
ALTER PUBLICATION testpub_mix DROP ALL TABLES IN SCHEMA pub_test;
\dRp+ testpub_mix
DROP PUBLICATION testpub_mix;
DROP SEQUENCE testpub_seq1;
DROP SEQUENCE pub_test.testpub_seq2;
-- make sure we replicate only the correct relation type
CREATE SCHEMA pub_test1;
CREATE SEQUENCE pub_test1.test_seq1;
CREATE TABLE pub_test1.test_tbl1 (a int primary key, b int);
CREATE SCHEMA pub_test2;
CREATE SEQUENCE pub_test2.test_seq2;
CREATE TABLE pub_test2.test_tbl2 (a int primary key, b int);
SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_schemas;
RESET client_min_messages;
-- add tables from one schema, sequences from the other
ALTER PUBLICATION testpub_schemas ADD ALL TABLES IN SCHEMA pub_test2;
ALTER PUBLICATION testpub_schemas ADD ALL SEQUENCES IN SCHEMA pub_test1;
\dRp+ testpub_schemas
\dn+ pub_test1
\dn+ pub_test2
\d+ pub_test1.test_seq1;
\d+ pub_test1.test_tbl1;
\d+ pub_test2.test_seq2;
\d+ pub_test2.test_tbl2;
-- add the other object type from each schema
ALTER PUBLICATION testpub_schemas ADD ALL TABLES IN SCHEMA pub_test1;
ALTER PUBLICATION testpub_schemas ADD ALL SEQUENCES IN SCHEMA pub_test2;
\dRp+ testpub_schemas
\dn+ pub_test1
\dn+ pub_test2
\d+ pub_test1.test_seq1;
\d+ pub_test1.test_tbl1;
\d+ pub_test2.test_seq2;
\d+ pub_test2.test_tbl2;
-- now drop the object type added first
ALTER PUBLICATION testpub_schemas DROP ALL TABLES IN SCHEMA pub_test2;
ALTER PUBLICATION testpub_schemas DROP ALL SEQUENCES IN SCHEMA pub_test1;
\dRp+ testpub_schemas
\dn+ pub_test1
\dn+ pub_test2
\d+ pub_test1.test_seq1;
\d+ pub_test1.test_tbl1;
\d+ pub_test2.test_seq2;
\d+ pub_test2.test_tbl2;
-- should fail (publication contains the whole schema)
ALTER PUBLICATION testpub_schemas ADD TABLE pub_test1.test_tbl1;
ALTER PUBLICATION testpub_schemas ADD SEQUENCE pub_test2.test_seq2;
-- should work (different schema)
ALTER PUBLICATION testpub_schemas ADD TABLE pub_test2.test_tbl2;
ALTER PUBLICATION testpub_schemas ADD SEQUENCE pub_test1.test_seq1;
\dRp+ testpub_schemas
\d+ pub_test1.test_seq1;
\d+ pub_test1.test_tbl1;
\d+ pub_test2.test_seq2;
\d+ pub_test2.test_tbl2;
-- now drop the explicitly added objects again
ALTER PUBLICATION testpub_schemas DROP TABLE pub_test2.test_tbl2;
ALTER PUBLICATION testpub_schemas DROP SEQUENCE pub_test1.test_seq1;
\dRp+ testpub_schemas
\d+ pub_test1.test_seq1;
\d+ pub_test1.test_tbl1;
\d+ pub_test2.test_seq2;
\d+ pub_test2.test_tbl2;
DROP PUBLICATION testpub_schemas;
DROP TABLE pub_test1.test_tbl1, pub_test2.test_tbl2;
DROP SEQUENCE pub_test1.test_seq1, pub_test2.test_seq2;
DROP SCHEMA pub_test1, pub_test2;
-- Tests for partitioned tables -- Tests for partitioned tables
SET client_min_messages = 'ERROR'; SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_forparted; CREATE PUBLICATION testpub_forparted;
@ -1199,51 +1004,32 @@ CREATE SCHEMA sch1;
CREATE SCHEMA sch2; CREATE SCHEMA sch2;
CREATE TABLE sch1.tbl1 (a int) PARTITION BY RANGE(a); CREATE TABLE sch1.tbl1 (a int) PARTITION BY RANGE(a);
CREATE TABLE sch2.tbl1_part1 PARTITION OF sch1.tbl1 FOR VALUES FROM (1) to (10); CREATE TABLE sch2.tbl1_part1 PARTITION OF sch1.tbl1 FOR VALUES FROM (1) to (10);
CREATE SEQUENCE sch1.seq1;
CREATE SEQUENCE sch2.seq2;
-- Schema publication that does not include the schema that has the parent table -- Schema publication that does not include the schema that has the parent table
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=1); CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch2;
SELECT * FROM pg_publication_tables; SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
DROP PUBLICATION pub; DROP PUBLICATION pub;
-- Table publication that does not include the parent table -- Table publication that does not include the parent table
CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=1); CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
ALTER PUBLICATION pub ADD SEQUENCE sch2.seq2;
SELECT * FROM pg_publication_tables; SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
-- Table publication that includes both the parent table and the child table -- Table publication that includes both the parent table and the child table
ALTER PUBLICATION pub ADD TABLE sch1.tbl1; ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
SELECT * FROM pg_publication_tables; SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
DROP PUBLICATION pub; DROP PUBLICATION pub;
-- Schema publication that does not include the schema that has the parent table -- Schema publication that does not include the schema that has the parent table
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=0); CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
SELECT * FROM pg_publication_tables; SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
DROP PUBLICATION pub;
-- Sequence publication
CREATE PUBLICATION pub FOR SEQUENCE sch2.seq2;
SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
DROP PUBLICATION pub; DROP PUBLICATION pub;
-- Table publication that does not include the parent table -- Table publication that does not include the parent table
CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=0); CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
SELECT * FROM pg_publication_tables; SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
-- Table publication that includes both the parent table and the child table -- Table publication that includes both the parent table and the child table
ALTER PUBLICATION pub ADD TABLE sch1.tbl1; ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch2;
SELECT * FROM pg_publication_tables; SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
DROP PUBLICATION pub; DROP PUBLICATION pub;
DROP TABLE sch2.tbl1_part1; DROP TABLE sch2.tbl1_part1;
@ -1256,36 +1042,10 @@ CREATE TABLE sch1.tbl1_part3 (a int) PARTITION BY RANGE(a);
ALTER TABLE sch1.tbl1 ATTACH PARTITION sch1.tbl1_part3 FOR VALUES FROM (20) to (30); ALTER TABLE sch1.tbl1 ATTACH PARTITION sch1.tbl1_part3 FOR VALUES FROM (20) to (30);
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch1 WITH (PUBLISH_VIA_PARTITION_ROOT=1); CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
SELECT * FROM pg_publication_tables; SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
DROP PUBLICATION pub;
-- Schema publication
CREATE PUBLICATION pub FOR SEQUENCE sch2.seq2;
SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
DROP PUBLICATION pub;
-- Sequence publication
CREATE PUBLICATION pub FOR ALL SEQUENCES IN SCHEMA sch2;
SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
ALTER PUBLICATION pub DROP SEQUENCE sch1.seq1;
SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch1;
SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
RESET client_min_messages; RESET client_min_messages;
DROP PUBLICATION pub; DROP PUBLICATION pub;
DROP TABLE sch1.tbl1; DROP TABLE sch1.tbl1;
DROP SEQUENCE sch1.seq1, sch2.seq2;
DROP SCHEMA sch1 cascade; DROP SCHEMA sch1 cascade;
DROP SCHEMA sch2 cascade; DROP SCHEMA sch2 cascade;

View File

@ -1,202 +0,0 @@
# Copyright (c) 2021, PostgreSQL Global Development Group
# This tests that sequences are replicated correctly by logical replication
use strict;
use warnings;
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
# Initialize publisher node
my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
$node_publisher->init(allows_streaming => 'logical');
$node_publisher->start;
# Create subscriber node
my $node_subscriber = PostgreSQL::Test::Cluster->new('subscriber');
$node_subscriber->init(allows_streaming => 'logical');
$node_subscriber->start;
# Create some preexisting content on publisher
my $ddl = qq(
CREATE TABLE seq_test (v BIGINT);
CREATE SEQUENCE s;
);
# Setup structure on the publisher
$node_publisher->safe_psql('postgres', $ddl);
# Create some the same structure on subscriber, and an extra sequence that
# we'll create on the publisher later
$ddl = qq(
CREATE TABLE seq_test (v BIGINT);
CREATE SEQUENCE s;
CREATE SEQUENCE s2;
);
$node_subscriber->safe_psql('postgres', $ddl);
# Setup logical replication
my $publisher_connstr = $node_publisher->connstr . ' dbname=postgres';
$node_publisher->safe_psql('postgres',
"CREATE PUBLICATION seq_pub");
$node_publisher->safe_psql('postgres',
"ALTER PUBLICATION seq_pub ADD SEQUENCE s");
$node_subscriber->safe_psql('postgres',
"CREATE SUBSCRIPTION seq_sub CONNECTION '$publisher_connstr' PUBLICATION seq_pub"
);
$node_publisher->wait_for_catchup('seq_sub');
# Wait for initial sync to finish as well
my $synced_query =
"SELECT count(1) = 0 FROM pg_subscription_rel WHERE srsubstate NOT IN ('s', 'r');";
$node_subscriber->poll_query_until('postgres', $synced_query)
or die "Timed out while waiting for subscriber to synchronize data";
# Insert initial test data
$node_publisher->safe_psql(
'postgres', qq(
-- generate a number of values using the sequence
INSERT INTO seq_test SELECT nextval('s') FROM generate_series(1,100);
));
$node_publisher->wait_for_catchup('seq_sub');
# Check the data on subscriber
my $result = $node_subscriber->safe_psql(
'postgres', qq(
SELECT * FROM s;
));
is( $result, '132|0|t',
'initial test data replicated');
# advance the sequence in a rolled-back transaction - the rollback
# does not wait for the replication, so we could see any intermediate state
# so do something else after the test, to ensure we wait for everything
$node_publisher->safe_psql(
'postgres', qq(
BEGIN;
INSERT INTO seq_test SELECT nextval('s') FROM generate_series(1,100);
ROLLBACK;
INSERT INTO seq_test VALUES (-1);
));
$node_publisher->wait_for_catchup('seq_sub');
# Check the data on subscriber
$result = $node_subscriber->safe_psql(
'postgres', qq(
SELECT * FROM s;
));
is( $result, '231|0|t',
'advance sequence in rolled-back transaction');
# create a new sequence and roll it back - should not be replicated, due to
# the transactional behavior
$node_publisher->safe_psql(
'postgres', qq(
BEGIN;
CREATE SEQUENCE s2;
ALTER PUBLICATION seq_pub ADD SEQUENCE s2;
INSERT INTO seq_test SELECT nextval('s2') FROM generate_series(1,100);
ROLLBACK;
));
$node_publisher->wait_for_catchup('seq_sub');
# Check the data on subscriber
$result = $node_subscriber->safe_psql(
'postgres', qq(
SELECT * FROM s2;
));
is( $result, '1|0|f',
'create new sequence and roll it back');
# create a new sequence, advance it in a rolled-back transaction, but commit
# the create - the advance should be replicated nevertheless
$node_publisher->safe_psql(
'postgres', qq(
BEGIN;
CREATE SEQUENCE s2;
ALTER PUBLICATION seq_pub ADD SEQUENCE s2;
SAVEPOINT sp1;
INSERT INTO seq_test SELECT nextval('s2') FROM generate_series(1,100);
ROLLBACK TO sp1;
COMMIT;
));
$node_publisher->wait_for_catchup('seq_sub');
# Wait for sync of the second sequence we just added to finish
$synced_query =
"SELECT count(1) = 0 FROM pg_subscription_rel WHERE srsubstate NOT IN ('s', 'r');";
$node_subscriber->poll_query_until('postgres', $synced_query)
or die "Timed out while waiting for subscriber to synchronize data";
# Check the data on subscriber
$result = $node_subscriber->safe_psql(
'postgres', qq(
SELECT * FROM s2;
));
is( $result, '132|0|t',
'create sequence, advance it in rolled-back transaction, but commit the create');
# advance the new sequence in a transaction, and roll it back - the rollback
# does not wait for the replication, so we could see any intermediate state
# so do something else after the test, to ensure we wait for everything
$node_publisher->safe_psql(
'postgres', qq(
BEGIN;
INSERT INTO seq_test SELECT nextval('s2') FROM generate_series(1,100);
ROLLBACK;
INSERT INTO seq_test VALUES (-1);
));
$node_publisher->wait_for_catchup('seq_sub');
# Check the data on subscriber
$result = $node_subscriber->safe_psql(
'postgres', qq(
SELECT * FROM s2;
));
is( $result, '231|0|t',
'advance the new sequence in a transaction and roll it back');
# advance the sequence in a subtransaction - the subtransaction gets rolled
# back, but commit the main one - the changes should still be replicated
$node_publisher->safe_psql(
'postgres', qq(
BEGIN;
SAVEPOINT s1;
INSERT INTO seq_test SELECT nextval('s2') FROM generate_series(1,100);
ROLLBACK TO s1;
COMMIT;
));
$node_publisher->wait_for_catchup('seq_sub');
# Check the data on subscriber
$result = $node_subscriber->safe_psql(
'postgres', qq(
SELECT * FROM s2;
));
is( $result, '330|0|t',
'advance sequence in a subtransaction');
done_testing();