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 \
decoding_into_rel binary prepared replorigin time messages \
spill slot truncate stream stats twophase twophase_stream \
sequence
spill slot truncate stream stats twophase twophase_stream
ISOLATION = mxact delayed_startup ondisk_startup concurrent_ddl_dml \
oldest_xmin snapshot_transfer subxact_without_top concurrent_stream \
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
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
-----------------------------------------------------------
BEGIN
@ -68,7 +68,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
COMMIT;
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
-----------------------------------------------------------
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);
INSERT INTO somechange DEFAULT VALUES;
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;
data
------------------------------------------------
@ -29,9 +29,9 @@ SELECT * FROM changeresult;
(3 rows)
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
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;
data
--------------------------------------------------------------------------------------------------------------------------------------------------
@ -63,7 +63,7 @@ DROP TABLE somechange;
CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$
BEGIN
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;
SELECT * FROM slot_changes_wrapper('regression_slot');
slot_changes_wrapper
@ -84,7 +84,7 @@ SELECT * FROM slot_changes_wrapper('regression_slot');
COMMIT
(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
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN

View File

@ -7,7 +7,7 @@ step s0init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_sl
init
(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
----
(0 rows)
@ -27,7 +27,7 @@ t
(1 row)
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
--------------------------------------------
BEGIN
@ -50,7 +50,7 @@ step s0init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_sl
init
(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
----
(0 rows)
@ -71,7 +71,7 @@ t
step s0alter: ALTER TABLE do_write ADD column ts timestamptz;
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
------------------------------------------------------------------------------
BEGIN

View File

@ -35,7 +35,7 @@ init
step s2c: COMMIT;
step s1insert: INSERT INTO do_write DEFAULT VALUES;
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
--------------------------------------------------------------------
BEGIN
@ -46,7 +46,7 @@ COMMIT
step s1insert: INSERT INTO do_write DEFAULT VALUES;
step s1alter: ALTER TABLE do_write ADD COLUMN addedbys1 int;
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
--------------------------------------------------------------------------------------------
BEGIN

View File

@ -72,9 +72,9 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
-- origin tx
INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again');
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
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
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN
@ -110,7 +110,7 @@ SELECT pg_replication_origin_xact_setup('0/aabbccdd', '2013-01-01 00:00');
(1 row)
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;
-- check replication progress for the session is correct
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();
ERROR: no replication origin is configured
-- 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
------
(0 rows)
--but new original changes still show up
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
--------------------------------------------------------------------------------
BEGIN
@ -227,7 +227,7 @@ SELECT local_id, external_id,
1 | regress_test_decoding: regression_slot_no_lsn | f | t
(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
-------------------------------------------------------------------------------------
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));
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
----------------------------------------------------------------------------------------------------------
BEGIN
@ -115,7 +115,7 @@ INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (7, 5
COMMIT;
-- make old files go away
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
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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);
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);
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
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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)
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
---------------------------------------------------------------------------------------------------------
BEGIN
@ -107,7 +107,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'in
COMMIT
(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
---------------------------------------------------------------------------------------------------------
BEGIN
@ -132,7 +132,7 @@ SELECT :'wal_lsn' = :'end_lsn';
t
(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
---------------------------------------------------------------------------------------------------------
BEGIN
@ -140,7 +140,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'in
COMMIT
(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
------
(0 rows)

View File

@ -52,7 +52,7 @@ CREATE TABLE toasted_copy (
);
ALTER TABLE toasted_copy ALTER COLUMN data SET STORAGE EXTERNAL;
\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
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN
@ -316,7 +316,7 @@ SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several;
t
(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
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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
UPDATE toasted_several SET toasted_col1 = toasted_key;
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
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN
@ -350,7 +350,7 @@ UPDATE toasted_several SET toasted_col1 = toasted_col2 WHERE id = 1;
DELETE FROM toasted_several WHERE id = 1;
COMMIT;
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: %';
regexp_replace
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -373,7 +373,7 @@ INSERT INTO tbl1 VALUES(1, repeat('a', 4000)) ;
ALTER TABLE tbl1 ADD COLUMN id serial primary key;
INSERT INTO tbl2 VALUES(1);
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
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN

View File

@ -11,7 +11,7 @@ CREATE TABLE tab2 (a int primary key, b int);
TRUNCATE tab1;
TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE;
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
------------------------------------------------------
BEGIN

View File

@ -13,7 +13,7 @@ teardown
session "s0"
setup { SET synchronous_commit=on; }
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 "s0w" { INSERT INTO do_write DEFAULT VALUES; }

View File

@ -16,7 +16,7 @@ session "s1"
setup { SET synchronous_commit=on; }
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 "s1checkpoint" { CHECKPOINT; }
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
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');
@ -64,11 +64,11 @@ ALTER TABLE replication_example RENAME COLUMN text TO somenum;
INSERT INTO replication_example(somedata, somenum) VALUES (4, 1);
-- 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);
-- 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);
@ -82,7 +82,7 @@ INSERT INTO replication_example(somedata, somenum, zaphod1) VALUES (6, 4, 2);
COMMIT;
-- 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
BEGIN;
@ -91,7 +91,7 @@ INSERT INTO replication_example(id, somedata, somenum) SELECT i, i, i FROM gener
COMMIT;
/* 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
BEGIN;
@ -113,14 +113,14 @@ CREATE TABLE tr_unique(id2 serial unique NOT NULL, data int);
INSERT INTO tr_unique(data) VALUES(10);
ALTER TABLE tr_unique RENAME TO tr_pkey;
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);
--show deletion with primary key
DELETE FROM tr_pkey;
/* 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)
@ -137,7 +137,7 @@ COMMIT;
/* display results, but hide most of the output */
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)
ORDER BY 1,2;
@ -152,7 +152,7 @@ DROP TABLE spoolme;
COMMIT;
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';
-- 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;
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
ORDER BY min(ordinality);
@ -189,7 +189,7 @@ INSERT INTO tr_sub(path) VALUES ('1-top-2-#1');
RELEASE SAVEPOINT b;
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
BEGIN;
@ -218,7 +218,7 @@ RELEASE SAVEPOINT subtop;
INSERT INTO tr_sub(path) VALUES ('2-top-#1');
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
BEGIN;
@ -231,7 +231,7 @@ ROLLBACK TO SAVEPOINT b;
INSERT INTO tr_sub(path) VALUES ('3-top-2-#2');
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
-- subxact commit is handled correctly
@ -250,7 +250,7 @@ INSERT INTO tr_sub(path) VALUES ('5-top-1-#1');
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
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);
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)
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
@ -414,7 +414,7 @@ UPDATE toasttable
SET toasted_col1 = (SELECT string_agg(g.i::text, '') FROM generate_series(1, 2000) g(i))
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);
@ -426,10 +426,10 @@ WHERE id = 1;
-- make sure we decode correctly even if the toast table is gone
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
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');

View File

@ -32,10 +32,10 @@ BEGIN;
SELECT pg_current_xact_id() = '0';
-- don't show yet, haven't committed
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;
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');

View File

@ -15,14 +15,14 @@ CREATE TABLE somechange(id serial primary key);
INSERT INTO somechange DEFAULT VALUES;
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;
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
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;
DROP TABLE changeresult;
@ -32,11 +32,11 @@ DROP TABLE somechange;
CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$
BEGIN
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;
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');

View File

@ -41,10 +41,10 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
-- origin tx
INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again');
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
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');
@ -60,7 +60,7 @@ BEGIN;
-- setup transaction origin
SELECT pg_replication_origin_xact_setup('0/aabbccdd', '2013-01-01 00:00');
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;
-- 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();
-- 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
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_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,
local_lsn <> '0/0' AS valid_local_lsn
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
SELECT pg_replication_origin_session_reset();
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');
CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120));
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;
INSERT INTO replication_example(somedata) VALUES (2);
@ -90,7 +90,7 @@ COMMIT;
-- make old files go away
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,
-- 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);
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);
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');
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);
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_slot2', 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');
INSERT INTO replication_example(somedata, text) VALUES (1, 4);
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 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_slot2', 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');
DROP TABLE replication_example;

View File

