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:
parent
d7ab2a9a3c
commit
2c7ea57e56
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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');
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -6354,16 +6354,6 @@ SCRAM-SHA-256$<replaceable><iteration count></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><iteration count></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>
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,27 +717,9 @@ 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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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[],
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
res = walrcv_exec(LogRepWorkerWalRcvConn, "COMMIT", 0, NULL);
|
||||
if (res->status != WALRCV_OK_COMMAND)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,22 +5610,11 @@ 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());
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
/*
|
||||
* 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, " %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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
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);
|
||||
|
||||
result = PSQLexec(buf.data);
|
||||
if (!result)
|
||||
goto error_return;
|
||||
if (tableinfo.relpersistence == 'u')
|
||||
printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
|
||||
schemaname, relationname);
|
||||
else
|
||||
tuples = PQntuples(result);
|
||||
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
|
||||
schemaname, relationname);
|
||||
|
||||
if (tuples > 0)
|
||||
printTableAddFooter(&cont, _("Publications:"));
|
||||
myopt.footers = footers;
|
||||
myopt.topt.default_footer = false;
|
||||
myopt.title = title.data;
|
||||
myopt.translate_header = true;
|
||||
|
||||
/* Might be an empty set - that's ok */
|
||||
for (i = 0; i < tuples; i++)
|
||||
{
|
||||
printfPQExpBuffer(&buf, " \"%s\"",
|
||||
PQgetvalue(result, i, 0));
|
||||
printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
|
||||
|
||||
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,23 +5851,6 @@ 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"
|
||||
|
@ -5952,15 +5864,10 @@ listPublications(const char *pattern)
|
|||
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);
|
||||
|
||||
|
|
|
@ -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", "("))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -53,6 +53,6 @@
|
|||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 202204074
|
||||
#define CATALOG_VERSION_NO 202204075
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ typedef struct PGOutputData
|
|||
bool streaming;
|
||||
bool messages;
|
||||
bool two_phase;
|
||||
bool sequences;
|
||||
} PGOutputData;
|
||||
|
||||
#endif /* PGOUTPUT_H */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
Loading…
Reference in New Issue