@ -264,7 +264,7 @@ ALTER TABLE toasted_copy ALTER COLUMN data SET STORAGE EXTERNAL;
202 untoasted199
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
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));
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
UPDATE toasted_several SET toasted_col1 = toasted_key;
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
@ -306,7 +306,7 @@ COMMIT;
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: %';
/*
@ -322,6 +322,6 @@ INSERT INTO tbl1 VALUES(1, repeat('a', 4000)) ;
ALTER TABLE tbl1 ADD COLUMN id serial primary key;
INSERT INTO tbl2 VALUES(1);
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');

View File

@ -10,5 +10,5 @@ TRUNCATE tab1;
TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE;
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');

View File

@ -35,7 +35,6 @@ typedef struct
bool include_timestamp;
bool skip_empty_xacts;
bool only_local;
bool include_sequences;
} TestDecodingData;
/*
@ -77,10 +76,6 @@ static void pg_decode_message(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, XLogRecPtr message_lsn,
bool transactional, const char *prefix,
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,
TransactionId xid,
const char *gid);
@ -121,10 +116,6 @@ static void pg_decode_stream_message(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, XLogRecPtr message_lsn,
bool transactional, const char *prefix,
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,
ReorderBufferTXN *txn,
int nrelations, Relation relations[],
@ -150,7 +141,6 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
cb->filter_by_origin_cb = pg_decode_filter;
cb->shutdown_cb = pg_decode_shutdown;
cb->message_cb = pg_decode_message;
cb->sequence_cb = pg_decode_sequence;
cb->filter_prepare_cb = pg_decode_filter_prepare;
cb->begin_prepare_cb = pg_decode_begin_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_change_cb = pg_decode_stream_change;
cb->stream_message_cb = pg_decode_stream_message;
cb->stream_sequence_cb = pg_decode_stream_sequence;
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_timestamp = false;
data->skip_empty_xacts = false;
data->include_sequences = true;
data->only_local = false;
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\"",
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
{
ereport(ERROR,
@ -779,38 +756,6 @@ pg_decode_message(LogicalDecodingContext *ctx,
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
pg_decode_stream_start(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn)
@ -1010,38 +955,6 @@ pg_decode_stream_message(LogicalDecodingContext *ctx,
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.
* 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
</para></entry>
</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>
</tgroup>
</table>
@ -9699,11 +9689,6 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
<entry>prepared transactions</entry>
</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>
<entry><link linkend="view-pg-publication-tables"><structname>pg_publication_tables</structname></link></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 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">
<title><structname>pg_publication_tables</structname></title>

View File

@ -458,7 +458,6 @@ typedef struct OutputPluginCallbacks
LogicalDecodeTruncateCB truncate_cb;
LogicalDecodeCommitCB commit_cb;
LogicalDecodeMessageCB message_cb;
LogicalDecodeSequenceCB sequence_cb;
LogicalDecodeFilterByOriginCB filter_by_origin_cb;
LogicalDecodeShutdownCB shutdown_cb;
LogicalDecodeFilterPrepareCB filter_prepare_cb;
@ -473,7 +472,6 @@ typedef struct OutputPluginCallbacks
LogicalDecodeStreamCommitCB stream_commit_cb;
LogicalDecodeStreamChangeCB stream_change_cb;
LogicalDecodeStreamMessageCB stream_message_cb;
LogicalDecodeStreamSequenceCB stream_sequence_cb;
LogicalDecodeStreamTruncateCB stream_truncate_cb;
} OutputPluginCallbacks;
@ -483,11 +481,9 @@ typedef void (*LogicalOutputPluginInit) (struct OutputPluginCallbacks *cb);
and <function>commit_cb</function> callbacks are required,
while <function>startup_cb</function>,
<function>filter_by_origin_cb</function>, <function>truncate_cb</function>,
<function>sequence_cb</function>, and <function>shutdown_cb</function> are
optional. If <function>truncate_cb</function> is not set but a
and <function>shutdown_cb</function> are optional.
If <function>truncate_cb</function> is not set but a
<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>
@ -496,8 +492,7 @@ typedef void (*LogicalOutputPluginInit) (struct OutputPluginCallbacks *cb);
<function>stream_stop_cb</function>, <function>stream_abort_cb</function>,
<function>stream_commit_cb</function>, <function>stream_change_cb</function>,
and <function>stream_prepare_cb</function>
are required, while <function>stream_message_cb</function>,
<function>stream_sequence_cb</function>, and
are required, while <function>stream_message_cb</function> and
<function>stream_truncate_cb</function> are optional.
</para>
@ -813,35 +808,6 @@ typedef void (*LogicalDecodeMessageCB) (struct LogicalDecodingContext *ctx,
</para>
</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">
<title>Prepare Filter Callback</title>
@ -1051,26 +1017,6 @@ typedef void (*LogicalDecodeStreamMessageCB) (struct LogicalDecodingContext *ctx
</para>
</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">
<title>Stream Truncate Callback</title>
<para>
@ -1251,9 +1197,8 @@ OutputPluginWrite(ctx, true);
in-progress transactions. There are multiple required streaming callbacks
(<function>stream_start_cb</function>, <function>stream_stop_cb</function>,
<function>stream_abort_cb</function>, <function>stream_commit_cb</function>
and <function>stream_change_cb</function>) and multiple optional callbacks
(<function>stream_message_cb</function>, <function>stream_sequence_cb</function>,
and <function>stream_truncate_cb</function>).
and <function>stream_change_cb</function>) and two optional callbacks
(<function>stream_message_cb</function> and <function>stream_truncate_cb</function>).
Also, if streaming of two-phase commands is to be supported, then additional
callbacks must be provided. (See <xref linkend="logicaldecoding-two-phase-commits"/>
for details).

View File

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

View File

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

View File

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

View File

@ -1941,14 +1941,12 @@ get_object_address_publication_schema(List *object, bool missing_ok)
char *pubname;
char *schemaname;
Oid schemaid;
char *objtype;
ObjectAddressSet(address, PublicationNamespaceRelationId, InvalidOid);
/* Fetch schema name and publication name from input list */
schemaname = strVal(linitial(object));
pubname = strVal(lsecond(object));
objtype = strVal(lthird(object));
schemaid = get_namespace_oid(schemaname, missing_ok);
if (!OidIsValid(schemaid))
@ -1961,12 +1959,10 @@ get_object_address_publication_schema(List *object, bool missing_ok)
/* Find the publication schema mapping in syscache */
address.objectId =
GetSysCacheOid3(PUBLICATIONNAMESPACEMAP,
GetSysCacheOid2(PUBLICATIONNAMESPACEMAP,
Anum_pg_publication_namespace_oid,
ObjectIdGetDatum(schemaid),
ObjectIdGetDatum(pub->oid),
CharGetDatum(objtype[0]));
ObjectIdGetDatum(pub->oid));
if (!OidIsValid(address.objectId) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@ -2247,6 +2243,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_DOMCONSTRAINT:
case OBJECT_CAST:
case OBJECT_USER_MAPPING:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
case OBJECT_DEFACL:
case OBJECT_TRANSFORM:
@ -2271,7 +2268,6 @@ pg_get_object_address(PG_FUNCTION_ARGS)
/* fall through to check args length */
/* FALLTHROUGH */
case OBJECT_OPERATOR:
case OBJECT_PUBLICATION_NAMESPACE:
if (list_length(args) != 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@ -2343,8 +2339,6 @@ pg_get_object_address(PG_FUNCTION_ARGS)
objnode = (Node *) list_make2(name, linitial(args));
break;
case OBJECT_PUBLICATION_NAMESPACE:
objnode = (Node *) list_make3(linitial(name), linitial(args), lsecond(args));
break;
case OBJECT_USER_MAPPING:
objnode = (Node *) list_make2(linitial(name), linitial(args));
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
* 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 schema.
* the caller.
*/
static bool
getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok,
char **pubname, char **nspname, char **objtype)
char **pubname, char **nspname)
{
HeapTuple tup;
Form_pg_publication_namespace pnform;
@ -2941,13 +2934,6 @@ getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok,
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);
return true;
}
@ -3979,17 +3965,15 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
{
char *pubname;
char *nspname;
char *objtype;
if (!getPublicationSchemaInfo(object, missing_ok,
&pubname, &nspname, &objtype))
&pubname, &nspname))
break;
appendStringInfo(&buffer, _("publication of schema %s in publication %s type %s"),
nspname, pubname, objtype);
appendStringInfo(&buffer, _("publication of schema %s in publication %s"),
nspname, pubname);
pfree(pubname);
pfree(nspname);
pfree(objtype);
break;
}
@ -5816,24 +5800,18 @@ getObjectIdentityParts(const ObjectAddress *object,
{
char *pubname;
char *nspname;
char *objtype;
if (!getPublicationSchemaInfo(object, missing_ok, &pubname,
&nspname, &objtype))
&nspname))
break;
appendStringInfo(&buffer, "%s in publication %s type %s",
nspname, pubname, objtype);
appendStringInfo(&buffer, "%s in publication %s",
nspname, pubname);
if (objargs)
*objargs = list_make1(pubname);
else
pfree(pubname);
if (objargs)
*objargs = lappend(*objargs, objtype);
else
pfree(objtype);
if (objname)
*objname = list_make1(nspname);
else

View File

@ -55,10 +55,9 @@ static void publication_translate_columns(Relation targetrel, List *columns,
static void
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 &&
RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE &&
RelationGetForm(targetrel)->relkind != RELKIND_SEQUENCE)
RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot add relation \"%s\" to publication",
@ -135,8 +134,7 @@ static bool
is_publishable_class(Oid relid, Form_pg_class reltuple)
{
return (reltuple->relkind == RELKIND_RELATION ||
reltuple->relkind == RELKIND_PARTITIONED_TABLE ||
reltuple->relkind == RELKIND_SEQUENCE) &&
reltuple->relkind == RELKIND_PARTITIONED_TABLE) &&
!IsCatalogRelationOid(relid) &&
reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
relid >= FirstNormalObjectId;
@ -181,52 +179,6 @@ filter_partitions(List *relids)
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.
*/
@ -256,7 +208,7 @@ is_schema_publication(Oid pubid)
ObjectIdGetDatum(pubid));
scan = systable_beginscan(pubschsrel,
PublicationNamespacePnnspidPnpubidPntypeIndexId,
PublicationNamespacePnnspidPnpubidIndexId,
true, NULL, 1, &scankey);
tup = systable_getnext(scan);
result = HeapTupleIsValid(tup);
@ -364,9 +316,7 @@ GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level
}
else
{
/* we only search for ancestors of tables, so PUB_OBJTYPE_TABLE */
aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor),
PUB_OBJTYPE_TABLE);
aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
if (list_member_oid(aschemaPubids, puboid))
{
topmost_relid = ancestor;
@ -635,7 +585,7 @@ pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols, MemoryContext mcxt)
* Insert new publication / schema mapping.
*/
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;
HeapTuple tup;
@ -647,8 +597,6 @@ publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exi
ObjectAddress myself,
referenced;
AssertObjectTypeValid(objectType);
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
* case. The real protection is the unique key on the catalog.
*/
if (SearchSysCacheExists3(PUBLICATIONNAMESPACEMAP,
if (SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP,
ObjectIdGetDatum(schemaid),
ObjectIdGetDatum(pubid),
CharGetDatum(objectType)))
ObjectIdGetDatum(pubid)))
{
table_close(rel, RowExclusiveLock);
@ -685,8 +632,6 @@ publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exi
ObjectIdGetDatum(pubid);
values[Anum_pg_publication_namespace_pnnspid - 1] =
ObjectIdGetDatum(schemaid);
values[Anum_pg_publication_namespace_pntype - 1] =
CharGetDatum(objectType);
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
* partitions.
*/
schemaRels = GetSchemaPublicationRelations(schemaid, objectType,
schemaRels = GetSchemaPublicationRelations(schemaid,
PUBLICATION_PART_ALL);
InvalidatePublicationRels(schemaRels);
@ -746,14 +691,11 @@ GetRelationPublications(Oid relid)
/*
* Gets list of relation oids for a publication.
*
* This should only be used FOR TABLE / FOR SEQUENCE publications, the FOR
* ALL TABLES / SEQUENCES should use GetAllTablesPublicationRelations()
* and GetAllSequencesPublicationRelations().
*
* XXX pub_partopt only matters for tables, not sequences.
* This should only be used FOR TABLE publications, the FOR ALL TABLES
* should use GetAllTablesPublicationRelations().
*/
List *
GetPublicationRelations(Oid pubid, char objectType, PublicationPartOpt pub_partopt)
GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
{
List *result;
Relation pubrelsrel;
@ -761,8 +703,6 @@ GetPublicationRelations(Oid pubid, char objectType, PublicationPartOpt pub_parto
SysScanDesc scan;
HeapTuple tup;
AssertObjectTypeValid(objectType);
/* Find all publications associated with the relation. */
pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
@ -777,29 +717,11 @@ GetPublicationRelations(Oid pubid, char objectType, PublicationPartOpt pub_parto
result = NIL;
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
char relkind;
Form_pg_publication_rel pubrel;
pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
relkind = get_rel_relkind(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);
result = GetPubPartitionOptionRelations(result, pub_partopt,
pubrel->prrelid);
}
systable_endscan(scan);
@ -849,43 +771,6 @@ GetAllTablesPublications(void)
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).
*
@ -952,38 +837,28 @@ GetAllTablesPublicationRelations(bool pubviaroot)
/*
* Gets the list of schema oids for a publication.
*
* This should only be used FOR ALL TABLES IN SCHEMA and FOR ALL SEQUENCES
* publications.
*
* 'objectType' determines whether to get FOR TABLE or FOR SEQUENCES schemas
* This should only be used FOR ALL TABLES IN SCHEMA publications.
*/
List *
GetPublicationSchemas(Oid pubid, char objectType)
GetPublicationSchemas(Oid pubid)
{
List *result = NIL;
Relation pubschsrel;
ScanKeyData scankey[2];
ScanKeyData scankey;
SysScanDesc scan;
HeapTuple tup;
AssertObjectTypeValid(objectType);
/* Find all schemas associated with the publication */
pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
ScanKeyInit(&scankey[0],
ScanKeyInit(&scankey,
Anum_pg_publication_namespace_pnpubid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(pubid));
ScanKeyInit(&scankey[1],
Anum_pg_publication_namespace_pntype,
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(objectType));
scan = systable_beginscan(pubschsrel,
PublicationNamespacePnnspidPnpubidPntypeIndexId,
true, NULL, 2, scankey);
PublicationNamespacePnnspidPnpubidIndexId,
true, NULL, 1, &scankey);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
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.
*
* 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 *
GetSchemaPublications(Oid schemaid, char objectType)
GetSchemaPublications(Oid schemaid)
{
List *result = NIL;
CatCList *pubschlist;
int i;
/* unsupported object type */
if (objectType == PUB_OBJTYPE_UNSUPPORTED)
return result;
AssertObjectTypeValid(objectType);
/* Find all publications associated with the schema */
pubschlist = SearchSysCacheList1(PUBLICATIONNAMESPACEMAP,
ObjectIdGetDatum(schemaid));
@ -1028,11 +891,6 @@ GetSchemaPublications(Oid schemaid, char objectType)
{
HeapTuple tup = &pubschlist->members[i]->tuple;
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);
}
@ -1044,13 +902,9 @@ GetSchemaPublications(Oid schemaid, char objectType)
/*
* 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 *
GetSchemaPublicationRelations(Oid schemaid, char objectType,
PublicationPartOpt pub_partopt)
GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
{
Relation classRel;
ScanKeyData key[1];
@ -1059,7 +913,6 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
List *result = NIL;
Assert(OidIsValid(schemaid));
AssertObjectTypeValid(objectType);
classRel = table_open(RelationRelationId, AccessShareLock);
@ -1080,16 +933,9 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
continue;
relkind = get_rel_relkind(relid);
/* Skip if the relkind does not match FOR ALL TABLES / SEQUENCES. */
if (!pub_object_type_matches_relkind(objectType, relkind))
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)
if (relkind == RELKIND_RELATION)
result = lappend_oid(result, relid);
else if (relkind == RELKIND_PARTITIONED_TABLE)
{
List *partitionrels = NIL;
@ -1102,11 +948,7 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
pub_partopt,
relForm->oid);
result = list_concat_unique_oid(result, partitionrels);
continue;
}
/* non-partitioned tables and sequences */
result = lappend_oid(result, relid);
}
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
* or FOR ALL SEQUENCES IN SCHEMA publication.
* publication.
*/
List *
GetAllSchemaPublicationRelations(Oid pubid, char objectType,
PublicationPartOpt pub_partopt)
GetAllSchemaPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
{
List *result = NIL;
List *pubschemalist = GetPublicationSchemas(pubid, objectType);
List *pubschemalist = GetPublicationSchemas(pubid);
ListCell *cell;
AssertObjectTypeValid(objectType);
foreach(cell, pubschemalist)
{
Oid schemaid = lfirst_oid(cell);
List *schemaRels = NIL;
schemaRels = GetSchemaPublicationRelations(schemaid, objectType,
pub_partopt);
schemaRels = GetSchemaPublicationRelations(schemaid, pub_partopt);
result = list_concat(result, schemaRels);
}
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
*
@ -1199,12 +1001,10 @@ GetPublication(Oid pubid)
pub->oid = pubid;
pub->name = pstrdup(NameStr(pubform->pubname));
pub->alltables = pubform->puballtables;
pub->allsequences = pubform->puballsequences;
pub->pubactions.pubinsert = pubform->pubinsert;
pub->pubactions.pubupdate = pubform->pubupdate;
pub->pubactions.pubdelete = pubform->pubdelete;
pub->pubactions.pubtruncate = pubform->pubtruncate;
pub->pubactions.pubsequence = pubform->pubsequence;
pub->pubviaroot = pubform->pubviaroot;
ReleaseSysCache(tup);
@ -1315,12 +1115,10 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
*schemarelids;
relids = GetPublicationRelations(publication->oid,
PUB_OBJTYPE_TABLE,
publication->pubviaroot ?
PUBLICATION_PART_ROOT :
PUBLICATION_PART_LEAF);
schemarelids = GetAllSchemaPublicationRelations(publication->oid,
PUB_OBJTYPE_TABLE,
publication->pubviaroot ?
PUBLICATION_PART_ROOT :
PUBLICATION_PART_LEAF);
@ -1356,71 +1154,3 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
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)
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
SELECT * FROM pg_lock_status() AS L;

View File

@ -16,7 +16,6 @@
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@ -68,17 +67,15 @@ typedef struct rf_context
} rf_context;
static List *OpenRelIdList(List *relids);
static List *OpenRelationList(List *rels, char objectType);
static void CloseRelationList(List *rels);
static List *OpenTableList(List *tables);
static void CloseTableList(List *rels);
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);
static void PublicationDropRelations(Oid pubid, List *rels, bool missing_ok);
static void PublicationAddSchemas(Oid pubid, List *schemas, char objectType,
bool if_not_exists, AlterPublicationStmt *stmt);
static void PublicationDropSchemas(Oid pubid, List *schemas, char objectType,
bool missing_ok);
static void PublicationDropTables(Oid pubid, List *rels, bool missing_ok);
static void PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists,
AlterPublicationStmt *stmt);
static void PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok);
static void
parse_publication_options(ParseState *pstate,
@ -98,7 +95,6 @@ parse_publication_options(ParseState *pstate,
pubactions->pubupdate = true;
pubactions->pubdelete = true;
pubactions->pubtruncate = true;
pubactions->pubsequence = true;
*publish_via_partition_root = false;
/* Parse options */
@ -123,7 +119,6 @@ parse_publication_options(ParseState *pstate,
pubactions->pubupdate = false;
pubactions->pubdelete = false;
pubactions->pubtruncate = false;
pubactions->pubsequence = false;
*publish_given = true;
publish = defGetString(defel);
@ -146,8 +141,6 @@ parse_publication_options(ParseState *pstate,
pubactions->pubdelete = true;
else if (strcmp(publish_opt, "truncate") == 0)
pubactions->pubtruncate = true;
else if (strcmp(publish_opt, "sequence") == 0)
pubactions->pubsequence = true;
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@ -174,8 +167,7 @@ parse_publication_options(ParseState *pstate,
*/
static void
ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
List **tables, List **sequences,
List **tables_schemas, List **sequences_schemas)
List **rels, List **schemas)
{
ListCell *cell;
PublicationObjSpec *pubobj;
@ -193,22 +185,13 @@ ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
switch (pubobj->pubobjtype)
{
case PUBLICATIONOBJ_TABLE:
*tables = lappend(*tables, pubobj->pubtable);
break;
case PUBLICATIONOBJ_SEQUENCE:
*sequences = lappend(*sequences, pubobj->pubtable);
*rels = lappend(*rels, pubobj->pubtable);
break;
case PUBLICATIONOBJ_TABLES_IN_SCHEMA:
schemaid = get_namespace_oid(pubobj->name, false);
/* Filter out duplicates if user specifies "sch1, sch1" */
*tables_schemas = list_append_unique_oid(*tables_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);
*schemas = list_append_unique_oid(*schemas, schemaid);
break;
case PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA:
search_path = fetch_search_path(false);
@ -221,20 +204,7 @@ ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
list_free(search_path);
/* Filter out duplicates if user specifies "sch1, sch1" */
*tables_schemas = list_append_unique_oid(*tables_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);
*schemas = list_append_unique_oid(*schemas, schemaid);
break;
default:
/* 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.",
RelationGetRelationName(rel),
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)
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@ -286,14 +248,6 @@ CheckObjSchemaNotAlreadyInPublication(List *rels, List *schemaidlist,
RelationGetRelationName(rel)),
errdetail("Table's schema \"%s\" is already part of the publication or part of the specified schema list.",
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
CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
{
ListCell *lc;
Relation rel;
ObjectAddress myself;
Oid puboid;
@ -811,23 +764,8 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
bool publish_via_partition_root_given;
bool publish_via_partition_root;
AclResult aclresult;
List *tables = NIL;
List *sequences = 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;
}
List *relations = NIL;
List *schemaidlist = NIL;
/* must have CREATE privilege on database */
aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
@ -836,17 +774,11 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
get_database_name(MyDatabaseId));
/* FOR ALL TABLES requires superuser */
if (for_all_tables && !superuser())
if (stmt->for_all_tables && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
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);
/* Check if name is used */
@ -878,9 +810,7 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
Anum_pg_publication_oid);
values[Anum_pg_publication_oid - 1] = ObjectIdGetDatum(puboid);
values[Anum_pg_publication_puballtables - 1] =
BoolGetDatum(for_all_tables);
values[Anum_pg_publication_puballsequences - 1] =
BoolGetDatum(for_all_sequences);
BoolGetDatum(stmt->for_all_tables);
values[Anum_pg_publication_pubinsert - 1] =
BoolGetDatum(pubactions.pubinsert);
values[Anum_pg_publication_pubupdate - 1] =
@ -889,8 +819,6 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
BoolGetDatum(pubactions.pubdelete);
values[Anum_pg_publication_pubtruncate - 1] =
BoolGetDatum(pubactions.pubtruncate);
values[Anum_pg_publication_pubsequence - 1] =
BoolGetDatum(pubactions.pubsequence);
values[Anum_pg_publication_pubviaroot - 1] =
BoolGetDatum(publish_via_partition_root);
@ -908,42 +836,28 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
CommandCounterIncrement();
/* 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. */
CacheInvalidateRelcacheAll();
}
/*
* If the publication might have either tables or sequences (directly or
* through a schema), process that.
*/
if (!for_all_tables || !for_all_sequences)
else
{
ObjectsInPublicationToOids(stmt->pubobjects, pstate,
&tables, &sequences,
&tables_schemaidlist,
&sequences_schemaidlist);
ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
&schemaidlist);
/* FOR ALL TABLES IN SCHEMA requires superuser */
if (list_length(tables_schemaidlist) > 0 && !superuser())
if (list_length(schemaidlist) > 0 && !superuser())
ereport(ERROR,
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication"));
/* FOR ALL SEQUENCES IN SCHEMA requires superuser */
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)
if (list_length(relations) > 0)
{
List *rels;
rels = OpenRelationList(tables, PUB_OBJTYPE_TABLE);
CheckObjSchemaNotAlreadyInPublication(rels, tables_schemaidlist,
rels = OpenTableList(relations);
CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
PUBLICATIONOBJ_TABLE);
TransformPubWhereClauses(rels, pstate->p_sourcetext,
@ -952,46 +866,18 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
CheckPubRelationColumnList(rels, pstate->p_sourcetext,
publish_via_partition_root);
PublicationAddRelations(puboid, rels, true, NULL);
CloseRelationList(rels);
PublicationAddTables(puboid, rels, true, NULL);
CloseTableList(rels);
}
/* sequences added directly */
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)
if (list_length(schemaidlist) > 0)
{
/*
* Schema lock is held until the publication is created to prevent
* concurrent schema deletion.
*/
LockSchemaList(tables_schemaidlist);
PublicationAddSchemas(puboid,
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);
LockSchemaList(schemaidlist);
PublicationAddSchemas(puboid, schemaidlist, true, NULL);
}
}
@ -1055,7 +941,6 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
AccessShareLock);
root_relids = GetPublicationRelations(pubform->oid,
PUB_OBJTYPE_TABLE,
PUBLICATION_PART_ROOT);
foreach(lc, root_relids)
@ -1135,9 +1020,6 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
values[Anum_pg_publication_pubtruncate - 1] = BoolGetDatum(pubactions.pubtruncate);
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)
@ -1157,7 +1039,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
pubform = (Form_pg_publication) GETSTRUCT(tup);
/* Invalidate the relcache. */
if (pubform->puballtables || pubform->puballsequences)
if (pubform->puballtables)
{
CacheInvalidateRelcacheAll();
}
@ -1173,7 +1055,6 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
*/
if (root_relids == NIL)
relids = GetPublicationRelations(pubform->oid,
PUB_OBJTYPE_TABLE,
PUBLICATION_PART_ALL);
else
{
@ -1187,20 +1068,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
lfirst_oid(lc));
}
/* tables */
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);
relids = list_concat_unique_oid(relids, schemarelids);
@ -1255,7 +1123,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
if (!tables && stmt->action != AP_SetObjects)
return;
rels = OpenRelationList(tables, PUB_OBJTYPE_TABLE);
rels = OpenTableList(tables);
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
* publication or member of the schema list specified.
*/
schemas = list_concat_copy(schemaidlist,
GetPublicationSchemas(pubid,
PUB_OBJTYPE_TABLE));
schemas = list_concat_copy(schemaidlist, GetPublicationSchemas(pubid));
CheckObjSchemaNotAlreadyInPublication(rels, schemas,
PUBLICATIONOBJ_TABLE);
@ -1275,14 +1141,13 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
CheckPubRelationColumnList(rels, queryString, pubform->pubviaroot);
PublicationAddRelations(pubid, rels, false, stmt);
PublicationAddTables(pubid, rels, false, stmt);
}
else if (stmt->action == AP_DropObjects)
PublicationDropRelations(pubid, rels, false);
PublicationDropTables(pubid, rels, false);
else /* AP_SetObjects */
{
List *oldrelids = GetPublicationRelations(pubid,
PUB_OBJTYPE_TABLE,
PUBLICATION_PART_ROOT);
List *delrels = NIL;
ListCell *oldlc;
@ -1401,18 +1266,18 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
}
/* And drop them. */
PublicationDropRelations(pubid, delrels, true);
PublicationDropTables(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);
PublicationAddTables(pubid, rels, true, stmt);
CloseRelationList(delrels);
CloseTableList(delrels);
}
CloseRelationList(rels);
CloseTableList(rels);
}
/*
@ -1422,8 +1287,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
*/
static void
AlterPublicationSchemas(AlterPublicationStmt *stmt,
HeapTuple tup, List *schemaidlist,
char objectType)
HeapTuple tup, List *schemaidlist)
{
Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
@ -1445,20 +1309,20 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
List *rels;
List *reloids;
reloids = GetPublicationRelations(pubform->oid, objectType, PUBLICATION_PART_ROOT);
reloids = GetPublicationRelations(pubform->oid, PUBLICATION_PART_ROOT);
rels = OpenRelIdList(reloids);
CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
PUBLICATIONOBJ_TABLES_IN_SCHEMA);
CloseRelationList(rels);
PublicationAddSchemas(pubform->oid, schemaidlist, objectType, false, stmt);
CloseTableList(rels);
PublicationAddSchemas(pubform->oid, schemaidlist, false, stmt);
}
else if (stmt->action == AP_DropObjects)
PublicationDropSchemas(pubform->oid, schemaidlist, objectType, false);
PublicationDropSchemas(pubform->oid, schemaidlist, false);
else /* AP_SetObjects */
{
List *oldschemaids = GetPublicationSchemas(pubform->oid, objectType);
List *oldschemaids = GetPublicationSchemas(pubform->oid);
List *delschemas = NIL;
/* Identify which schemas should be dropped */
@ -1471,13 +1335,13 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
LockSchemaList(delschemas);
/* 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
* 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
CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
List *tables, List *tables_schemaidlist,
List *sequences, List *sequences_schemaidlist)
List *tables, List *schemaidlist)
{
Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
if ((stmt->action == AP_AddObjects || stmt->action == AP_SetObjects) &&
(tables_schemaidlist || sequences_schemaidlist) && !superuser())
schemaidlist && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
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
* schema
*/
if (tables_schemaidlist && pubform->puballtables)
if (schemaidlist && pubform->puballtables)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("publication \"%s\" is defined as FOR ALL TABLES",
NameStr(pubform->pubname)),
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. */
if (tables && pubform->puballtables)
ereport(ERROR,
@ -1527,108 +1379,6 @@ CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
errmsg("publication \"%s\" is defined as FOR ALL TABLES",
NameStr(pubform->pubname)),
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);
else
{
List *tables = NIL;
List *sequences = NIL;
List *tables_schemaidlist = NIL;
List *sequences_schemaidlist = NIL;
List *relations = NIL;
List *schemaidlist = NIL;
Oid pubid = pubform->oid;
ObjectsInPublicationToOids(stmt->pubobjects, pstate,
&tables, &sequences,
&tables_schemaidlist,
&sequences_schemaidlist);
ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
&schemaidlist);
CheckAlterPublication(stmt, tup,
tables, tables_schemaidlist,
sequences, sequences_schemaidlist);
CheckAlterPublication(stmt, tup, relations, schemaidlist);
heap_freetuple(tup);
@ -1707,16 +1451,9 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
errmsg("publication \"%s\" does not exist",
stmt->pubname));
AlterPublicationTables(stmt, tup, tables, tables_schemaidlist,
AlterPublicationTables(stmt, tup, relations, schemaidlist,
pstate->p_sourcetext);
AlterPublicationSequences(stmt, tup, sequences, sequences_schemaidlist);
AlterPublicationSchemas(stmt, tup, tables_schemaidlist,
PUB_OBJTYPE_TABLE);
AlterPublicationSchemas(stmt, tup, sequences_schemaidlist,
PUB_OBJTYPE_SEQUENCE);
AlterPublicationSchemas(stmt, tup, schemaidlist);
}
/* Cleanup. */
@ -1784,7 +1521,7 @@ RemovePublicationById(Oid pubid)
pubform = (Form_pg_publication) GETSTRUCT(tup);
/* Invalidate relcache so that publication info is rebuilt. */
if (pubform->puballtables || pubform->puballsequences)
if (pubform->puballtables)
CacheInvalidateRelcacheAll();
CatalogTupleDelete(rel, &tup->t_self);
@ -1820,7 +1557,6 @@ RemovePublicationSchemaById(Oid psoid)
* partitions.
*/
schemaRels = GetSchemaPublicationRelations(pubsch->pnnspid,
pubsch->pntype,
PUBLICATION_PART_ALL);
InvalidatePublicationRels(schemaRels);
@ -1863,10 +1599,10 @@ OpenRelIdList(List *relids)
* add them to a publication.
*/
static List *
OpenRelationList(List *rels, char objectType)
OpenTableList(List *tables)
{
List *relids = NIL;
List *result = NIL;
List *rels = NIL;
ListCell *lc;
List *relids_with_rf = 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
*/
foreach(lc, rels)
foreach(lc, tables)
{
PublicationTable *t = lfirst_node(PublicationTable, lc);
bool recurse = t->relation->inh;
Relation rel;
Oid myrelid;
PublicationRelInfo *pub_rel;
char myrelkind;
/* Allow query cancel in case this takes a long time */
CHECK_FOR_INTERRUPTS();
rel = table_openrv(t->relation, ShareUpdateExclusiveLock);
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".
@ -1935,7 +1655,7 @@ OpenRelationList(List *rels, char objectType)
pub_rel->relation = rel;
pub_rel->whereClause = t->whereClause;
pub_rel->columns = t->columns;
result = lappend(result, pub_rel);
rels = lappend(rels, pub_rel);
relids = lappend_oid(relids, myrelid);
if (t->whereClause)
@ -2004,9 +1724,10 @@ OpenRelationList(List *rels, char objectType)
pub_rel->relation = rel;
/* child inherits WHERE clause from parent */
pub_rel->whereClause = t->whereClause;
/* child inherits column list from parent */
pub_rel->columns = t->columns;
result = lappend(result, pub_rel);
rels = lappend(rels, pub_rel);
relids = lappend_oid(relids, childrelid);
if (t->whereClause)
@ -2021,14 +1742,14 @@ OpenRelationList(List *rels, char objectType)
list_free(relids);
list_free(relids_with_rf);
return result;
return rels;
}
/*
* Close all relations in the list.
*/
static void
CloseRelationList(List *rels)
CloseTableList(List *rels)
{
ListCell *lc;
@ -2076,12 +1797,12 @@ LockSchemaList(List *schemalist)
* Add listed tables to the publication.
*/
static void
PublicationAddRelations(Oid pubid, List *rels, bool if_not_exists,
PublicationAddTables(Oid pubid, List *rels, bool if_not_exists,
AlterPublicationStmt *stmt)
{
ListCell *lc;
Assert(!stmt || !stmt->for_all_objects);
Assert(!stmt || !stmt->for_all_tables);
foreach(lc, rels)
{
@ -2110,7 +1831,7 @@ PublicationAddRelations(Oid pubid, List *rels, bool if_not_exists,
* Remove listed tables from the publication.
*/
static void
PublicationDropRelations(Oid pubid, List *rels, bool missing_ok)
PublicationDropTables(Oid pubid, List *rels, bool missing_ok)
{
ObjectAddress obj;
ListCell *lc;
@ -2155,19 +1876,19 @@ PublicationDropRelations(Oid pubid, List *rels, bool missing_ok)
* Add listed schemas to the publication.
*/
static void
PublicationAddSchemas(Oid pubid, List *schemas, char objectType,
bool if_not_exists, AlterPublicationStmt *stmt)
PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists,
AlterPublicationStmt *stmt)
{
ListCell *lc;
Assert(!stmt || !stmt->for_all_objects);
Assert(!stmt || !stmt->for_all_tables);
foreach(lc, schemas)
{
Oid schemaid = lfirst_oid(lc);
ObjectAddress obj;
obj = publication_add_schema(pubid, schemaid, objectType, if_not_exists);
obj = publication_add_schema(pubid, schemaid, if_not_exists);
if (stmt)
{
EventTriggerCollectSimpleCommand(obj, InvalidObjectAddress,
@ -2183,7 +1904,7 @@ PublicationAddSchemas(Oid pubid, List *schemas, char objectType,
* Remove listed schemas from the publication.
*/
static void
PublicationDropSchemas(Oid pubid, List *schemas, char objectType, bool missing_ok)
PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok)
{
ObjectAddress obj;
ListCell *lc;
@ -2193,11 +1914,10 @@ PublicationDropSchemas(Oid pubid, List *schemas, char objectType, bool missing_o
{
Oid schemaid = lfirst_oid(lc);
psid = GetSysCacheOid3(PUBLICATIONNAMESPACEMAP,
psid = GetSysCacheOid2(PUBLICATIONNAMESPACEMAP,
Anum_pg_publication_namespace_oid,
ObjectIdGetDatum(schemaid),
ObjectIdGetDatum(pubid),
CharGetDatum(objectType));
ObjectIdGetDatum(pubid));
if (!OidIsValid(psid))
{
if (missing_ok)
@ -2252,13 +1972,6 @@ AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
NameStr(form->pubname)),
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))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),

View File

@ -332,160 +332,6 @@ ResetSequence(Oid seq_relid)
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
*
@ -552,13 +398,8 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
/* check the comment above nextval_internal()'s equivalent call. */
if (RelationNeedsWAL(rel))
{
GetTopTransactionId();
if (XLogLogicalInfoActive())
GetCurrentTransactionId();
}
START_CRIT_SECTION();
MarkBufferDirty(buf);
@ -578,7 +419,6 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
xlrec.node = rel->rd_node;
xlrec.created = true;
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
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
* to assign xids subxacts, that'll already trigger an appropriate wait.
* (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))
{
GetTopTransactionId();
if (XLogLogicalInfoActive())
GetCurrentTransactionId();
}
/* ready to change the on-disk (or really, in-buffer) tuple */
START_CRIT_SECTION();
@ -1015,7 +837,6 @@ nextval_internal(Oid relid, bool check_permissions)
seq->log_cnt = 0;
xlrec.node = seqrel->rd_node;
xlrec.created = false;
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
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. */
if (RelationNeedsWAL(seqrel))
{
GetTopTransactionId();
if (XLogLogicalInfoActive())
GetCurrentTransactionId();
}
/* ready to change the on-disk (or really, in-buffer) tuple */
START_CRIT_SECTION();
@ -1208,8 +1024,6 @@ do_setval(Oid relid, int64 next, bool iscalled)
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);

View File

@ -90,7 +90,6 @@ typedef struct SubOpts
} SubOpts;
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 List *merge_publications(List *oldpublist, List *newpublist, bool addpub, const char *subname);
static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err);
@ -639,9 +638,9 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
{
char *err;
WalReceiverConn *wrconn;
List *relations;
List *tables;
ListCell *lc;
char sync_state;
char table_state;
/* Try to connect to the publisher. */
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
* 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
* local relation sync status info.
* Get the table list from publisher and build local table status
* info.
*/
relations = fetch_table_list(wrconn, publications);
relations = list_concat(relations,
fetch_sequence_list(wrconn, publications));
foreach(lc, relations)
tables = fetch_table_list(wrconn, publications);
foreach(lc, tables)
{
RangeVar *rv = (RangeVar *) lfirst(lc);
Oid relid;
@ -679,7 +675,7 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
CheckSubscriptionRelkind(get_rel_relkind(relid),
rv->schemaname, rv->relname);
AddSubscriptionRelState(subid, relid, sync_state,
AddSubscriptionRelState(subid, relid, table_state,
InvalidXLogRecPtr);
}
@ -705,12 +701,12 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
*
* Note that if tables were specified but copy_data is false
* then it is safe to enable two_phase up-front because those
* relations are already initially in READY state. When the
* subscription has no relations, we leave the twophase state
* as PENDING, to allow ALTER SUBSCRIPTION ... REFRESH
* tables are already initially in READY state. When the
* subscription has no tables, we leave the twophase state as
* PENDING, to allow ALTER SUBSCRIPTION ... REFRESH
* PUBLICATION to work.
*/
if (opts.twophase && !opts.copy_data && relations != NIL)
if (opts.twophase && !opts.copy_data && tables != NIL)
twophase_enabled = true;
walrcv_create_slot(wrconn, opts.slot_name, false, twophase_enabled,
@ -786,10 +782,8 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data,
if (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 = list_concat(pubrel_names,
fetch_sequence_list(wrconn, sub->publications));
/* Get local table list. */
subrel_states = GetSubscriptionRelations(sub->oid);
@ -1813,75 +1807,6 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications)
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.
* 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_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_tablespace.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
* publication having both a schema and the same schema's table, as this
* 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)
{
ListCell *lc;
List *schemaPubids = GetSchemaPublications(nspOid, PUB_OBJTYPE_TABLE);
List *schemaPubids = GetSchemaPublications(nspOid);
List *relPubids = GetRelationPublications(RelationGetRelid(rel));
foreach(lc, relPubids)
@ -16434,27 +16430,6 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
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 */
CheckSetNamespace(oldNspOid, nspOid);

View File

@ -649,9 +649,7 @@ void
CheckSubscriptionRelkind(char relkind, const char *nspname,
const char *relname)
{
if (relkind != RELKIND_RELATION &&
relkind != RELKIND_PARTITIONED_TABLE &&
relkind != RELKIND_SEQUENCE)
if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
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_NODE_FIELD(options);
COPY_NODE_FIELD(pubobjects);
COPY_NODE_FIELD(for_all_objects);
COPY_SCALAR_FIELD(for_all_tables);
return newnode;
}
@ -5403,7 +5403,7 @@ _copyAlterPublicationStmt(const AlterPublicationStmt *from)
COPY_STRING_FIELD(pubname);
COPY_NODE_FIELD(options);
COPY_NODE_FIELD(pubobjects);
COPY_NODE_FIELD(for_all_objects);
COPY_SCALAR_FIELD(for_all_tables);
COPY_SCALAR_FIELD(action);
return newnode;

View File

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

View File

@ -455,7 +455,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
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 <groupclause> group_clause
@ -588,7 +588,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> var_value zone_value
%type <rolespec> auth_ident RoleSpec opt_granted_by
%type <publicationobjectspec> PublicationObjSpec
%type <node> pub_obj_type
%type <keyword> unreserved_keyword type_func_name_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 SEQUENCES [WITH options]
*
* CREATE PUBLICATION FOR pub_obj [, ...] [WITH options]
*
* pub_obj is one of:
*
* TABLE table [, ...]
* ALL TABLES IN SCHEMA schema [, ...]
*
*****************************************************************************/
CreatePublicationStmt:
@ -9877,12 +9879,12 @@ CreatePublicationStmt:
n->options = $4;
$$ = (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);
n->pubname = $3;
n->options = $7;
n->for_all_objects = $6;
n->for_all_tables = true;
$$ = (Node *)n;
}
| CREATE PUBLICATION name FOR pub_obj_list opt_definition
@ -9932,26 +9934,6 @@ PublicationObjSpec:
$$->pubobjtype = PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA;
$$->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
{
$$ = makeNode(PublicationObjSpec);
@ -10013,19 +9995,6 @@ pub_obj_list: PublicationObjSpec
{ $$ = 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 )
@ -10036,6 +10005,11 @@ pub_obj_type_list: pub_obj_type
*
* ALTER PUBLICATION name SET pub_obj [, ...]
*
* pub_obj is one of:
*
* TABLE table_name [, ...]
* ALL TABLES IN SCHEMA schema_name [, ...]
*
*****************************************************************************/
AlterPublicationStmt:
@ -18757,8 +18731,7 @@ preprocess_pubobj_list(List *pubobjspec_list, core_yyscan_t yyscanner)
if (pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION)
pubobj->pubobjtype = prevobjtype;
if (pubobj->pubobjtype == PUBLICATIONOBJ_TABLE ||
pubobj->pubobjtype == PUBLICATIONOBJ_SEQUENCE)
if (pubobj->pubobjtype == PUBLICATIONOBJ_TABLE)
{
/* relation name or pubtable must be set for this type of object */
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"),
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;
}

View File

@ -42,7 +42,6 @@
#include "replication/reorderbuffer.h"
#include "replication/snapbuild.h"
#include "storage/standby.h"
#include "commands/sequence.h"
/* individual record(group)'s handlers */
static void DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
@ -64,7 +63,6 @@ static void DecodePrepare(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
/* common function to decode tuples */
static void DecodeXLogTuple(char *data, Size len, ReorderBufferTupleBuf *tup);
static void DecodeSeqTuple(char *data, Size len, ReorderBufferTupleBuf *tuple);
/* helper functions for decoding transactions */
static inline bool FilterPrepare(LogicalDecodingContext *ctx,
@ -1252,132 +1250,3 @@ DecodeTXNNeedSkip(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
(txn_dbid != InvalidOid && txn_dbid != ctx->slot->data.database) ||
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,
XLogRecPtr message_lsn, bool transactional,
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 */
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,
XLogRecPtr message_lsn, bool transactional,
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,
int nrelations, Relation relations[], ReorderBufferChange *change);
@ -226,7 +218,6 @@ StartupDecodingContext(List *output_plugin_options,
ctx->reorder->apply_truncate = truncate_cb_wrapper;
ctx->reorder->commit = commit_cb_wrapper;
ctx->reorder->message = message_cb_wrapper;
ctx->reorder->sequence = sequence_cb_wrapper;
/*
* 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_change_cb != NULL) ||
(ctx->callbacks.stream_message_cb != NULL) ||
(ctx->callbacks.stream_sequence_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_change = stream_change_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;
@ -1216,42 +1205,6 @@ message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
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
stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
XLogRecPtr first_lsn)
@ -1557,47 +1510,6 @@ stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
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
stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
int nrelations, Relation relations[],

View File

@ -662,56 +662,6 @@ logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
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.
*/
@ -1286,8 +1236,6 @@ logicalrep_message_type(LogicalRepMsgType action)
return "STREAM ABORT";
case LOGICAL_REP_MSG_STREAM_PREPARE:
return "STREAM PREPARE";
case LOGICAL_REP_MSG_SEQUENCE:
return "SEQUENCE";
}
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
* 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"
@ -125,7 +91,6 @@
#include "access/xact.h"
#include "access/xlog_internal.h"
#include "catalog/catalog.h"
#include "commands/sequence.h"
#include "lib/binaryheap.h"
#include "miscadmin.h"
#include "pgstat.h"
@ -151,13 +116,6 @@ typedef struct ReorderBufferTXNByIdEnt
ReorderBufferTXN *txn;
} 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 */
typedef struct ReorderBufferTupleCidKey
{
@ -388,14 +346,6 @@ ReorderBufferAllocate(void)
buffer->by_txn = hash_create("ReorderBufferByXid", 1000, &hash_ctl,
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_txn = NULL;
@ -582,13 +532,6 @@ ReorderBufferReturnChange(ReorderBuffer *rb, ReorderBufferChange *change,
change->data.truncate.relids = NULL;
}
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_ABORT:
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
* Verify LSN ordering of transaction lists in the reorderbuffer
@ -1823,9 +1542,6 @@ ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
&found);
Assert(found);
/* Remove sequences created in this transaction (if any). */
ReorderBufferSequenceCleanup(rb, txn->xid);
/* remove entries spilled to disk */
if (rbtxn_is_serialized(txn))
ReorderBufferRestoreCleanup(rb, txn);
@ -2241,29 +1957,6 @@ ReorderBufferApplyMessage(ReorderBuffer *rb, ReorderBufferTXN *txn,
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
* 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:
elog(ERROR, "tuplecid value in changequeue");
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);
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;
}
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
@ -4412,22 +4047,6 @@ ReorderBufferChangeSize(ReorderBufferChange *change)
{
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;
}
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
@ -4729,30 +4348,6 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
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_ABORT:
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:

View File

@ -100,7 +100,6 @@
#include "catalog/pg_subscription_rel.h"
#include "catalog/pg_type.h"
#include "commands/copy.h"
#include "commands/sequence.h"
#include "miscadmin.h"
#include "parser/parse_relation.h"
#include "pgstat.h"
@ -1137,95 +1136,6 @@ copy_table(Relation rel)
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.
*
@ -1487,21 +1397,10 @@ LogicalRepSyncTableStart(XLogRecPtr *origin_startpos)
originname)));
}
/* Do the right action depending on the relation kind. */
if (get_rel_relkind(RelationGetRelid(rel)) == RELKIND_SEQUENCE)
{
/* Now do the initial sequence copy */
PushActiveSnapshot(GetTransactionSnapshot());
copy_sequence(rel);
PopActiveSnapshot();
}
else
{
/* Now do the initial data copy */
PushActiveSnapshot(GetTransactionSnapshot());
copy_table(rel);
PopActiveSnapshot();
}
/* Now do the initial data copy */
PushActiveSnapshot(GetTransactionSnapshot());
copy_table(rel);
PopActiveSnapshot();
res = walrcv_exec(LogRepWorkerWalRcvConn, "COMMIT", 0, NULL);
if (res->status != WALRCV_OK_COMMAND)

View File

@ -143,7 +143,6 @@
#include "catalog/pg_subscription.h"
#include "catalog/pg_subscription_rel.h"
#include "catalog/pg_tablespace.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
@ -1144,57 +1143,6 @@ apply_handle_origin(StringInfo s)
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.
*/
@ -2563,10 +2511,6 @@ apply_dispatch(StringInfo s)
*/
break;
case LOGICAL_REP_MSG_SEQUENCE:
apply_handle_sequence(s);
return;
case LOGICAL_REP_MSG_STREAM_START:
apply_handle_stream_start(s);
break;

View File

@ -15,7 +15,6 @@
#include "access/tupconvert.h"
#include "catalog/partition.h"
#include "catalog/pg_publication.h"
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_publication_rel.h"
#include "commands/defrem.h"
#include "executor/executor.h"
@ -55,10 +54,6 @@ static void pgoutput_message(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, XLogRecPtr message_lsn,
bool transactional, const char *prefix,
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,
RepOriginId origin_id);
static void pgoutput_begin_prepare_txn(LogicalDecodingContext *ctx,
@ -260,7 +255,6 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
cb->change_cb = pgoutput_change;
cb->truncate_cb = pgoutput_truncate;
cb->message_cb = pgoutput_message;
cb->sequence_cb = pgoutput_sequence;
cb->commit_cb = pgoutput_commit_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_change_cb = pgoutput_change;
cb->stream_message_cb = pgoutput_message;
cb->stream_sequence_cb = pgoutput_sequence;
cb->stream_truncate_cb = pgoutput_truncate;
/* transaction streaming - two-phase commit */
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 binary_option_given = false;
bool messages_option_given = false;
bool sequences_option_given = false;
bool streaming_given = false;
bool two_phase_option_given = false;
@ -299,7 +291,6 @@ parse_output_parameters(List *options, PGOutputData *data)
data->streaming = false;
data->messages = false;
data->two_phase = false;
data->sequences = true;
foreach(lc, options)
{
@ -368,16 +359,6 @@ parse_output_parameters(List *options, PGOutputData *data)
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)
{
if (streaming_given)
@ -1709,64 +1690,6 @@ pgoutput_message(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
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.
*/
@ -2052,8 +1975,7 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
entry->schema_sent = false;
entry->streamed_txns = NIL;
entry->pubactions.pubinsert = entry->pubactions.pubupdate =
entry->pubactions.pubdelete = entry->pubactions.pubtruncate =
entry->pubactions.pubsequence = false;
entry->pubactions.pubdelete = entry->pubactions.pubtruncate = false;
entry->new_slot = NULL;
entry->old_slot = NULL;
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);
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
* the cache entry using a historic snapshot and all the later changes
* are absorbed while decoding WAL.
*/
List *schemaPubids = GetSchemaPublications(schemaId, objectType);
List *schemaPubids = GetSchemaPublications(schemaId);
ListCell *lc;
Oid publish_as_relid = relid;
int publish_ancestor_level = 0;
bool am_partition = get_rel_relispartition(relid);
char relkind = get_rel_relkind(relid);
List *rel_publications = NIL;
/* Reload publications if needed before use. */
@ -2111,7 +2033,6 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
entry->pubactions.pubupdate = false;
entry->pubactions.pubdelete = false;
entry->pubactions.pubtruncate = false;
entry->pubactions.pubsequence = false;
/*
* 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
* and set the ancestor level accordingly. If this is a FOR ALL
* SEQUENCES publication, we publish it too but we don't need to
* pick the partition root etc.
* and set the ancestor level accordingly.
*/
if (pub->alltables || pub->allsequences)
if (pub->alltables)
{
publish = true;
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.pubdelete |= pub->pubactions.pubdelete;
entry->pubactions.pubtruncate |= pub->pubactions.pubtruncate;
entry->pubactions.pubsequence |= pub->pubactions.pubsequence;
/*
* 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_proc.h"
#include "catalog/pg_publication.h"
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_shseclabel.h"
#include "catalog/pg_statistic_ext.h"
@ -5568,8 +5567,6 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
Oid schemaid;
List *ancestors = NIL;
Oid relid = RelationGetRelid(relation);
char relkind = relation->rd_rel->relkind;
char objType;
/*
* If not publishable, it publishes no actions. (pgoutput_change() will
@ -5600,15 +5597,8 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
/* Fetch the publication membership info. */
puboids = GetRelationPublications(relid);
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)
{
/* Add publications that the ancestors are in too. */
@ -5620,23 +5610,12 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
puboids = list_concat_unique_oid(puboids,
GetRelationPublications(ancestor));
/* include all publications publishing schema of all ancestors */
schemaid = get_rel_namespace(ancestor);
puboids = list_concat_unique_oid(puboids,
GetSchemaPublications(schemaid,
PUB_OBJTYPE_TABLE));
GetSchemaPublications(schemaid));
}
}
/*
* 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());
puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
foreach(lc, puboids)
{
@ -5655,7 +5634,6 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
pubdesc->pubactions.pubupdate |= pubform->pubupdate;
pubdesc->pubactions.pubdelete |= pubform->pubdelete;
pubdesc->pubactions.pubtruncate |= pubform->pubtruncate;
pubdesc->pubactions.pubsequence |= pubform->pubsequence;
/*
* Check if all columns referenced in the filter expression are part of

View File

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

View File

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

View File

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

View File

@ -2420,7 +2420,7 @@ my %tests = (
create_order => 50,
create_sql => 'CREATE PUBLICATION pub1;',
regexp => qr/^
\QCREATE PUBLICATION pub1 WITH (publish = 'insert, update, delete, truncate, sequence');\E
\QCREATE PUBLICATION pub1 WITH (publish = 'insert, update, delete, truncate');\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
@ -2440,27 +2440,16 @@ my %tests = (
create_order => 50,
create_sql => 'CREATE PUBLICATION pub3;',
regexp => qr/^
\QCREATE PUBLICATION pub3 WITH (publish = 'insert, update, delete, truncate, sequence');\E
\QCREATE PUBLICATION pub3 WITH (publish = 'insert, update, delete, truncate');\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
'CREATE PUBLICATION pub4' => {
create_order => 50,
create_sql => 'CREATE PUBLICATION pub4
FOR ALL SEQUENCES
WITH (publish = \'\');',
create_sql => 'CREATE PUBLICATION pub4;',
regexp => qr/^
\QCREATE PUBLICATION pub4 FOR ALL SEQUENCES WITH (publish = '');\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
\QCREATE PUBLICATION pub4 WITH (publish = 'insert, update, delete, truncate');\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
@ -2569,27 +2558,6 @@ my %tests = (
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' => {
regexp => qr/^CREATE SCHEMA public;/m,

View File

@ -1633,19 +1633,28 @@ describeOneTableDetails(const char *schemaname,
if (tableinfo.relkind == RELKIND_SEQUENCE)
{
PGresult *result = NULL;
printQueryOpt myopt = pset.popt;
char *footers[2] = {NULL, NULL};
if (pset.sversion >= 100000)
{
printfPQExpBuffer(&buf,
"SELECT pg_catalog.format_type(seqtypid, NULL),\n"
" seqstart,\n"
" seqmin,\n"
" seqmax,\n"
" seqincrement,\n"
" CASE WHEN seqcycle THEN '%s' ELSE '%s' END,\n"
" seqcache\n",
"SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
" seqstart AS \"%s\",\n"
" seqmin AS \"%s\",\n"
" seqmax AS \"%s\",\n"
" seqincrement AS \"%s\",\n"
" CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\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("no"));
gettext_noop("no"),
gettext_noop("Cycles?"),
gettext_noop("Cache"));
appendPQExpBuffer(&buf,
"FROM pg_catalog.pg_sequence\n"
"WHERE seqrelid = '%s';",
@ -1654,15 +1663,22 @@ describeOneTableDetails(const char *schemaname,
else
{
printfPQExpBuffer(&buf,
"SELECT 'bigint',\n"
" start_value,\n"
" min_value,\n"
" max_value,\n"
" increment_by,\n"
" CASE WHEN is_cycled THEN '%s' ELSE '%s' END,\n"
" cache_value\n",
"SELECT 'bigint' AS \"%s\",\n"
" start_value AS \"%s\",\n"
" min_value AS \"%s\",\n"
" max_value AS \"%s\",\n"
" increment_by AS \"%s\",\n"
" CASE WHEN is_cycled THEN '%s' ELSE '%s' END AS \"%s\",\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("no"));
gettext_noop("no"),
gettext_noop("Cycles?"),
gettext_noop("Cache"));
appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname));
/* must be separate because fmtId isn't reentrant */
appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
@ -1672,57 +1688,6 @@ describeOneTableDetails(const char *schemaname,
if (!res)
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 */
printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
"\n pg_catalog.quote_ident(relname) || '.' ||"
@ -1754,63 +1719,33 @@ describeOneTableDetails(const char *schemaname,
switch (PQgetvalue(result, 0, 1)[0])
{
case 'a':
printTableAddFooter(&cont,
psprintf(_("Owned by: %s"),
PQgetvalue(result, 0, 0)));
footers[0] = psprintf(_("Owned by: %s"),
PQgetvalue(result, 0, 0));
break;
case 'i':
printTableAddFooter(&cont,
psprintf(_("Sequence for identity column: %s"),
PQgetvalue(result, 0, 0)));
footers[0] = psprintf(_("Sequence for identity column: %s"),
PQgetvalue(result, 0, 0));
break;
}
}
PQclear(result);
/* print any publications */
if (pset.sversion >= 150000)
{
int tuples = 0;
if (tableinfo.relpersistence == 'u')
printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
schemaname, relationname);
else
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
schemaname, relationname);
printfPQExpBuffer(&buf,
"SELECT pubname\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_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);
myopt.footers = footers;
myopt.topt.default_footer = false;
myopt.title = title.data;
myopt.translate_header = true;
result = PSQLexec(buf.data);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
if (tuples > 0)
printTableAddFooter(&cont, _("Publications:"));
/* 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);
if (footers[0])
free(footers[0]);
retval = true;
goto error_return; /* not an error, just return early */
@ -2037,11 +1972,6 @@ describeOneTableDetails(const char *schemaname,
for (i = 0; i < cols; i++)
printTableAddHeader(&cont, headers[i], true, 'l');
res = PSQLexec(buf.data);
if (!res)
goto error_return;
numrows = PQntuples(res);
/* Generate table cells to be printed */
for (i = 0; i < numrows; i++)
{
@ -2968,7 +2898,7 @@ describeOneTableDetails(const char *schemaname,
"FROM pg_catalog.pg_publication p\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"
"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"
"SELECT pubname\n"
" , pg_get_expr(pr.prqual, c.oid)\n"
@ -4872,7 +4802,7 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
int i;
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"
" 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"
@ -4901,9 +4831,8 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
/* Might be an empty set - that's ok */
for (i = 0; i < pub_schema_tuples; i++)
{
printfPQExpBuffer(&buf, " \"%s\" (%s)",
PQgetvalue(result, i, 0),
PQgetvalue(result, i, 1));
printfPQExpBuffer(&buf, " \"%s\"",
PQgetvalue(result, i, 0));
footers[i + 1] = pg_strdup(buf.data);
}
@ -5908,7 +5837,7 @@ listPublications(const char *pattern)
PQExpBufferData buf;
PGresult *res;
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)
{
@ -5922,45 +5851,23 @@ listPublications(const char *pattern)
initPQExpBuffer(&buf);
if (pset.sversion >= 150000)
printfPQExpBuffer(&buf,
"SELECT pubname AS \"%s\",\n"
" pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
" puballtables AS \"%s\",\n"
" puballsequences 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("All sequences"),
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"));
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)
appendPQExpBuffer(&buf,
",\n pubtruncate AS \"%s\"",
gettext_noop("Truncates"));
if (pset.sversion >= 150000)
appendPQExpBuffer(&buf,
",\n pubsequence AS \"%s\"",
gettext_noop("Sequences"));
if (pset.sversion >= 130000)
appendPQExpBuffer(&buf,
",\n pubviaroot AS \"%s\"",
@ -6050,7 +5957,6 @@ describePublications(const char *pattern)
PGresult *res;
bool has_pubtruncate;
bool has_pubviaroot;
bool has_pubsequence;
PQExpBufferData title;
printTableContent cont;
@ -6067,7 +5973,6 @@ describePublications(const char *pattern)
has_pubtruncate = (pset.sversion >= 110000);
has_pubviaroot = (pset.sversion >= 130000);
has_pubsequence = (pset.sversion >= 150000);
initPQExpBuffer(&buf);
@ -6075,17 +5980,12 @@ describePublications(const char *pattern)
"SELECT oid, pubname,\n"
" pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
" puballtables, pubinsert, pubupdate, pubdelete");
if (has_pubtruncate)
appendPQExpBufferStr(&buf,
", pubtruncate");
if (has_pubviaroot)
appendPQExpBufferStr(&buf,
", pubviaroot");
if (has_pubsequence)
appendPQExpBufferStr(&buf,
", puballsequences, pubsequence");
appendPQExpBufferStr(&buf,
"\nFROM pg_catalog.pg_publication\n");
@ -6126,7 +6026,6 @@ describePublications(const char *pattern)
char *pubid = PQgetvalue(res, i, 0);
char *pubname = PQgetvalue(res, i, 1);
bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
bool puballsequences = strcmp(PQgetvalue(res, i, 9), "t") == 0;
printTableOpt myopt = pset.popt.topt;
if (has_pubtruncate)
@ -6134,43 +6033,29 @@ describePublications(const char *pattern)
if (has_pubviaroot)
ncols++;
/* sequences have two extra columns (puballsequences, pubsequences) */
if (has_pubsequence)
ncols += 2;
initPQExpBuffer(&title);
printfPQExpBuffer(&title, _("Publication %s"), pubname);
printTableInit(&cont, &myopt, title.data, ncols, nrows);
printTableAddHeader(&cont, gettext_noop("Owner"), 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("Updates"), true, align);
printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
if (has_pubtruncate)
printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
if (has_pubsequence)
printTableAddHeader(&cont, gettext_noop("Sequences"), true, align);
if (has_pubviaroot)
printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false); /* owner */
printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false); /* all tables */
if (has_pubsequence)
printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false); /* all sequences */
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 */
printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
if (has_pubtruncate)
printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false); /* truncate */
if (has_pubsequence)
printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false); /* sequence */
printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
if (has_pubviaroot)
printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false); /* via root */
printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
if (!puballtables)
{
@ -6201,7 +6086,6 @@ describePublications(const char *pattern)
"WHERE c.relnamespace = n.oid\n"
" AND c.oid = pr.prrelid\n"
" AND pr.prpubid = '%s'\n"
" AND c.relkind != 'S'\n" /* exclude sequences */
"ORDER BY 1,2", pubid);
if (!addFooterToPublicationDesc(&buf, "Tables:", false, &cont))
goto error_return;
@ -6213,7 +6097,7 @@ describePublications(const char *pattern)
"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 = 't'\n"
"WHERE pn.pnpubid = '%s'\n"
"ORDER BY 1", pubid);
if (!addFooterToPublicationDesc(&buf, "Tables from schemas:",
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);
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");
/* ALTER PUBLICATION <name> 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") ||
(HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
ends_with(prev_wd, ',')))
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
* table attributes
@ -1854,11 +1850,11 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH(",");
/* ALTER PUBLICATION <name> 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 */
else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET"))
COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "ALL SEQUENCES IN SCHEMA", "TABLE", "SEQUENCE");
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES|SEQUENCES", "IN", "SCHEMA"))
COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "TABLE");
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES", "IN", "SCHEMA"))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
" AND nspname NOT LIKE E'pg\\\\_%%'",
"CURRENT_SCHEMA");
@ -2988,27 +2984,21 @@ psql_completion(const char *text, int start, int end)
/* CREATE PUBLICATION */
else if (Matches("CREATE", "PUBLICATION", MatchAny))
COMPLETE_WITH("FOR TABLE", "FOR ALL TABLES", "FOR ALL TABLES IN SCHEMA",
"FOR SEQUENCE", "FOR ALL SEQUENCES", "FOR ALL SEQUENCES IN SCHEMA",
"WITH (");
COMPLETE_WITH("FOR TABLE", "FOR ALL TABLES", "FOR ALL TABLES IN SCHEMA", "WITH (");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR"))
COMPLETE_WITH("TABLE", "ALL TABLES", "ALL TABLES IN SCHEMA",
"SEQUENCE", "ALL SEQUENCES", "ALL SEQUENCES IN SCHEMA");
COMPLETE_WITH("TABLE", "ALL TABLES", "ALL TABLES IN SCHEMA");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL"))
COMPLETE_WITH("TABLES", "TABLES IN SCHEMA", "SEQUENCES", "SEQUENCES IN SCHEMA");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES|SEQUENCES"))
COMPLETE_WITH("TABLES", "TABLES IN SCHEMA");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES"))
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 "CREATE PUBLICATION <name> FOR TABLE" with "<table>, ..." */
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE"))
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
*/
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 "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
" AND nspname NOT LIKE E'pg\\\\_%%'",
"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 "CREATE PUBLICATION <name> [...] 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_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_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_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)

View File

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

View File

@ -11658,11 +11658,6 @@
provolatile => 's', prorettype => 'oid', proargtypes => 'text',
proallargtypes => '{text,oid}', proargmodes => '{i,o}',
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',
descr => 'returns whether a relation can be part of a publication',
proname => 'pg_relation_is_publishable', provolatile => 's',

View File

@ -40,12 +40,6 @@ CATALOG(pg_publication,6104,PublicationRelationId)
*/
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 */
bool pubinsert;
@ -58,9 +52,6 @@ CATALOG(pg_publication,6104,PublicationRelationId)
/* true if truncates are published */
bool pubtruncate;
/* true if sequences are published */
bool pubsequence;
/* true if partition changes are published using root schema */
bool pubviaroot;
} FormData_pg_publication;
@ -81,7 +72,6 @@ typedef struct PublicationActions
bool pubupdate;
bool pubdelete;
bool pubtruncate;
bool pubsequence;
} PublicationActions;
typedef struct PublicationDesc
@ -109,7 +99,6 @@ typedef struct Publication
Oid oid;
char *name;
bool alltables;
bool allsequences;
bool pubviaroot;
PublicationActions pubactions;
} Publication;
@ -141,16 +130,14 @@ typedef enum PublicationPartOpt
PUBLICATION_PART_ALL,
} PublicationPartOpt;
extern List *GetPublicationRelations(Oid pubid, char objectType,
PublicationPartOpt pub_partopt);
extern List *GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt);
extern List *GetAllTablesPublications(void);
extern List *GetAllTablesPublicationRelations(bool pubviaroot);
extern void GetActionsInPublication(Oid pubid, PublicationActions *actions);
extern List *GetPublicationSchemas(Oid pubid, char objectType);
extern List *GetSchemaPublications(Oid schemaid, char objectType);
extern List *GetSchemaPublicationRelations(Oid schemaid, char objectType,
extern List *GetPublicationSchemas(Oid pubid);
extern List *GetSchemaPublications(Oid schemaid);
extern List *GetSchemaPublicationRelations(Oid schemaid,
PublicationPartOpt pub_partopt);
extern List *GetAllSchemaPublicationRelations(Oid puboid, char objectType,
extern List *GetAllSchemaPublicationRelations(Oid puboid,
PublicationPartOpt pub_partopt);
extern List *GetPubPartitionOptionRelations(List *result,
PublicationPartOpt pub_partopt,
@ -158,15 +145,11 @@ extern List *GetPubPartitionOptionRelations(List *result,
extern Oid GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
int *ancestor_level);
extern List *GetAllSequencesPublications(void);
extern List *GetAllSequencesPublicationRelations(void);
extern bool is_publishable_relation(Relation rel);
extern bool is_schema_publication(Oid pubid);
extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri,
bool if_not_exists);
extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid,
char objectType,
bool if_not_exists);
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 pnpubid BKI_LOOKUP(pg_publication); /* Oid of the publication */
Oid pnnspid BKI_LOOKUP(pg_namespace); /* Oid of the schema */
char pntype; /* object type to include */
} FormData_pg_publication_namespace;
/* ----------------
@ -43,13 +42,6 @@ CATALOG(pg_publication_namespace,8901,PublicationNamespaceRelationId)
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(pg_publication_namespace_pnnspid_pnpubid_pntype_index, 8903, PublicationNamespacePnnspidPnpubidPntypeIndexId, on pg_publication_namespace using btree(pnnspid oid_ops, pnpubid oid_ops, pntype char_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);
DECLARE_UNIQUE_INDEX(pg_publication_namespace_pnnspid_pnpubid_index, 8903, PublicationNamespacePnnspidPnpubidIndexId, on pg_publication_namespace using btree(pnnspid oid_ops, pnpubid oid_ops));
#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
{
RelFileNode node;
bool created; /* creates a new relfilenode (CREATE/ALTER) */
/* SEQUENCE TUPLE DATA FOLLOWS AT THE END */
} xl_seq_rec;
@ -61,7 +60,6 @@ extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
extern void SequenceChangePersistence(Oid relid, char newrelpersistence);
extern void DeleteSequenceTuple(Oid 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 seq_redo(XLogReaderState *rptr);

View File

@ -4013,10 +4013,6 @@ typedef enum PublicationObjSpecType
PUBLICATIONOBJ_TABLES_IN_SCHEMA, /* All tables in schema */
PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA, /* All tables in first element of
* 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 */
} PublicationObjSpecType;
@ -4035,7 +4031,7 @@ typedef struct CreatePublicationStmt
char *pubname; /* Name of the publication */
List *options; /* List of DefElem nodes */
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;
typedef enum AlterPublicationAction
@ -4058,7 +4054,7 @@ typedef struct AlterPublicationStmt
* 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
* objects */
} 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 standby_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,
XLogReaderState *record);

View File

@ -61,7 +61,6 @@ typedef enum LogicalRepMsgType
LOGICAL_REP_MSG_RELATION = 'R',
LOGICAL_REP_MSG_TYPE = 'Y',
LOGICAL_REP_MSG_MESSAGE = 'M',
LOGICAL_REP_MSG_SEQUENCE = 'Q',
LOGICAL_REP_MSG_BEGIN_PREPARE = 'b',
LOGICAL_REP_MSG_PREPARE = 'P',
LOGICAL_REP_MSG_COMMIT_PREPARED = 'K',
@ -119,18 +118,6 @@ typedef struct LogicalRepTyp
char *typname; /* name of the remote type */
} 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 */
typedef struct LogicalRepBeginData
{
@ -243,12 +230,6 @@ extern List *logicalrep_read_truncate(StringInfo in,
bool *cascade, bool *restart_seqs);
extern void logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
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,
Relation rel, Bitmapset *columns);
extern LogicalRepRelation *logicalrep_read_rel(StringInfo in);

View File

@ -88,18 +88,6 @@ typedef void (*LogicalDecodeMessageCB) (struct LogicalDecodingContext *ctx,
Size message_size,
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.
*/
@ -211,19 +199,6 @@ typedef void (*LogicalDecodeStreamMessageCB) (struct LogicalDecodingContext *ctx
Size message_size,
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.
*/
@ -244,7 +219,6 @@ typedef struct OutputPluginCallbacks
LogicalDecodeTruncateCB truncate_cb;
LogicalDecodeCommitCB commit_cb;
LogicalDecodeMessageCB message_cb;
LogicalDecodeSequenceCB sequence_cb;
LogicalDecodeFilterByOriginCB filter_by_origin_cb;
LogicalDecodeShutdownCB shutdown_cb;
@ -263,7 +237,6 @@ typedef struct OutputPluginCallbacks
LogicalDecodeStreamCommitCB stream_commit_cb;
LogicalDecodeStreamChangeCB stream_change_cb;
LogicalDecodeStreamMessageCB stream_message_cb;
LogicalDecodeStreamSequenceCB stream_sequence_cb;
LogicalDecodeStreamTruncateCB stream_truncate_cb;
} OutputPluginCallbacks;

View File

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

View File

@ -64,8 +64,7 @@ typedef enum ReorderBufferChangeType
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT,
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM,
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT,
REORDER_BUFFER_CHANGE_TRUNCATE,
REORDER_BUFFER_CHANGE_SEQUENCE
REORDER_BUFFER_CHANGE_TRUNCATE
} ReorderBufferChangeType;
/* forward declaration */
@ -159,13 +158,6 @@ typedef struct ReorderBufferChange
uint32 ninvalidations; /* Number of messages */
SharedInvalidationMessage *invalidations; /* invalidation message */
} inval;
/* Context data for Sequence changes */
struct
{
RelFileNode relnode;
ReorderBufferTupleBuf *tuple;
} sequence;
} data;
/*
@ -438,15 +430,6 @@ typedef void (*ReorderBufferMessageCB) (ReorderBuffer *rb,
const char *prefix, Size sz,
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 */
typedef void (*ReorderBufferBeginPrepareCB) (ReorderBuffer *rb,
ReorderBufferTXN *txn);
@ -513,15 +496,6 @@ typedef void (*ReorderBufferStreamMessageCB) (
const char *prefix, Size sz,
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 */
typedef void (*ReorderBufferStreamTruncateCB) (
ReorderBuffer *rb,
@ -537,12 +511,6 @@ struct ReorderBuffer
*/
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
* record bearing that xid.
@ -573,7 +541,6 @@ struct ReorderBuffer
ReorderBufferApplyTruncateCB apply_truncate;
ReorderBufferCommitCB commit;
ReorderBufferMessageCB message;
ReorderBufferSequenceCB sequence;
/*
* Callbacks to be called when streaming a transaction at prepare time.
@ -593,7 +560,6 @@ struct ReorderBuffer
ReorderBufferStreamCommitCB stream_commit;
ReorderBufferStreamChangeCB stream_change;
ReorderBufferStreamMessageCB stream_message;
ReorderBufferStreamSequenceCB stream_sequence;
ReorderBufferStreamTruncateCB stream_truncate;
/*
@ -669,10 +635,6 @@ void ReorderBufferQueueChange(ReorderBuffer *, TransactionId,
void ReorderBufferQueueMessage(ReorderBuffer *, TransactionId, Snapshot snapshot, XLogRecPtr lsn,
bool transactional, const char *prefix,
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,
XLogRecPtr commit_lsn, XLogRecPtr end_lsn,
TimestampTz commit_time, RepOriginId origin_id, XLogRecPtr origin_lsn);
@ -720,7 +682,4 @@ void ReorderBufferSetRestartPoint(ReorderBuffer *, XLogRecPtr ptr);
void StartupReorderBuffer(void);
bool ReorderBufferSequenceIsTransactional(ReorderBuffer *rb,
RelFileNode rnode, bool created);
#endif

View File

@ -46,7 +46,6 @@ CREATE TRANSFORM FOR int LANGUAGE SQL (
SET client_min_messages = 'ERROR';
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_schema2 FOR ALL SEQUENCES IN SCHEMA addr_nsp;
RESET client_min_messages;
CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE);
WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables
@ -429,8 +428,7 @@ WITH objects (type, name, args) AS (VALUES
('transform', '{int}', '{sql}'),
('access method', '{btree}', '{}'),
('publication', '{addr_pub}', '{}'),
('publication namespace', '{addr_nsp}', '{addr_pub_schema,t}'),
('publication namespace', '{addr_nsp}', '{addr_pub_schema2,s}'),
('publication namespace', '{addr_nsp}', '{addr_pub_schema}'),
('publication relation', '{addr_nsp, gentable}', '{addr_pub}'),
('subscription', '{regress_addr_sub}', '{}'),
('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
publication | | addr_pub | 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_schema2 type s | t
(51 rows)
publication namespace | | | addr_nsp in publication addr_pub_schema | t
(50 rows)
---
--- Cleanup resources
@ -509,7 +506,6 @@ drop cascades to server integer
drop cascades to user mapping for regress_addr_user on server integer
DROP PUBLICATION addr_pub;
DROP PUBLICATION addr_pub_schema;
DROP PUBLICATION addr_pub_schema2;
DROP SUBSCRIPTION regress_addr_sub;
DROP SCHEMA addr_nsp CASCADE;
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)
LEFT JOIN pg_authid u ON ((p.ownerid = u.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,
n.nspname AS schemaname,
c.relname AS tablename

View File

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

View File

@ -27,7 +27,7 @@ CREATE PUBLICATION testpub_xxx WITH (publish_via_partition_root = 'true', publis
\dRp
ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete, sequence');
ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete');
\dRp
@ -46,8 +46,6 @@ ALTER PUBLICATION testpub_foralltables SET (publish = 'insert, update');
CREATE TABLE testpub_tbl2 (id serial primary key, data text);
-- fail - can't add to for all tables publication
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
ALTER PUBLICATION testpub_foralltables DROP TABLE testpub_tbl2;
-- fail - can't add to for all tables publication
@ -106,199 +104,6 @@ RESET client_min_messages;
DROP TABLE testpub_tbl3, testpub_tbl3a;
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
SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_forparted;
@ -1199,51 +1004,32 @@ CREATE SCHEMA sch1;
CREATE SCHEMA sch2;
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 SEQUENCE sch1.seq1;
CREATE SEQUENCE sch2.seq2;
-- 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);
ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch2;
SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
DROP PUBLICATION pub;
-- Table publication that does not include the parent table
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_sequences;
-- Table publication that includes both the parent table and the child table
ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
DROP PUBLICATION pub;
-- 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);
ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
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;
-- Table publication that does not include the parent table
CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
SELECT * FROM pg_publication_tables;
SELECT * FROM pg_publication_sequences;
-- Table publication that includes both the parent table and the child table
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_sequences;
DROP PUBLICATION pub;
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);
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
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;
DROP PUBLICATION pub;
DROP TABLE sch1.tbl1;
DROP SEQUENCE sch1.seq1, sch2.seq2;
DROP SCHEMA sch1 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();