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 \
|
REGRESS = ddl xact rewrite toast permissions decoding_in_xact \
|
||||||
decoding_into_rel binary prepared replorigin time messages \
|
decoding_into_rel binary prepared replorigin time messages \
|
||||||
spill slot truncate stream stats twophase twophase_stream \
|
spill slot truncate stream stats twophase twophase_stream
|
||||||
sequence
|
|
||||||
ISOLATION = mxact delayed_startup ondisk_startup concurrent_ddl_dml \
|
ISOLATION = mxact delayed_startup ondisk_startup concurrent_ddl_dml \
|
||||||
oldest_xmin snapshot_transfer subxact_without_top concurrent_stream \
|
oldest_xmin snapshot_transfer subxact_without_top concurrent_stream \
|
||||||
twophase_snapshot slot_creation_error
|
twophase_snapshot slot_creation_error
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -58,7 +58,7 @@ SELECT pg_current_xact_id() = '0';
|
||||||
|
|
||||||
-- don't show yet, haven't committed
|
-- don't show yet, haven't committed
|
||||||
INSERT INTO nobarf(data) VALUES('2');
|
INSERT INTO nobarf(data) VALUES('2');
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -68,7 +68,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
INSERT INTO nobarf(data) VALUES('3');
|
INSERT INTO nobarf(data) VALUES('3');
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
@ -19,7 +19,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
|
||||||
CREATE TABLE somechange(id serial primary key);
|
CREATE TABLE somechange(id serial primary key);
|
||||||
INSERT INTO somechange DEFAULT VALUES;
|
INSERT INTO somechange DEFAULT VALUES;
|
||||||
CREATE TABLE changeresult AS
|
CREATE TABLE changeresult AS
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
SELECT * FROM changeresult;
|
SELECT * FROM changeresult;
|
||||||
data
|
data
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
@ -29,9 +29,9 @@ SELECT * FROM changeresult;
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
INSERT INTO changeresult
|
INSERT INTO changeresult
|
||||||
SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
INSERT INTO changeresult
|
INSERT INTO changeresult
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
SELECT * FROM changeresult;
|
SELECT * FROM changeresult;
|
||||||
data
|
data
|
||||||
--------------------------------------------------------------------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -63,7 +63,7 @@ DROP TABLE somechange;
|
||||||
CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$
|
CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$
|
||||||
BEGIN
|
BEGIN
|
||||||
RETURN QUERY
|
RETURN QUERY
|
||||||
SELECT data FROM pg_logical_slot_peek_changes(slot_name, NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_peek_changes(slot_name, NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
END$$ LANGUAGE plpgsql;
|
END$$ LANGUAGE plpgsql;
|
||||||
SELECT * FROM slot_changes_wrapper('regression_slot');
|
SELECT * FROM slot_changes_wrapper('regression_slot');
|
||||||
slot_changes_wrapper
|
slot_changes_wrapper
|
||||||
|
@ -84,7 +84,7 @@ SELECT * FROM slot_changes_wrapper('regression_slot');
|
||||||
COMMIT
|
COMMIT
|
||||||
(14 rows)
|
(14 rows)
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
@ -7,7 +7,7 @@ step s0init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_sl
|
||||||
init
|
init
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
|
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
|
||||||
data
|
data
|
||||||
----
|
----
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
@ -27,7 +27,7 @@ t
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
step s0w: INSERT INTO do_write DEFAULT VALUES;
|
step s0w: INSERT INTO do_write DEFAULT VALUES;
|
||||||
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
|
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
|
||||||
data
|
data
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -50,7 +50,7 @@ step s0init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_sl
|
||||||
init
|
init
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
|
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
|
||||||
data
|
data
|
||||||
----
|
----
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
@ -71,7 +71,7 @@ t
|
||||||
|
|
||||||
step s0alter: ALTER TABLE do_write ADD column ts timestamptz;
|
step s0alter: ALTER TABLE do_write ADD column ts timestamptz;
|
||||||
step s0w: INSERT INTO do_write DEFAULT VALUES;
|
step s0w: INSERT INTO do_write DEFAULT VALUES;
|
||||||
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
|
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
|
||||||
data
|
data
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
@ -35,7 +35,7 @@ init
|
||||||
step s2c: COMMIT;
|
step s2c: COMMIT;
|
||||||
step s1insert: INSERT INTO do_write DEFAULT VALUES;
|
step s1insert: INSERT INTO do_write DEFAULT VALUES;
|
||||||
step s1checkpoint: CHECKPOINT;
|
step s1checkpoint: CHECKPOINT;
|
||||||
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
|
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
|
||||||
data
|
data
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -46,7 +46,7 @@ COMMIT
|
||||||
step s1insert: INSERT INTO do_write DEFAULT VALUES;
|
step s1insert: INSERT INTO do_write DEFAULT VALUES;
|
||||||
step s1alter: ALTER TABLE do_write ADD COLUMN addedbys1 int;
|
step s1alter: ALTER TABLE do_write ADD COLUMN addedbys1 int;
|
||||||
step s1insert: INSERT INTO do_write DEFAULT VALUES;
|
step s1insert: INSERT INTO do_write DEFAULT VALUES;
|
||||||
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
|
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
|
||||||
data
|
data
|
||||||
--------------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
@ -72,9 +72,9 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
|
||||||
-- origin tx
|
-- origin tx
|
||||||
INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again');
|
INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again');
|
||||||
INSERT INTO target_tbl(data)
|
INSERT INTO target_tbl(data)
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
-- as is normal, the insert into target_tbl shows up
|
-- as is normal, the insert into target_tbl shows up
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -110,7 +110,7 @@ SELECT pg_replication_origin_xact_setup('0/aabbccdd', '2013-01-01 00:00');
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
INSERT INTO target_tbl(data)
|
INSERT INTO target_tbl(data)
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
-- check replication progress for the session is correct
|
-- check replication progress for the session is correct
|
||||||
SELECT pg_replication_origin_session_progress(false);
|
SELECT pg_replication_origin_session_progress(false);
|
||||||
|
@ -154,14 +154,14 @@ SELECT pg_replication_origin_progress('regress_test_decoding: regression_slot',
|
||||||
SELECT pg_replication_origin_session_reset();
|
SELECT pg_replication_origin_session_reset();
|
||||||
ERROR: no replication origin is configured
|
ERROR: no replication origin is configured
|
||||||
-- and magically the replayed xact will be filtered!
|
-- and magically the replayed xact will be filtered!
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
|
||||||
data
|
data
|
||||||
------
|
------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
--but new original changes still show up
|
--but new original changes still show up
|
||||||
INSERT INTO origin_tbl(data) VALUES ('will be replicated');
|
INSERT INTO origin_tbl(data) VALUES ('will be replicated');
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
|
||||||
data
|
data
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -227,7 +227,7 @@ SELECT local_id, external_id,
|
||||||
1 | regress_test_decoding: regression_slot_no_lsn | f | t
|
1 | regress_test_decoding: regression_slot_no_lsn | f | t
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot_no_lsn', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot_no_lsn', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0');
|
||||||
data
|
data
|
||||||
-------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
@ -64,7 +64,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
|
||||||
|
|
||||||
CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120));
|
CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120));
|
||||||
INSERT INTO replication_example(somedata) VALUES (1);
|
INSERT INTO replication_example(somedata) VALUES (1);
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
----------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -115,7 +115,7 @@ INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (7, 5
|
||||||
COMMIT;
|
COMMIT;
|
||||||
-- make old files go away
|
-- make old files go away
|
||||||
CHECKPOINT;
|
CHECKPOINT;
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -141,7 +141,7 @@ VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; V
|
||||||
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (8, 6, 1);
|
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (8, 6, 1);
|
||||||
VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; VACUUM FULL iamalargetable;
|
VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; VACUUM FULL iamalargetable;
|
||||||
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (9, 7, 1);
|
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (9, 7, 1);
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
@ -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)
|
(1 row)
|
||||||
|
|
||||||
INSERT INTO replication_example(somedata, text) VALUES (1, 3);
|
INSERT INTO replication_example(somedata, text) VALUES (1, 3);
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
---------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -107,7 +107,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'in
|
||||||
COMMIT
|
COMMIT
|
||||||
(7 rows)
|
(7 rows)
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
---------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -132,7 +132,7 @@ SELECT :'wal_lsn' = :'end_lsn';
|
||||||
t
|
t
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
---------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -140,7 +140,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'in
|
||||||
COMMIT
|
COMMIT
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
------
|
------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
|
@ -52,7 +52,7 @@ CREATE TABLE toasted_copy (
|
||||||
);
|
);
|
||||||
ALTER TABLE toasted_copy ALTER COLUMN data SET STORAGE EXTERNAL;
|
ALTER TABLE toasted_copy ALTER COLUMN data SET STORAGE EXTERNAL;
|
||||||
\copy toasted_copy FROM STDIN
|
\copy toasted_copy FROM STDIN
|
||||||
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
substr
|
substr
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -316,7 +316,7 @@ SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several;
|
||||||
t
|
t
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
regexp_replace
|
regexp_replace
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -327,7 +327,7 @@ SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_sl
|
||||||
-- test update of a toasted key without changing it
|
-- test update of a toasted key without changing it
|
||||||
UPDATE toasted_several SET toasted_col1 = toasted_key;
|
UPDATE toasted_several SET toasted_col1 = toasted_key;
|
||||||
UPDATE toasted_several SET toasted_col2 = toasted_col1;
|
UPDATE toasted_several SET toasted_col2 = toasted_col1;
|
||||||
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
regexp_replace
|
regexp_replace
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -350,7 +350,7 @@ UPDATE toasted_several SET toasted_col1 = toasted_col2 WHERE id = 1;
|
||||||
DELETE FROM toasted_several WHERE id = 1;
|
DELETE FROM toasted_several WHERE id = 1;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
DROP TABLE toasted_several;
|
DROP TABLE toasted_several;
|
||||||
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0')
|
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')
|
||||||
WHERE data NOT LIKE '%INSERT: %';
|
WHERE data NOT LIKE '%INSERT: %';
|
||||||
regexp_replace
|
regexp_replace
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -373,7 +373,7 @@ INSERT INTO tbl1 VALUES(1, repeat('a', 4000)) ;
|
||||||
ALTER TABLE tbl1 ADD COLUMN id serial primary key;
|
ALTER TABLE tbl1 ADD COLUMN id serial primary key;
|
||||||
INSERT INTO tbl2 VALUES(1);
|
INSERT INTO tbl2 VALUES(1);
|
||||||
commit;
|
commit;
|
||||||
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
substr
|
substr
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
@ -11,7 +11,7 @@ CREATE TABLE tab2 (a int primary key, b int);
|
||||||
TRUNCATE tab1;
|
TRUNCATE tab1;
|
||||||
TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE;
|
TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE;
|
||||||
TRUNCATE tab1, tab2;
|
TRUNCATE tab1, tab2;
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
------------------------------------------------------
|
------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
@ -13,7 +13,7 @@ teardown
|
||||||
session "s0"
|
session "s0"
|
||||||
setup { SET synchronous_commit=on; }
|
setup { SET synchronous_commit=on; }
|
||||||
step "s0init" {SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding');}
|
step "s0init" {SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding');}
|
||||||
step "s0start" {SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');}
|
step "s0start" {SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');}
|
||||||
step "s0alter" {ALTER TABLE do_write ADD column ts timestamptz; }
|
step "s0alter" {ALTER TABLE do_write ADD column ts timestamptz; }
|
||||||
step "s0w" { INSERT INTO do_write DEFAULT VALUES; }
|
step "s0w" { INSERT INTO do_write DEFAULT VALUES; }
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ session "s1"
|
||||||
setup { SET synchronous_commit=on; }
|
setup { SET synchronous_commit=on; }
|
||||||
|
|
||||||
step "s1init" {SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding');}
|
step "s1init" {SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding');}
|
||||||
step "s1start" {SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');}
|
step "s1start" {SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');}
|
||||||
step "s1insert" { INSERT INTO do_write DEFAULT VALUES; }
|
step "s1insert" { INSERT INTO do_write DEFAULT VALUES; }
|
||||||
step "s1checkpoint" { CHECKPOINT; }
|
step "s1checkpoint" { CHECKPOINT; }
|
||||||
step "s1alter" { ALTER TABLE do_write ADD COLUMN addedbys1 int; }
|
step "s1alter" { ALTER TABLE do_write ADD COLUMN addedbys1 int; }
|
||||||
|
|
|
@ -19,7 +19,7 @@ SELECT pg_drop_replication_slot('regression_slot');
|
||||||
|
|
||||||
-- check that we're detecting a streaming rep slot used for logical decoding
|
-- check that we're detecting a streaming rep slot used for logical decoding
|
||||||
SELECT 'init' FROM pg_create_physical_replication_slot('repl');
|
SELECT 'init' FROM pg_create_physical_replication_slot('repl');
|
||||||
SELECT data FROM pg_logical_slot_get_changes('repl', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('repl', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
SELECT pg_drop_replication_slot('repl');
|
SELECT pg_drop_replication_slot('repl');
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,11 +64,11 @@ ALTER TABLE replication_example RENAME COLUMN text TO somenum;
|
||||||
INSERT INTO replication_example(somedata, somenum) VALUES (4, 1);
|
INSERT INTO replication_example(somedata, somenum) VALUES (4, 1);
|
||||||
|
|
||||||
-- collect all changes
|
-- collect all changes
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
ALTER TABLE replication_example ALTER COLUMN somenum TYPE int4 USING (somenum::int4);
|
ALTER TABLE replication_example ALTER COLUMN somenum TYPE int4 USING (somenum::int4);
|
||||||
-- check that this doesn't produce any changes from the heap rewrite
|
-- check that this doesn't produce any changes from the heap rewrite
|
||||||
SELECT count(data) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT count(data) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
INSERT INTO replication_example(somedata, somenum) VALUES (5, 1);
|
INSERT INTO replication_example(somedata, somenum) VALUES (5, 1);
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ INSERT INTO replication_example(somedata, somenum, zaphod1) VALUES (6, 4, 2);
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- show changes
|
-- show changes
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- ON CONFLICT DO UPDATE support
|
-- ON CONFLICT DO UPDATE support
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
@ -91,7 +91,7 @@ INSERT INTO replication_example(id, somedata, somenum) SELECT i, i, i FROM gener
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
/* display results */
|
/* display results */
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- MERGE support
|
-- MERGE support
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
@ -113,14 +113,14 @@ CREATE TABLE tr_unique(id2 serial unique NOT NULL, data int);
|
||||||
INSERT INTO tr_unique(data) VALUES(10);
|
INSERT INTO tr_unique(data) VALUES(10);
|
||||||
ALTER TABLE tr_unique RENAME TO tr_pkey;
|
ALTER TABLE tr_unique RENAME TO tr_pkey;
|
||||||
ALTER TABLE tr_pkey ADD COLUMN id serial primary key;
|
ALTER TABLE tr_pkey ADD COLUMN id serial primary key;
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-rewrites', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-rewrites', '1');
|
||||||
|
|
||||||
INSERT INTO tr_pkey(data) VALUES(1);
|
INSERT INTO tr_pkey(data) VALUES(1);
|
||||||
--show deletion with primary key
|
--show deletion with primary key
|
||||||
DELETE FROM tr_pkey;
|
DELETE FROM tr_pkey;
|
||||||
|
|
||||||
/* display results */
|
/* display results */
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check that disk spooling works (also for logical messages)
|
* check that disk spooling works (also for logical messages)
|
||||||
|
@ -137,7 +137,7 @@ COMMIT;
|
||||||
|
|
||||||
/* display results, but hide most of the output */
|
/* display results, but hide most of the output */
|
||||||
SELECT count(*), min(data), max(data)
|
SELECT count(*), min(data), max(data)
|
||||||
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0')
|
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')
|
||||||
GROUP BY substring(data, 1, 24)
|
GROUP BY substring(data, 1, 24)
|
||||||
ORDER BY 1,2;
|
ORDER BY 1,2;
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ DROP TABLE spoolme;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
SELECT data
|
SELECT data
|
||||||
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0')
|
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')
|
||||||
WHERE data ~ 'UPDATE';
|
WHERE data ~ 'UPDATE';
|
||||||
|
|
||||||
-- check that a large, spooled, upsert works
|
-- check that a large, spooled, upsert works
|
||||||
|
@ -161,7 +161,7 @@ SELECT g.i, -g.i FROM generate_series(8000, 12000) g(i)
|
||||||
ON CONFLICT(id) DO UPDATE SET data = EXCLUDED.data;
|
ON CONFLICT(id) DO UPDATE SET data = EXCLUDED.data;
|
||||||
|
|
||||||
SELECT substring(data, 1, 29), count(*)
|
SELECT substring(data, 1, 29), count(*)
|
||||||
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0') WITH ORDINALITY
|
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1') WITH ORDINALITY
|
||||||
GROUP BY 1
|
GROUP BY 1
|
||||||
ORDER BY min(ordinality);
|
ORDER BY min(ordinality);
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ INSERT INTO tr_sub(path) VALUES ('1-top-2-#1');
|
||||||
RELEASE SAVEPOINT b;
|
RELEASE SAVEPOINT b;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- check that we handle xlog assignments correctly
|
-- check that we handle xlog assignments correctly
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
@ -218,7 +218,7 @@ RELEASE SAVEPOINT subtop;
|
||||||
INSERT INTO tr_sub(path) VALUES ('2-top-#1');
|
INSERT INTO tr_sub(path) VALUES ('2-top-#1');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- make sure rollbacked subtransactions aren't decoded
|
-- make sure rollbacked subtransactions aren't decoded
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
@ -231,7 +231,7 @@ ROLLBACK TO SAVEPOINT b;
|
||||||
INSERT INTO tr_sub(path) VALUES ('3-top-2-#2');
|
INSERT INTO tr_sub(path) VALUES ('3-top-2-#2');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- test whether a known, but not yet logged toplevel xact, followed by a
|
-- test whether a known, but not yet logged toplevel xact, followed by a
|
||||||
-- subxact commit is handled correctly
|
-- subxact commit is handled correctly
|
||||||
|
@ -250,7 +250,7 @@ INSERT INTO tr_sub(path) VALUES ('5-top-1-#1');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- check that DDL in aborted subtransactions handled correctly
|
-- check that DDL in aborted subtransactions handled correctly
|
||||||
CREATE TABLE tr_sub_ddl(data int);
|
CREATE TABLE tr_sub_ddl(data int);
|
||||||
|
@ -263,7 +263,7 @@ ALTER TABLE tr_sub_ddl ALTER COLUMN data TYPE bigint;
|
||||||
INSERT INTO tr_sub_ddl VALUES(43);
|
INSERT INTO tr_sub_ddl VALUES(43);
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -303,7 +303,7 @@ ALTER TABLE replication_metadata SET (user_catalog_table = false);
|
||||||
INSERT INTO replication_metadata(relation, options)
|
INSERT INTO replication_metadata(relation, options)
|
||||||
VALUES ('zaphod', NULL);
|
VALUES ('zaphod', NULL);
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check whether we handle updates/deletes correct with & without a pkey
|
* check whether we handle updates/deletes correct with & without a pkey
|
||||||
|
@ -414,7 +414,7 @@ UPDATE toasttable
|
||||||
SET toasted_col1 = (SELECT string_agg(g.i::text, '') FROM generate_series(1, 2000) g(i))
|
SET toasted_col1 = (SELECT string_agg(g.i::text, '') FROM generate_series(1, 2000) g(i))
|
||||||
WHERE id = 1;
|
WHERE id = 1;
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
INSERT INTO toasttable(toasted_col1) SELECT string_agg(g.i::text, '') FROM generate_series(1, 2000) g(i);
|
INSERT INTO toasttable(toasted_col1) SELECT string_agg(g.i::text, '') FROM generate_series(1, 2000) g(i);
|
||||||
|
|
||||||
|
@ -426,10 +426,10 @@ WHERE id = 1;
|
||||||
-- make sure we decode correctly even if the toast table is gone
|
-- make sure we decode correctly even if the toast table is gone
|
||||||
DROP TABLE toasttable;
|
DROP TABLE toasttable;
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- done, free logical replication slot
|
-- done, free logical replication slot
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
SELECT pg_drop_replication_slot('regression_slot');
|
SELECT pg_drop_replication_slot('regression_slot');
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,10 @@ BEGIN;
|
||||||
SELECT pg_current_xact_id() = '0';
|
SELECT pg_current_xact_id() = '0';
|
||||||
-- don't show yet, haven't committed
|
-- don't show yet, haven't committed
|
||||||
INSERT INTO nobarf(data) VALUES('2');
|
INSERT INTO nobarf(data) VALUES('2');
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
INSERT INTO nobarf(data) VALUES('3');
|
INSERT INTO nobarf(data) VALUES('3');
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
SELECT 'stop' FROM pg_drop_replication_slot('regression_slot');
|
SELECT 'stop' FROM pg_drop_replication_slot('regression_slot');
|
||||||
|
|
|
@ -15,14 +15,14 @@ CREATE TABLE somechange(id serial primary key);
|
||||||
INSERT INTO somechange DEFAULT VALUES;
|
INSERT INTO somechange DEFAULT VALUES;
|
||||||
|
|
||||||
CREATE TABLE changeresult AS
|
CREATE TABLE changeresult AS
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
SELECT * FROM changeresult;
|
SELECT * FROM changeresult;
|
||||||
|
|
||||||
INSERT INTO changeresult
|
INSERT INTO changeresult
|
||||||
SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
INSERT INTO changeresult
|
INSERT INTO changeresult
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
SELECT * FROM changeresult;
|
SELECT * FROM changeresult;
|
||||||
DROP TABLE changeresult;
|
DROP TABLE changeresult;
|
||||||
|
@ -32,11 +32,11 @@ DROP TABLE somechange;
|
||||||
CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$
|
CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$
|
||||||
BEGIN
|
BEGIN
|
||||||
RETURN QUERY
|
RETURN QUERY
|
||||||
SELECT data FROM pg_logical_slot_peek_changes(slot_name, NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_peek_changes(slot_name, NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
END$$ LANGUAGE plpgsql;
|
END$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
SELECT * FROM slot_changes_wrapper('regression_slot');
|
SELECT * FROM slot_changes_wrapper('regression_slot');
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
SELECT 'stop' FROM pg_drop_replication_slot('regression_slot');
|
SELECT 'stop' FROM pg_drop_replication_slot('regression_slot');
|
||||||
|
|
|
@ -41,10 +41,10 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
|
||||||
-- origin tx
|
-- origin tx
|
||||||
INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again');
|
INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again');
|
||||||
INSERT INTO target_tbl(data)
|
INSERT INTO target_tbl(data)
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- as is normal, the insert into target_tbl shows up
|
-- as is normal, the insert into target_tbl shows up
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
INSERT INTO origin_tbl(data) VALUES ('will be replicated, but not decoded again');
|
INSERT INTO origin_tbl(data) VALUES ('will be replicated, but not decoded again');
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ BEGIN;
|
||||||
-- setup transaction origin
|
-- setup transaction origin
|
||||||
SELECT pg_replication_origin_xact_setup('0/aabbccdd', '2013-01-01 00:00');
|
SELECT pg_replication_origin_xact_setup('0/aabbccdd', '2013-01-01 00:00');
|
||||||
INSERT INTO target_tbl(data)
|
INSERT INTO target_tbl(data)
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- check replication progress for the session is correct
|
-- check replication progress for the session is correct
|
||||||
|
@ -79,11 +79,11 @@ SELECT pg_replication_origin_progress('regress_test_decoding: regression_slot',
|
||||||
SELECT pg_replication_origin_session_reset();
|
SELECT pg_replication_origin_session_reset();
|
||||||
|
|
||||||
-- and magically the replayed xact will be filtered!
|
-- and magically the replayed xact will be filtered!
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
|
||||||
|
|
||||||
--but new original changes still show up
|
--but new original changes still show up
|
||||||
INSERT INTO origin_tbl(data) VALUES ('will be replicated');
|
INSERT INTO origin_tbl(data) VALUES ('will be replicated');
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'only-local', '1');
|
||||||
|
|
||||||
SELECT pg_drop_replication_slot('regression_slot');
|
SELECT pg_drop_replication_slot('regression_slot');
|
||||||
SELECT pg_replication_origin_drop('regress_test_decoding: regression_slot');
|
SELECT pg_replication_origin_drop('regress_test_decoding: regression_slot');
|
||||||
|
@ -114,7 +114,7 @@ SELECT local_id, external_id,
|
||||||
remote_lsn <> '0/0' AS valid_remote_lsn,
|
remote_lsn <> '0/0' AS valid_remote_lsn,
|
||||||
local_lsn <> '0/0' AS valid_local_lsn
|
local_lsn <> '0/0' AS valid_local_lsn
|
||||||
FROM pg_replication_origin_status;
|
FROM pg_replication_origin_status;
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot_no_lsn', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot_no_lsn', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0');
|
||||||
-- Clean up
|
-- Clean up
|
||||||
SELECT pg_replication_origin_session_reset();
|
SELECT pg_replication_origin_session_reset();
|
||||||
SELECT pg_drop_replication_slot('regression_slot_no_lsn');
|
SELECT pg_drop_replication_slot('regression_slot_no_lsn');
|
||||||
|
|
|
@ -35,7 +35,7 @@ SELECT pg_relation_size((SELECT reltoastrelid FROM pg_class WHERE oid = 'pg_shde
|
||||||
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
|
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
|
||||||
CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120));
|
CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120));
|
||||||
INSERT INTO replication_example(somedata) VALUES (1);
|
INSERT INTO replication_example(somedata) VALUES (1);
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO replication_example(somedata) VALUES (2);
|
INSERT INTO replication_example(somedata) VALUES (2);
|
||||||
|
@ -90,7 +90,7 @@ COMMIT;
|
||||||
-- make old files go away
|
-- make old files go away
|
||||||
CHECKPOINT;
|
CHECKPOINT;
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- trigger repeated rewrites of a system catalog with a toast table,
|
-- trigger repeated rewrites of a system catalog with a toast table,
|
||||||
-- that previously was buggy: 20180914021046.oi7dm4ra3ot2g2kt@alap3.anarazel.de
|
-- that previously was buggy: 20180914021046.oi7dm4ra3ot2g2kt@alap3.anarazel.de
|
||||||
|
@ -98,7 +98,7 @@ VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; V
|
||||||
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (8, 6, 1);
|
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (8, 6, 1);
|
||||||
VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; VACUUM FULL iamalargetable;
|
VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; VACUUM FULL iamalargetable;
|
||||||
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (9, 7, 1);
|
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (9, 7, 1);
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
SELECT pg_drop_replication_slot('regression_slot');
|
SELECT pg_drop_replication_slot('regression_slot');
|
||||||
DROP TABLE IF EXISTS replication_example;
|
DROP TABLE IF EXISTS replication_example;
|
||||||
|
|
|
@ -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);
|
INSERT INTO replication_example(somedata, text) VALUES (1, 3);
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
INSERT INTO replication_example(somedata, text) VALUES (1, 4);
|
INSERT INTO replication_example(somedata, text) VALUES (1, 4);
|
||||||
INSERT INTO replication_example(somedata, text) VALUES (1, 5);
|
INSERT INTO replication_example(somedata, text) VALUES (1, 5);
|
||||||
|
@ -65,8 +65,8 @@ SELECT slot_name FROM pg_replication_slot_advance('regression_slot2', pg_current
|
||||||
|
|
||||||
SELECT :'wal_lsn' = :'end_lsn';
|
SELECT :'wal_lsn' = :'end_lsn';
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
DROP TABLE replication_example;
|
DROP TABLE replication_example;
|
||||||
|
|
||||||
|
|
|
@ -264,7 +264,7 @@ ALTER TABLE toasted_copy ALTER COLUMN data SET STORAGE EXTERNAL;
|
||||||
202 untoasted199
|
202 untoasted199
|
||||||
203 untoasted200
|
203 untoasted200
|
||||||
\.
|
\.
|
||||||
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- test we can decode "old" tuples bigger than the max heap tuple size correctly
|
-- test we can decode "old" tuples bigger than the max heap tuple size correctly
|
||||||
DROP TABLE IF EXISTS toasted_several;
|
DROP TABLE IF EXISTS toasted_several;
|
||||||
|
@ -287,13 +287,13 @@ UPDATE pg_attribute SET attstorage = 'x' WHERE attrelid = 'toasted_several_pkey'
|
||||||
INSERT INTO toasted_several(toasted_key) VALUES(repeat('9876543210', 10000));
|
INSERT INTO toasted_several(toasted_key) VALUES(repeat('9876543210', 10000));
|
||||||
SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several;
|
SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several;
|
||||||
|
|
||||||
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- test update of a toasted key without changing it
|
-- test update of a toasted key without changing it
|
||||||
UPDATE toasted_several SET toasted_col1 = toasted_key;
|
UPDATE toasted_several SET toasted_col1 = toasted_key;
|
||||||
UPDATE toasted_several SET toasted_col2 = toasted_col1;
|
UPDATE toasted_several SET toasted_col2 = toasted_col1;
|
||||||
|
|
||||||
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* update with large tuplebuf, in a transaction large enough to force to spool to disk
|
* update with large tuplebuf, in a transaction large enough to force to spool to disk
|
||||||
|
@ -306,7 +306,7 @@ COMMIT;
|
||||||
|
|
||||||
DROP TABLE toasted_several;
|
DROP TABLE toasted_several;
|
||||||
|
|
||||||
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0')
|
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')
|
||||||
WHERE data NOT LIKE '%INSERT: %';
|
WHERE data NOT LIKE '%INSERT: %';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -322,6 +322,6 @@ INSERT INTO tbl1 VALUES(1, repeat('a', 4000)) ;
|
||||||
ALTER TABLE tbl1 ADD COLUMN id serial primary key;
|
ALTER TABLE tbl1 ADD COLUMN id serial primary key;
|
||||||
INSERT INTO tbl2 VALUES(1);
|
INSERT INTO tbl2 VALUES(1);
|
||||||
commit;
|
commit;
|
||||||
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
SELECT pg_drop_replication_slot('regression_slot');
|
SELECT pg_drop_replication_slot('regression_slot');
|
||||||
|
|
|
@ -10,5 +10,5 @@ TRUNCATE tab1;
|
||||||
TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE;
|
TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE;
|
||||||
TRUNCATE tab1, tab2;
|
TRUNCATE tab1, tab2;
|
||||||
|
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
SELECT pg_drop_replication_slot('regression_slot');
|
SELECT pg_drop_replication_slot('regression_slot');
|
||||||
|
|
|
@ -35,7 +35,6 @@ typedef struct
|
||||||
bool include_timestamp;
|
bool include_timestamp;
|
||||||
bool skip_empty_xacts;
|
bool skip_empty_xacts;
|
||||||
bool only_local;
|
bool only_local;
|
||||||
bool include_sequences;
|
|
||||||
} TestDecodingData;
|
} TestDecodingData;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -77,10 +76,6 @@ static void pg_decode_message(LogicalDecodingContext *ctx,
|
||||||
ReorderBufferTXN *txn, XLogRecPtr message_lsn,
|
ReorderBufferTXN *txn, XLogRecPtr message_lsn,
|
||||||
bool transactional, const char *prefix,
|
bool transactional, const char *prefix,
|
||||||
Size sz, const char *message);
|
Size sz, const char *message);
|
||||||
static void pg_decode_sequence(LogicalDecodingContext *ctx,
|
|
||||||
ReorderBufferTXN *txn, XLogRecPtr sequence_lsn,
|
|
||||||
Relation rel, bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called);
|
|
||||||
static bool pg_decode_filter_prepare(LogicalDecodingContext *ctx,
|
static bool pg_decode_filter_prepare(LogicalDecodingContext *ctx,
|
||||||
TransactionId xid,
|
TransactionId xid,
|
||||||
const char *gid);
|
const char *gid);
|
||||||
|
@ -121,10 +116,6 @@ static void pg_decode_stream_message(LogicalDecodingContext *ctx,
|
||||||
ReorderBufferTXN *txn, XLogRecPtr message_lsn,
|
ReorderBufferTXN *txn, XLogRecPtr message_lsn,
|
||||||
bool transactional, const char *prefix,
|
bool transactional, const char *prefix,
|
||||||
Size sz, const char *message);
|
Size sz, const char *message);
|
||||||
static void pg_decode_stream_sequence(LogicalDecodingContext *ctx,
|
|
||||||
ReorderBufferTXN *txn, XLogRecPtr sequence_lsn,
|
|
||||||
Relation rel, bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called);
|
|
||||||
static void pg_decode_stream_truncate(LogicalDecodingContext *ctx,
|
static void pg_decode_stream_truncate(LogicalDecodingContext *ctx,
|
||||||
ReorderBufferTXN *txn,
|
ReorderBufferTXN *txn,
|
||||||
int nrelations, Relation relations[],
|
int nrelations, Relation relations[],
|
||||||
|
@ -150,7 +141,6 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
|
||||||
cb->filter_by_origin_cb = pg_decode_filter;
|
cb->filter_by_origin_cb = pg_decode_filter;
|
||||||
cb->shutdown_cb = pg_decode_shutdown;
|
cb->shutdown_cb = pg_decode_shutdown;
|
||||||
cb->message_cb = pg_decode_message;
|
cb->message_cb = pg_decode_message;
|
||||||
cb->sequence_cb = pg_decode_sequence;
|
|
||||||
cb->filter_prepare_cb = pg_decode_filter_prepare;
|
cb->filter_prepare_cb = pg_decode_filter_prepare;
|
||||||
cb->begin_prepare_cb = pg_decode_begin_prepare_txn;
|
cb->begin_prepare_cb = pg_decode_begin_prepare_txn;
|
||||||
cb->prepare_cb = pg_decode_prepare_txn;
|
cb->prepare_cb = pg_decode_prepare_txn;
|
||||||
|
@ -163,7 +153,6 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
|
||||||
cb->stream_commit_cb = pg_decode_stream_commit;
|
cb->stream_commit_cb = pg_decode_stream_commit;
|
||||||
cb->stream_change_cb = pg_decode_stream_change;
|
cb->stream_change_cb = pg_decode_stream_change;
|
||||||
cb->stream_message_cb = pg_decode_stream_message;
|
cb->stream_message_cb = pg_decode_stream_message;
|
||||||
cb->stream_sequence_cb = pg_decode_stream_sequence;
|
|
||||||
cb->stream_truncate_cb = pg_decode_stream_truncate;
|
cb->stream_truncate_cb = pg_decode_stream_truncate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +173,6 @@ pg_decode_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
|
||||||
data->include_xids = true;
|
data->include_xids = true;
|
||||||
data->include_timestamp = false;
|
data->include_timestamp = false;
|
||||||
data->skip_empty_xacts = false;
|
data->skip_empty_xacts = false;
|
||||||
data->include_sequences = true;
|
|
||||||
data->only_local = false;
|
data->only_local = false;
|
||||||
|
|
||||||
ctx->output_plugin_private = data;
|
ctx->output_plugin_private = data;
|
||||||
|
@ -277,17 +265,6 @@ pg_decode_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
|
||||||
errmsg("could not parse value \"%s\" for parameter \"%s\"",
|
errmsg("could not parse value \"%s\" for parameter \"%s\"",
|
||||||
strVal(elem->arg), elem->defname)));
|
strVal(elem->arg), elem->defname)));
|
||||||
}
|
}
|
||||||
else if (strcmp(elem->defname, "include-sequences") == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (elem->arg == NULL)
|
|
||||||
data->include_sequences = false;
|
|
||||||
else if (!parse_bool(strVal(elem->arg), &data->include_sequences))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("could not parse value \"%s\" for parameter \"%s\"",
|
|
||||||
strVal(elem->arg), elem->defname)));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -779,38 +756,6 @@ pg_decode_message(LogicalDecodingContext *ctx,
|
||||||
OutputPluginWrite(ctx, true);
|
OutputPluginWrite(ctx, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
pg_decode_sequence(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn, Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called)
|
|
||||||
{
|
|
||||||
TestDecodingData *data = ctx->output_plugin_private;
|
|
||||||
TestDecodingTxnData *txndata = txn->output_plugin_private;
|
|
||||||
|
|
||||||
if (!data->include_sequences)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* output BEGIN if we haven't yet, but only for the transactional case */
|
|
||||||
if (transactional)
|
|
||||||
{
|
|
||||||
if (data->skip_empty_xacts && !txndata->xact_wrote_changes)
|
|
||||||
{
|
|
||||||
pg_output_begin(ctx, data, txn, false);
|
|
||||||
}
|
|
||||||
txndata->xact_wrote_changes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
|
||||||
appendStringInfoString(ctx->out, "sequence ");
|
|
||||||
appendStringInfoString(ctx->out,
|
|
||||||
quote_qualified_identifier(get_namespace_name(get_rel_namespace(RelationGetRelid(rel))),
|
|
||||||
RelationGetRelationName(rel)));
|
|
||||||
appendStringInfo(ctx->out, ": transactional:%d last_value: " INT64_FORMAT " log_cnt: " INT64_FORMAT " is_called:%d",
|
|
||||||
transactional, last_value, log_cnt, is_called);
|
|
||||||
OutputPluginWrite(ctx, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pg_decode_stream_start(LogicalDecodingContext *ctx,
|
pg_decode_stream_start(LogicalDecodingContext *ctx,
|
||||||
ReorderBufferTXN *txn)
|
ReorderBufferTXN *txn)
|
||||||
|
@ -1010,38 +955,6 @@ pg_decode_stream_message(LogicalDecodingContext *ctx,
|
||||||
OutputPluginWrite(ctx, true);
|
OutputPluginWrite(ctx, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
pg_decode_stream_sequence(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn, Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called)
|
|
||||||
{
|
|
||||||
TestDecodingData *data = ctx->output_plugin_private;
|
|
||||||
TestDecodingTxnData *txndata = txn->output_plugin_private;
|
|
||||||
|
|
||||||
if (!data->include_sequences)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* output BEGIN if we haven't yet, but only for the transactional case */
|
|
||||||
if (transactional)
|
|
||||||
{
|
|
||||||
if (data->skip_empty_xacts && !txndata->xact_wrote_changes)
|
|
||||||
{
|
|
||||||
pg_output_begin(ctx, data, txn, false);
|
|
||||||
}
|
|
||||||
txndata->xact_wrote_changes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
|
||||||
appendStringInfoString(ctx->out, "streaming sequence ");
|
|
||||||
appendStringInfoString(ctx->out,
|
|
||||||
quote_qualified_identifier(get_namespace_name(get_rel_namespace(RelationGetRelid(rel))),
|
|
||||||
RelationGetRelationName(rel)));
|
|
||||||
appendStringInfo(ctx->out, ": transactional:%d last_value: " INT64_FORMAT " log_cnt: " INT64_FORMAT " is_called:%d",
|
|
||||||
transactional, last_value, log_cnt, is_called);
|
|
||||||
OutputPluginWrite(ctx, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In streaming mode, we don't display the detailed information of Truncate.
|
* In streaming mode, we don't display the detailed information of Truncate.
|
||||||
* See pg_decode_stream_change.
|
* See pg_decode_stream_change.
|
||||||
|
|
|
@ -6354,16 +6354,6 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
||||||
Reference to schema
|
Reference to schema
|
||||||
</para></entry>
|
</para></entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry role="catalog_table_entry"><para role="column_definition">
|
|
||||||
<structfield>pntype</structfield> <type>char</type>
|
|
||||||
Determines which object type is included from this schema.
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
Reference to schema
|
|
||||||
</para></entry>
|
|
||||||
</row>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</table>
|
||||||
|
@ -9699,11 +9689,6 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
||||||
<entry>prepared transactions</entry>
|
<entry>prepared transactions</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry><link linkend="view-pg-publication-sequences"><structname>pg_publication_sequences</structname></link></entry>
|
|
||||||
<entry>publications and their associated sequences</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><link linkend="view-pg-publication-tables"><structname>pg_publication_tables</structname></link></entry>
|
<entry><link linkend="view-pg-publication-tables"><structname>pg_publication_tables</structname></link></entry>
|
||||||
<entry>publications and their associated tables</entry>
|
<entry>publications and their associated tables</entry>
|
||||||
|
@ -11641,72 +11626,6 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="view-pg-publication-sequences">
|
|
||||||
<title><structname>pg_publication_sequences</structname></title>
|
|
||||||
|
|
||||||
<indexterm zone="view-pg-publication-sequences">
|
|
||||||
<primary>pg_publication_sequences</primary>
|
|
||||||
</indexterm>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The view <structname>pg_publication_sequences</structname> provides
|
|
||||||
information about the mapping between publications and the sequences they
|
|
||||||
contain. Unlike the underlying catalog
|
|
||||||
<link linkend="catalog-pg-publication-rel"><structname>pg_publication_rel</structname></link>,
|
|
||||||
this view expands
|
|
||||||
publications defined as <literal>FOR ALL SEQUENCES</literal>, so for such
|
|
||||||
publications there will be a row for each eligible sequence.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<title><structname>pg_publication_sequences</structname> Columns</title>
|
|
||||||
<tgroup cols="1">
|
|
||||||
<thead>
|
|
||||||
<row>
|
|
||||||
<entry role="catalog_table_entry"><para role="column_definition">
|
|
||||||
Column Type
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
Description
|
|
||||||
</para></entry>
|
|
||||||
</row>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
<row>
|
|
||||||
<entry role="catalog_table_entry"><para role="column_definition">
|
|
||||||
<structfield>pubname</structfield> <type>name</type>
|
|
||||||
(references <link linkend="catalog-pg-publication"><structname>pg_publication</structname></link>.<structfield>pubname</structfield>)
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
Name of publication
|
|
||||||
</para></entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry role="catalog_table_entry"><para role="column_definition">
|
|
||||||
<structfield>schemaname</structfield> <type>name</type>
|
|
||||||
(references <link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.<structfield>nspname</structfield>)
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
Name of schema containing sequence
|
|
||||||
</para></entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry role="catalog_table_entry"><para role="column_definition">
|
|
||||||
<structfield>sequencename</structfield> <type>name</type>
|
|
||||||
(references <link linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>relname</structfield>)
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
Name of sequence
|
|
||||||
</para></entry>
|
|
||||||
</row>
|
|
||||||
</tbody>
|
|
||||||
</tgroup>
|
|
||||||
</table>
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
<sect1 id="view-pg-publication-tables">
|
<sect1 id="view-pg-publication-tables">
|
||||||
<title><structname>pg_publication_tables</structname></title>
|
<title><structname>pg_publication_tables</structname></title>
|
||||||
|
|
||||||
|
|
|
@ -458,7 +458,6 @@ typedef struct OutputPluginCallbacks
|
||||||
LogicalDecodeTruncateCB truncate_cb;
|
LogicalDecodeTruncateCB truncate_cb;
|
||||||
LogicalDecodeCommitCB commit_cb;
|
LogicalDecodeCommitCB commit_cb;
|
||||||
LogicalDecodeMessageCB message_cb;
|
LogicalDecodeMessageCB message_cb;
|
||||||
LogicalDecodeSequenceCB sequence_cb;
|
|
||||||
LogicalDecodeFilterByOriginCB filter_by_origin_cb;
|
LogicalDecodeFilterByOriginCB filter_by_origin_cb;
|
||||||
LogicalDecodeShutdownCB shutdown_cb;
|
LogicalDecodeShutdownCB shutdown_cb;
|
||||||
LogicalDecodeFilterPrepareCB filter_prepare_cb;
|
LogicalDecodeFilterPrepareCB filter_prepare_cb;
|
||||||
|
@ -473,7 +472,6 @@ typedef struct OutputPluginCallbacks
|
||||||
LogicalDecodeStreamCommitCB stream_commit_cb;
|
LogicalDecodeStreamCommitCB stream_commit_cb;
|
||||||
LogicalDecodeStreamChangeCB stream_change_cb;
|
LogicalDecodeStreamChangeCB stream_change_cb;
|
||||||
LogicalDecodeStreamMessageCB stream_message_cb;
|
LogicalDecodeStreamMessageCB stream_message_cb;
|
||||||
LogicalDecodeStreamSequenceCB stream_sequence_cb;
|
|
||||||
LogicalDecodeStreamTruncateCB stream_truncate_cb;
|
LogicalDecodeStreamTruncateCB stream_truncate_cb;
|
||||||
} OutputPluginCallbacks;
|
} OutputPluginCallbacks;
|
||||||
|
|
||||||
|
@ -483,11 +481,9 @@ typedef void (*LogicalOutputPluginInit) (struct OutputPluginCallbacks *cb);
|
||||||
and <function>commit_cb</function> callbacks are required,
|
and <function>commit_cb</function> callbacks are required,
|
||||||
while <function>startup_cb</function>,
|
while <function>startup_cb</function>,
|
||||||
<function>filter_by_origin_cb</function>, <function>truncate_cb</function>,
|
<function>filter_by_origin_cb</function>, <function>truncate_cb</function>,
|
||||||
<function>sequence_cb</function>, and <function>shutdown_cb</function> are
|
and <function>shutdown_cb</function> are optional.
|
||||||
optional. If <function>truncate_cb</function> is not set but a
|
If <function>truncate_cb</function> is not set but a
|
||||||
<command>TRUNCATE</command> is to be decoded, the action will be ignored.
|
<command>TRUNCATE</command> is to be decoded, the action will be ignored.
|
||||||
Similarly, if <function>sequence_cb</function> is not set and a sequence
|
|
||||||
change is to be decoded, the action will be ignored.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -496,8 +492,7 @@ typedef void (*LogicalOutputPluginInit) (struct OutputPluginCallbacks *cb);
|
||||||
<function>stream_stop_cb</function>, <function>stream_abort_cb</function>,
|
<function>stream_stop_cb</function>, <function>stream_abort_cb</function>,
|
||||||
<function>stream_commit_cb</function>, <function>stream_change_cb</function>,
|
<function>stream_commit_cb</function>, <function>stream_change_cb</function>,
|
||||||
and <function>stream_prepare_cb</function>
|
and <function>stream_prepare_cb</function>
|
||||||
are required, while <function>stream_message_cb</function>,
|
are required, while <function>stream_message_cb</function> and
|
||||||
<function>stream_sequence_cb</function>, and
|
|
||||||
<function>stream_truncate_cb</function> are optional.
|
<function>stream_truncate_cb</function> are optional.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
@ -813,35 +808,6 @@ typedef void (*LogicalDecodeMessageCB) (struct LogicalDecodingContext *ctx,
|
||||||
</para>
|
</para>
|
||||||
</sect3>
|
</sect3>
|
||||||
|
|
||||||
<sect3 id="logicaldecoding-output-plugin-sequence">
|
|
||||||
<title>Sequence Callback</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The optional <function>sequence_cb</function> callback is called for
|
|
||||||
actions that update a sequence value.
|
|
||||||
<programlisting>
|
|
||||||
typedef void (*LogicalDecodeSequenceCB) (struct LogicalDecodingContext *ctx,
|
|
||||||
ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn,
|
|
||||||
Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value,
|
|
||||||
int64 log_cnt,
|
|
||||||
bool is_called);
|
|
||||||
</programlisting>
|
|
||||||
The <parameter>txn</parameter> parameter contains meta information about
|
|
||||||
the transaction the sequence change is part of. Note however that for
|
|
||||||
non-transactional increments, the transaction may be either NULL or not
|
|
||||||
NULL, depending on if the transaction already has an XID assigned.
|
|
||||||
The <parameter>sequence_lsn</parameter> has the WAL location of the
|
|
||||||
sequence update. <parameter>transactional</parameter> says if the
|
|
||||||
sequence has to be replayed as part of the transaction or directly.
|
|
||||||
|
|
||||||
The <parameter>last_value</parameter>, <parameter>log_cnt</parameter> and
|
|
||||||
<parameter>is_called</parameter> parameters describe the sequence change.
|
|
||||||
</para>
|
|
||||||
</sect3>
|
|
||||||
|
|
||||||
<sect3 id="logicaldecoding-output-plugin-filter-prepare">
|
<sect3 id="logicaldecoding-output-plugin-filter-prepare">
|
||||||
<title>Prepare Filter Callback</title>
|
<title>Prepare Filter Callback</title>
|
||||||
|
|
||||||
|
@ -1051,26 +1017,6 @@ typedef void (*LogicalDecodeStreamMessageCB) (struct LogicalDecodingContext *ctx
|
||||||
</para>
|
</para>
|
||||||
</sect3>
|
</sect3>
|
||||||
|
|
||||||
<sect3 id="logicaldecoding-output-plugin-stream-sequence">
|
|
||||||
<title>Stream Sequence Callback</title>
|
|
||||||
<para>
|
|
||||||
The optional <function>stream_sequence_cb</function> callback is called
|
|
||||||
for actions that change a sequence in a block of streamed changes
|
|
||||||
(demarcated by <function>stream_start_cb</function> and
|
|
||||||
<function>stream_stop_cb</function> calls).
|
|
||||||
<programlisting>
|
|
||||||
typedef void (*LogicalDecodeStreamSequenceCB) (struct LogicalDecodingContext *ctx,
|
|
||||||
ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn,
|
|
||||||
Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value,
|
|
||||||
int64 log_cnt,
|
|
||||||
bool is_called);
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
</sect3>
|
|
||||||
|
|
||||||
<sect3 id="logicaldecoding-output-plugin-stream-truncate">
|
<sect3 id="logicaldecoding-output-plugin-stream-truncate">
|
||||||
<title>Stream Truncate Callback</title>
|
<title>Stream Truncate Callback</title>
|
||||||
<para>
|
<para>
|
||||||
|
@ -1251,9 +1197,8 @@ OutputPluginWrite(ctx, true);
|
||||||
in-progress transactions. There are multiple required streaming callbacks
|
in-progress transactions. There are multiple required streaming callbacks
|
||||||
(<function>stream_start_cb</function>, <function>stream_stop_cb</function>,
|
(<function>stream_start_cb</function>, <function>stream_stop_cb</function>,
|
||||||
<function>stream_abort_cb</function>, <function>stream_commit_cb</function>
|
<function>stream_abort_cb</function>, <function>stream_commit_cb</function>
|
||||||
and <function>stream_change_cb</function>) and multiple optional callbacks
|
and <function>stream_change_cb</function>) and two optional callbacks
|
||||||
(<function>stream_message_cb</function>, <function>stream_sequence_cb</function>,
|
(<function>stream_message_cb</function> and <function>stream_truncate_cb</function>).
|
||||||
and <function>stream_truncate_cb</function>).
|
|
||||||
Also, if streaming of two-phase commands is to be supported, then additional
|
Also, if streaming of two-phase commands is to be supported, then additional
|
||||||
callbacks must be provided. (See <xref linkend="logicaldecoding-two-phase-commits"/>
|
callbacks must be provided. (See <xref linkend="logicaldecoding-two-phase-commits"/>
|
||||||
for details).
|
for details).
|
||||||
|
|
|
@ -7072,125 +7072,6 @@ Relation
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry id="protocol-logicalrep-message-formats-Sequence">
|
|
||||||
<term>
|
|
||||||
Sequence
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
|
|
||||||
<variablelist>
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
Byte1('X')
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
Identifies the message as a sequence message.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
Int32 (TransactionId)
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
Xid of the transaction (only present for streamed transactions).
|
|
||||||
This field is available since protocol version 2.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
Int8(0)
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
Flags; currently unused.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
Int64 (XLogRecPtr)
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
The LSN of the sequence increment.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
String
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
Namespace (empty string for <literal>pg_catalog</literal>).
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
String
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
Relation name.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
Int8
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
1 if the sequence update is transactional, 0 otherwise.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
Int64
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
<structfield>last_value</structfield> value of the sequence.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
Int64
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
<structfield>log_cnt</structfield> value of the sequence.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
Int8
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
<structfield>is_called</structfield> value of the sequence.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
</variablelist>
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry id="protocol-logicalrep-message-formats-Type">
|
<varlistentry id="protocol-logicalrep-message-formats-Type">
|
||||||
<term>
|
<term>
|
||||||
Type
|
Type
|
||||||
|
|
|
@ -31,9 +31,7 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
|
||||||
<phrase>where <replaceable class="parameter">publication_object</replaceable> is one of:</phrase>
|
<phrase>where <replaceable class="parameter">publication_object</replaceable> is one of:</phrase>
|
||||||
|
|
||||||
TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
|
TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
|
||||||
SEQUENCE <replaceable class="parameter">sequence_name</replaceable> [, ... ]
|
|
||||||
ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
|
ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
|
||||||
ALL SEQUENCES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
|
|
||||||
</synopsis>
|
</synopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
@ -46,13 +44,13 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The first three variants change which objects (tables, sequences or schemas)
|
The first three variants change which tables/schemas are part of the
|
||||||
are part of the publication. The <literal>SET</literal> clause will replace
|
publication. The <literal>SET</literal> clause will replace the list of
|
||||||
the list of objects in the publication with the specified list; the existing
|
tables/schemas in the publication with the specified list; the existing
|
||||||
objects that were present in the publication will be removed.
|
tables/schemas that were present in the publication will be removed. The
|
||||||
The <literal>ADD</literal> and <literal>DROP</literal> clauses will add and
|
<literal>ADD</literal> and <literal>DROP</literal> clauses will add and
|
||||||
remove one or more objects from the publication. Note that adding objects
|
remove one or more tables/schemas from the publication. Note that adding
|
||||||
to a publication that is already subscribed to will require an
|
tables/schemas to a publication that is already subscribed to will require an
|
||||||
<literal>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</literal> action on the
|
<literal>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</literal> action on the
|
||||||
subscribing side in order to become effective. Note also that the combination
|
subscribing side in order to become effective. Note also that the combination
|
||||||
of <literal>DROP</literal> with a <literal>WHERE</literal> clause is not
|
of <literal>DROP</literal> with a <literal>WHERE</literal> clause is not
|
||||||
|
@ -132,15 +130,6 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><replaceable class="parameter">sequence_name</replaceable></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
Name of an existing sequence.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><replaceable class="parameter">schema_name</replaceable></term>
|
<term><replaceable class="parameter">schema_name</replaceable></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
|
|
@ -150,8 +150,8 @@ ALTER SUBSCRIPTION <replaceable class="parameter">name</replaceable> RENAME TO <
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Fetch missing table information from publisher. This will start
|
Fetch missing table information from publisher. This will start
|
||||||
replication of tables and sequences that were added to the subscribed-to
|
replication of tables that were added to the subscribed-to publications
|
||||||
publications since <command>CREATE SUBSCRIPTION</command> or
|
since <command>CREATE SUBSCRIPTION</command> or
|
||||||
the last invocation of <command>REFRESH PUBLICATION</command>.
|
the last invocation of <command>REFRESH PUBLICATION</command>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
@ -169,8 +169,8 @@ ALTER SUBSCRIPTION <replaceable class="parameter">name</replaceable> RENAME TO <
|
||||||
The default is <literal>true</literal>.
|
The default is <literal>true</literal>.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Previously subscribed tables and sequences are not copied, even if a
|
Previously subscribed tables are not copied, even if a table's row
|
||||||
table's row filter <literal>WHERE</literal> clause has since been modified.
|
filter <literal>WHERE</literal> clause has since been modified.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
|
@ -22,21 +22,14 @@ PostgreSQL documentation
|
||||||
<refsynopsisdiv>
|
<refsynopsisdiv>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
|
CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
|
||||||
[ FOR ALL <replaceable class="parameter">object_type</replaceable> [, ...]
|
[ FOR ALL TABLES
|
||||||
| FOR <replaceable class="parameter">publication_object</replaceable> [, ... ] ]
|
| FOR <replaceable class="parameter">publication_object</replaceable> [, ... ] ]
|
||||||
[ WITH ( <replaceable class="parameter">publication_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
|
[ WITH ( <replaceable class="parameter">publication_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
|
||||||
|
|
||||||
<phrase>where <replaceable class="parameter">object type</replaceable> is one of:</phrase>
|
|
||||||
|
|
||||||
TABLES
|
|
||||||
SEQUENCES
|
|
||||||
|
|
||||||
<phrase>where <replaceable class="parameter">publication_object</replaceable> is one of:</phrase>
|
<phrase>where <replaceable class="parameter">publication_object</replaceable> is one of:</phrase>
|
||||||
|
|
||||||
TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
|
TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
|
||||||
SEQUENCE <replaceable class="parameter">sequence_name</replaceable> [ * ] [, ... ]
|
|
||||||
ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
|
ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
|
||||||
ALL SEQUENCES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
|
|
||||||
</synopsis>
|
</synopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
@ -121,43 +114,27 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><literal>FOR SEQUENCE</literal></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
Specifies a list of sequences to add to the publication.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Specifying a sequence that is part of a schema specified by <literal>FOR
|
|
||||||
ALL SEQUENCES IN SCHEMA</literal> is not supported.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>FOR ALL TABLES</literal></term>
|
<term><literal>FOR ALL TABLES</literal></term>
|
||||||
<term><literal>FOR ALL SEQUENCES</literal></term>
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Marks the publication as one that replicates changes for all tables/sequences in
|
Marks the publication as one that replicates changes for all tables in
|
||||||
the database, including tables/sequences created in the future.
|
the database, including tables created in the future.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>FOR ALL TABLES IN SCHEMA</literal></term>
|
<term><literal>FOR ALL TABLES IN SCHEMA</literal></term>
|
||||||
<term><literal>FOR ALL SEQUENCES IN SCHEMA</literal></term>
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Marks the publication as one that replicates changes for all sequences/tables in
|
Marks the publication as one that replicates changes for all tables in
|
||||||
the specified list of schemas, including sequences/tables created in the future.
|
the specified list of schemas, including tables created in the future.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Specifying a schema along with a sequence/table which belongs to the specified
|
Specifying a schema along with a table which belongs to the specified
|
||||||
schema using <literal>FOR SEQUENCE</literal>/<literal>FOR TABLE</literal> is not supported.
|
schema using <literal>FOR TABLE</literal> is not supported.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -232,9 +209,10 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
|
||||||
<title>Notes</title>
|
<title>Notes</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
If <literal>FOR TABLE</literal>, <literal>FOR SEQUENCE</literal>, etc. is
|
If <literal>FOR TABLE</literal>, <literal>FOR ALL TABLES</literal> or
|
||||||
not specified, then the publication starts out with an empty set of tables
|
<literal>FOR ALL TABLES IN SCHEMA</literal> are not specified, then the
|
||||||
and sequences. That is useful if objects are to be added later.
|
publication starts out with an empty set of tables. That is useful if
|
||||||
|
tables or schemas are to be added later.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -249,9 +227,10 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
To add a table or a sequence to a publication, the invoking user must
|
To add a table to a publication, the invoking user must have ownership
|
||||||
have ownership rights on the object. The <command>FOR ALL ...</command>
|
rights on the table. The <command>FOR ALL TABLES</command> and
|
||||||
clauses require the invoking user to be a superuser.
|
<command>FOR ALL TABLES IN SCHEMA</command> clauses require the invoking
|
||||||
|
user to be a superuser.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
|
|
@ -1941,14 +1941,12 @@ get_object_address_publication_schema(List *object, bool missing_ok)
|
||||||
char *pubname;
|
char *pubname;
|
||||||
char *schemaname;
|
char *schemaname;
|
||||||
Oid schemaid;
|
Oid schemaid;
|
||||||
char *objtype;
|
|
||||||
|
|
||||||
ObjectAddressSet(address, PublicationNamespaceRelationId, InvalidOid);
|
ObjectAddressSet(address, PublicationNamespaceRelationId, InvalidOid);
|
||||||
|
|
||||||
/* Fetch schema name and publication name from input list */
|
/* Fetch schema name and publication name from input list */
|
||||||
schemaname = strVal(linitial(object));
|
schemaname = strVal(linitial(object));
|
||||||
pubname = strVal(lsecond(object));
|
pubname = strVal(lsecond(object));
|
||||||
objtype = strVal(lthird(object));
|
|
||||||
|
|
||||||
schemaid = get_namespace_oid(schemaname, missing_ok);
|
schemaid = get_namespace_oid(schemaname, missing_ok);
|
||||||
if (!OidIsValid(schemaid))
|
if (!OidIsValid(schemaid))
|
||||||
|
@ -1961,12 +1959,10 @@ get_object_address_publication_schema(List *object, bool missing_ok)
|
||||||
|
|
||||||
/* Find the publication schema mapping in syscache */
|
/* Find the publication schema mapping in syscache */
|
||||||
address.objectId =
|
address.objectId =
|
||||||
GetSysCacheOid3(PUBLICATIONNAMESPACEMAP,
|
GetSysCacheOid2(PUBLICATIONNAMESPACEMAP,
|
||||||
Anum_pg_publication_namespace_oid,
|
Anum_pg_publication_namespace_oid,
|
||||||
ObjectIdGetDatum(schemaid),
|
ObjectIdGetDatum(schemaid),
|
||||||
ObjectIdGetDatum(pub->oid),
|
ObjectIdGetDatum(pub->oid));
|
||||||
CharGetDatum(objtype[0]));
|
|
||||||
|
|
||||||
if (!OidIsValid(address.objectId) && !missing_ok)
|
if (!OidIsValid(address.objectId) && !missing_ok)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
@ -2247,6 +2243,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
|
||||||
case OBJECT_DOMCONSTRAINT:
|
case OBJECT_DOMCONSTRAINT:
|
||||||
case OBJECT_CAST:
|
case OBJECT_CAST:
|
||||||
case OBJECT_USER_MAPPING:
|
case OBJECT_USER_MAPPING:
|
||||||
|
case OBJECT_PUBLICATION_NAMESPACE:
|
||||||
case OBJECT_PUBLICATION_REL:
|
case OBJECT_PUBLICATION_REL:
|
||||||
case OBJECT_DEFACL:
|
case OBJECT_DEFACL:
|
||||||
case OBJECT_TRANSFORM:
|
case OBJECT_TRANSFORM:
|
||||||
|
@ -2271,7 +2268,6 @@ pg_get_object_address(PG_FUNCTION_ARGS)
|
||||||
/* fall through to check args length */
|
/* fall through to check args length */
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case OBJECT_OPERATOR:
|
case OBJECT_OPERATOR:
|
||||||
case OBJECT_PUBLICATION_NAMESPACE:
|
|
||||||
if (list_length(args) != 2)
|
if (list_length(args) != 2)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
@ -2343,8 +2339,6 @@ pg_get_object_address(PG_FUNCTION_ARGS)
|
||||||
objnode = (Node *) list_make2(name, linitial(args));
|
objnode = (Node *) list_make2(name, linitial(args));
|
||||||
break;
|
break;
|
||||||
case OBJECT_PUBLICATION_NAMESPACE:
|
case OBJECT_PUBLICATION_NAMESPACE:
|
||||||
objnode = (Node *) list_make3(linitial(name), linitial(args), lsecond(args));
|
|
||||||
break;
|
|
||||||
case OBJECT_USER_MAPPING:
|
case OBJECT_USER_MAPPING:
|
||||||
objnode = (Node *) list_make2(linitial(name), linitial(args));
|
objnode = (Node *) list_make2(linitial(name), linitial(args));
|
||||||
break;
|
break;
|
||||||
|
@ -2900,12 +2894,11 @@ get_catalog_object_by_oid(Relation catalog, AttrNumber oidcol, Oid objectId)
|
||||||
*
|
*
|
||||||
* Get publication name and schema name from the object address into pubname and
|
* Get publication name and schema name from the object address into pubname and
|
||||||
* nspname. Both pubname and nspname are palloc'd strings which will be freed by
|
* nspname. Both pubname and nspname are palloc'd strings which will be freed by
|
||||||
* the caller. The last parameter specifies which object type is included from
|
* the caller.
|
||||||
* the schema.
|
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok,
|
getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok,
|
||||||
char **pubname, char **nspname, char **objtype)
|
char **pubname, char **nspname)
|
||||||
{
|
{
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
Form_pg_publication_namespace pnform;
|
Form_pg_publication_namespace pnform;
|
||||||
|
@ -2941,13 +2934,6 @@ getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* The type is always a single character, but we need to pass it as a string,
|
|
||||||
* so allocate two charaters and set the first one. The second one is \0.
|
|
||||||
*/
|
|
||||||
*objtype = palloc0(2);
|
|
||||||
*objtype[0] = pnform->pntype;
|
|
||||||
|
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3979,17 +3965,15 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
|
||||||
{
|
{
|
||||||
char *pubname;
|
char *pubname;
|
||||||
char *nspname;
|
char *nspname;
|
||||||
char *objtype;
|
|
||||||
|
|
||||||
if (!getPublicationSchemaInfo(object, missing_ok,
|
if (!getPublicationSchemaInfo(object, missing_ok,
|
||||||
&pubname, &nspname, &objtype))
|
&pubname, &nspname))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
appendStringInfo(&buffer, _("publication of schema %s in publication %s type %s"),
|
appendStringInfo(&buffer, _("publication of schema %s in publication %s"),
|
||||||
nspname, pubname, objtype);
|
nspname, pubname);
|
||||||
pfree(pubname);
|
pfree(pubname);
|
||||||
pfree(nspname);
|
pfree(nspname);
|
||||||
pfree(objtype);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5816,24 +5800,18 @@ getObjectIdentityParts(const ObjectAddress *object,
|
||||||
{
|
{
|
||||||
char *pubname;
|
char *pubname;
|
||||||
char *nspname;
|
char *nspname;
|
||||||
char *objtype;
|
|
||||||
|
|
||||||
if (!getPublicationSchemaInfo(object, missing_ok, &pubname,
|
if (!getPublicationSchemaInfo(object, missing_ok, &pubname,
|
||||||
&nspname, &objtype))
|
&nspname))
|
||||||
break;
|
break;
|
||||||
appendStringInfo(&buffer, "%s in publication %s type %s",
|
appendStringInfo(&buffer, "%s in publication %s",
|
||||||
nspname, pubname, objtype);
|
nspname, pubname);
|
||||||
|
|
||||||
if (objargs)
|
if (objargs)
|
||||||
*objargs = list_make1(pubname);
|
*objargs = list_make1(pubname);
|
||||||
else
|
else
|
||||||
pfree(pubname);
|
pfree(pubname);
|
||||||
|
|
||||||
if (objargs)
|
|
||||||
*objargs = lappend(*objargs, objtype);
|
|
||||||
else
|
|
||||||
pfree(objtype);
|
|
||||||
|
|
||||||
if (objname)
|
if (objname)
|
||||||
*objname = list_make1(nspname);
|
*objname = list_make1(nspname);
|
||||||
else
|
else
|
||||||
|
|
|
@ -55,10 +55,9 @@ static void publication_translate_columns(Relation targetrel, List *columns,
|
||||||
static void
|
static void
|
||||||
check_publication_add_relation(Relation targetrel)
|
check_publication_add_relation(Relation targetrel)
|
||||||
{
|
{
|
||||||
/* Must be a regular or partitioned table, or a sequence */
|
/* Must be a regular or partitioned table */
|
||||||
if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION &&
|
if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION &&
|
||||||
RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE &&
|
RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE)
|
||||||
RelationGetForm(targetrel)->relkind != RELKIND_SEQUENCE)
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("cannot add relation \"%s\" to publication",
|
errmsg("cannot add relation \"%s\" to publication",
|
||||||
|
@ -135,8 +134,7 @@ static bool
|
||||||
is_publishable_class(Oid relid, Form_pg_class reltuple)
|
is_publishable_class(Oid relid, Form_pg_class reltuple)
|
||||||
{
|
{
|
||||||
return (reltuple->relkind == RELKIND_RELATION ||
|
return (reltuple->relkind == RELKIND_RELATION ||
|
||||||
reltuple->relkind == RELKIND_PARTITIONED_TABLE ||
|
reltuple->relkind == RELKIND_PARTITIONED_TABLE) &&
|
||||||
reltuple->relkind == RELKIND_SEQUENCE) &&
|
|
||||||
!IsCatalogRelationOid(relid) &&
|
!IsCatalogRelationOid(relid) &&
|
||||||
reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
|
reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
|
||||||
relid >= FirstNormalObjectId;
|
relid >= FirstNormalObjectId;
|
||||||
|
@ -181,52 +179,6 @@ filter_partitions(List *relids)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Check the character is a valid object type for schema publication.
|
|
||||||
*
|
|
||||||
* This recognizes either 't' for tables or 's' for sequences. Places that
|
|
||||||
* need to handle 'u' for unsupported relkinds need to do that explicitlyl
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
AssertObjectTypeValid(char objectType)
|
|
||||||
{
|
|
||||||
#ifdef USE_ASSERT_CHECKING
|
|
||||||
Assert(objectType == PUB_OBJTYPE_SEQUENCE || objectType == PUB_OBJTYPE_TABLE);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine object type matching a given a relkind value.
|
|
||||||
*/
|
|
||||||
char
|
|
||||||
pub_get_object_type_for_relkind(char relkind)
|
|
||||||
{
|
|
||||||
/* sequence maps directly to sequence relkind */
|
|
||||||
if (relkind == RELKIND_SEQUENCE)
|
|
||||||
return PUB_OBJTYPE_SEQUENCE;
|
|
||||||
|
|
||||||
/* for table, we match either regular or partitioned table */
|
|
||||||
if (relkind == RELKIND_RELATION ||
|
|
||||||
relkind == RELKIND_PARTITIONED_TABLE)
|
|
||||||
return PUB_OBJTYPE_TABLE;
|
|
||||||
|
|
||||||
return PUB_OBJTYPE_UNSUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine if publication object type matches the relkind.
|
|
||||||
*
|
|
||||||
* Returns true if the relation matches object type replicated by this schema,
|
|
||||||
* false otherwise.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
pub_object_type_matches_relkind(char objectType, char relkind)
|
|
||||||
{
|
|
||||||
AssertObjectTypeValid(objectType);
|
|
||||||
|
|
||||||
return (pub_get_object_type_for_relkind(relkind) == objectType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Another variant of this, taking a Relation.
|
* Another variant of this, taking a Relation.
|
||||||
*/
|
*/
|
||||||
|
@ -256,7 +208,7 @@ is_schema_publication(Oid pubid)
|
||||||
ObjectIdGetDatum(pubid));
|
ObjectIdGetDatum(pubid));
|
||||||
|
|
||||||
scan = systable_beginscan(pubschsrel,
|
scan = systable_beginscan(pubschsrel,
|
||||||
PublicationNamespacePnnspidPnpubidPntypeIndexId,
|
PublicationNamespacePnnspidPnpubidIndexId,
|
||||||
true, NULL, 1, &scankey);
|
true, NULL, 1, &scankey);
|
||||||
tup = systable_getnext(scan);
|
tup = systable_getnext(scan);
|
||||||
result = HeapTupleIsValid(tup);
|
result = HeapTupleIsValid(tup);
|
||||||
|
@ -364,9 +316,7 @@ GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* we only search for ancestors of tables, so PUB_OBJTYPE_TABLE */
|
aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
|
||||||
aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor),
|
|
||||||
PUB_OBJTYPE_TABLE);
|
|
||||||
if (list_member_oid(aschemaPubids, puboid))
|
if (list_member_oid(aschemaPubids, puboid))
|
||||||
{
|
{
|
||||||
topmost_relid = ancestor;
|
topmost_relid = ancestor;
|
||||||
|
@ -635,7 +585,7 @@ pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols, MemoryContext mcxt)
|
||||||
* Insert new publication / schema mapping.
|
* Insert new publication / schema mapping.
|
||||||
*/
|
*/
|
||||||
ObjectAddress
|
ObjectAddress
|
||||||
publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exists)
|
publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
|
||||||
{
|
{
|
||||||
Relation rel;
|
Relation rel;
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
|
@ -647,8 +597,6 @@ publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exi
|
||||||
ObjectAddress myself,
|
ObjectAddress myself,
|
||||||
referenced;
|
referenced;
|
||||||
|
|
||||||
AssertObjectTypeValid(objectType);
|
|
||||||
|
|
||||||
rel = table_open(PublicationNamespaceRelationId, RowExclusiveLock);
|
rel = table_open(PublicationNamespaceRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -656,10 +604,9 @@ publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exi
|
||||||
* duplicates, it's here just to provide nicer error message in common
|
* duplicates, it's here just to provide nicer error message in common
|
||||||
* case. The real protection is the unique key on the catalog.
|
* case. The real protection is the unique key on the catalog.
|
||||||
*/
|
*/
|
||||||
if (SearchSysCacheExists3(PUBLICATIONNAMESPACEMAP,
|
if (SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP,
|
||||||
ObjectIdGetDatum(schemaid),
|
ObjectIdGetDatum(schemaid),
|
||||||
ObjectIdGetDatum(pubid),
|
ObjectIdGetDatum(pubid)))
|
||||||
CharGetDatum(objectType)))
|
|
||||||
{
|
{
|
||||||
table_close(rel, RowExclusiveLock);
|
table_close(rel, RowExclusiveLock);
|
||||||
|
|
||||||
|
@ -685,8 +632,6 @@ publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exi
|
||||||
ObjectIdGetDatum(pubid);
|
ObjectIdGetDatum(pubid);
|
||||||
values[Anum_pg_publication_namespace_pnnspid - 1] =
|
values[Anum_pg_publication_namespace_pnnspid - 1] =
|
||||||
ObjectIdGetDatum(schemaid);
|
ObjectIdGetDatum(schemaid);
|
||||||
values[Anum_pg_publication_namespace_pntype - 1] =
|
|
||||||
CharGetDatum(objectType);
|
|
||||||
|
|
||||||
tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
|
tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
|
||||||
|
|
||||||
|
@ -712,7 +657,7 @@ publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exi
|
||||||
* publication_add_relation for why we need to consider all the
|
* publication_add_relation for why we need to consider all the
|
||||||
* partitions.
|
* partitions.
|
||||||
*/
|
*/
|
||||||
schemaRels = GetSchemaPublicationRelations(schemaid, objectType,
|
schemaRels = GetSchemaPublicationRelations(schemaid,
|
||||||
PUBLICATION_PART_ALL);
|
PUBLICATION_PART_ALL);
|
||||||
InvalidatePublicationRels(schemaRels);
|
InvalidatePublicationRels(schemaRels);
|
||||||
|
|
||||||
|
@ -746,14 +691,11 @@ GetRelationPublications(Oid relid)
|
||||||
/*
|
/*
|
||||||
* Gets list of relation oids for a publication.
|
* Gets list of relation oids for a publication.
|
||||||
*
|
*
|
||||||
* This should only be used FOR TABLE / FOR SEQUENCE publications, the FOR
|
* This should only be used FOR TABLE publications, the FOR ALL TABLES
|
||||||
* ALL TABLES / SEQUENCES should use GetAllTablesPublicationRelations()
|
* should use GetAllTablesPublicationRelations().
|
||||||
* and GetAllSequencesPublicationRelations().
|
|
||||||
*
|
|
||||||
* XXX pub_partopt only matters for tables, not sequences.
|
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
GetPublicationRelations(Oid pubid, char objectType, PublicationPartOpt pub_partopt)
|
GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
|
||||||
{
|
{
|
||||||
List *result;
|
List *result;
|
||||||
Relation pubrelsrel;
|
Relation pubrelsrel;
|
||||||
|
@ -761,8 +703,6 @@ GetPublicationRelations(Oid pubid, char objectType, PublicationPartOpt pub_parto
|
||||||
SysScanDesc scan;
|
SysScanDesc scan;
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
|
|
||||||
AssertObjectTypeValid(objectType);
|
|
||||||
|
|
||||||
/* Find all publications associated with the relation. */
|
/* Find all publications associated with the relation. */
|
||||||
pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
|
pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
@ -777,29 +717,11 @@ GetPublicationRelations(Oid pubid, char objectType, PublicationPartOpt pub_parto
|
||||||
result = NIL;
|
result = NIL;
|
||||||
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
||||||
{
|
{
|
||||||
char relkind;
|
|
||||||
Form_pg_publication_rel pubrel;
|
Form_pg_publication_rel pubrel;
|
||||||
|
|
||||||
pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
|
pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
|
||||||
relkind = get_rel_relkind(pubrel->prrelid);
|
result = GetPubPartitionOptionRelations(result, pub_partopt,
|
||||||
|
pubrel->prrelid);
|
||||||
/*
|
|
||||||
* If the relkind does not match the requested object type, ignore the
|
|
||||||
* relation. For example we might be interested only in sequences, so
|
|
||||||
* we ignore tables.
|
|
||||||
*/
|
|
||||||
if (!pub_object_type_matches_relkind(objectType, relkind))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We don't have partitioned sequences, so just add them to the list.
|
|
||||||
* Otherwise consider adding all child relations, if requested.
|
|
||||||
*/
|
|
||||||
if (relkind == RELKIND_SEQUENCE)
|
|
||||||
result = lappend_oid(result, pubrel->prrelid);
|
|
||||||
else
|
|
||||||
result = GetPubPartitionOptionRelations(result, pub_partopt,
|
|
||||||
pubrel->prrelid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
systable_endscan(scan);
|
systable_endscan(scan);
|
||||||
|
@ -849,43 +771,6 @@ GetAllTablesPublications(void)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Gets list of publication oids for publications marked as FOR ALL SEQUENCES.
|
|
||||||
*/
|
|
||||||
List *
|
|
||||||
GetAllSequencesPublications(void)
|
|
||||||
{
|
|
||||||
List *result;
|
|
||||||
Relation rel;
|
|
||||||
ScanKeyData scankey;
|
|
||||||
SysScanDesc scan;
|
|
||||||
HeapTuple tup;
|
|
||||||
|
|
||||||
/* Find all publications that are marked as for all sequences. */
|
|
||||||
rel = table_open(PublicationRelationId, AccessShareLock);
|
|
||||||
|
|
||||||
ScanKeyInit(&scankey,
|
|
||||||
Anum_pg_publication_puballsequences,
|
|
||||||
BTEqualStrategyNumber, F_BOOLEQ,
|
|
||||||
BoolGetDatum(true));
|
|
||||||
|
|
||||||
scan = systable_beginscan(rel, InvalidOid, false,
|
|
||||||
NULL, 1, &scankey);
|
|
||||||
|
|
||||||
result = NIL;
|
|
||||||
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
|
||||||
{
|
|
||||||
Oid oid = ((Form_pg_publication) GETSTRUCT(tup))->oid;
|
|
||||||
|
|
||||||
result = lappend_oid(result, oid);
|
|
||||||
}
|
|
||||||
|
|
||||||
systable_endscan(scan);
|
|
||||||
table_close(rel, AccessShareLock);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gets list of all relation published by FOR ALL TABLES publication(s).
|
* Gets list of all relation published by FOR ALL TABLES publication(s).
|
||||||
*
|
*
|
||||||
|
@ -952,38 +837,28 @@ GetAllTablesPublicationRelations(bool pubviaroot)
|
||||||
/*
|
/*
|
||||||
* Gets the list of schema oids for a publication.
|
* Gets the list of schema oids for a publication.
|
||||||
*
|
*
|
||||||
* This should only be used FOR ALL TABLES IN SCHEMA and FOR ALL SEQUENCES
|
* This should only be used FOR ALL TABLES IN SCHEMA publications.
|
||||||
* publications.
|
|
||||||
*
|
|
||||||
* 'objectType' determines whether to get FOR TABLE or FOR SEQUENCES schemas
|
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
GetPublicationSchemas(Oid pubid, char objectType)
|
GetPublicationSchemas(Oid pubid)
|
||||||
{
|
{
|
||||||
List *result = NIL;
|
List *result = NIL;
|
||||||
Relation pubschsrel;
|
Relation pubschsrel;
|
||||||
ScanKeyData scankey[2];
|
ScanKeyData scankey;
|
||||||
SysScanDesc scan;
|
SysScanDesc scan;
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
|
|
||||||
AssertObjectTypeValid(objectType);
|
|
||||||
|
|
||||||
/* Find all schemas associated with the publication */
|
/* Find all schemas associated with the publication */
|
||||||
pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
|
pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
|
||||||
|
|
||||||
ScanKeyInit(&scankey[0],
|
ScanKeyInit(&scankey,
|
||||||
Anum_pg_publication_namespace_pnpubid,
|
Anum_pg_publication_namespace_pnpubid,
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(pubid));
|
ObjectIdGetDatum(pubid));
|
||||||
|
|
||||||
ScanKeyInit(&scankey[1],
|
|
||||||
Anum_pg_publication_namespace_pntype,
|
|
||||||
BTEqualStrategyNumber, F_CHAREQ,
|
|
||||||
CharGetDatum(objectType));
|
|
||||||
|
|
||||||
scan = systable_beginscan(pubschsrel,
|
scan = systable_beginscan(pubschsrel,
|
||||||
PublicationNamespacePnnspidPnpubidPntypeIndexId,
|
PublicationNamespacePnnspidPnpubidIndexId,
|
||||||
true, NULL, 2, scankey);
|
true, NULL, 1, &scankey);
|
||||||
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
||||||
{
|
{
|
||||||
Form_pg_publication_namespace pubsch;
|
Form_pg_publication_namespace pubsch;
|
||||||
|
@ -1001,26 +876,14 @@ GetPublicationSchemas(Oid pubid, char objectType)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gets the list of publication oids associated with a specified schema.
|
* Gets the list of publication oids associated with a specified schema.
|
||||||
*
|
|
||||||
* objectType specifies whether we're looking for schemas including tables or
|
|
||||||
* sequences.
|
|
||||||
*
|
|
||||||
* Note: relcache calls this for all object types, not just tables and sequences.
|
|
||||||
* Which is why we handle the PUB_OBJTYPE_UNSUPPORTED object type too.
|
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
GetSchemaPublications(Oid schemaid, char objectType)
|
GetSchemaPublications(Oid schemaid)
|
||||||
{
|
{
|
||||||
List *result = NIL;
|
List *result = NIL;
|
||||||
CatCList *pubschlist;
|
CatCList *pubschlist;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* unsupported object type */
|
|
||||||
if (objectType == PUB_OBJTYPE_UNSUPPORTED)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
AssertObjectTypeValid(objectType);
|
|
||||||
|
|
||||||
/* Find all publications associated with the schema */
|
/* Find all publications associated with the schema */
|
||||||
pubschlist = SearchSysCacheList1(PUBLICATIONNAMESPACEMAP,
|
pubschlist = SearchSysCacheList1(PUBLICATIONNAMESPACEMAP,
|
||||||
ObjectIdGetDatum(schemaid));
|
ObjectIdGetDatum(schemaid));
|
||||||
|
@ -1028,11 +891,6 @@ GetSchemaPublications(Oid schemaid, char objectType)
|
||||||
{
|
{
|
||||||
HeapTuple tup = &pubschlist->members[i]->tuple;
|
HeapTuple tup = &pubschlist->members[i]->tuple;
|
||||||
Oid pubid = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pnpubid;
|
Oid pubid = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pnpubid;
|
||||||
char pntype = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pntype;
|
|
||||||
|
|
||||||
/* Skip schemas publishing a different object type. */
|
|
||||||
if (pntype != objectType)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
result = lappend_oid(result, pubid);
|
result = lappend_oid(result, pubid);
|
||||||
}
|
}
|
||||||
|
@ -1044,13 +902,9 @@ GetSchemaPublications(Oid schemaid, char objectType)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the list of publishable relation oids for a specified schema.
|
* Get the list of publishable relation oids for a specified schema.
|
||||||
*
|
|
||||||
* objectType specifies whether this is FOR ALL TABLES IN SCHEMA or FOR ALL
|
|
||||||
* SEQUENCES IN SCHEMA
|
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
GetSchemaPublicationRelations(Oid schemaid, char objectType,
|
GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
|
||||||
PublicationPartOpt pub_partopt)
|
|
||||||
{
|
{
|
||||||
Relation classRel;
|
Relation classRel;
|
||||||
ScanKeyData key[1];
|
ScanKeyData key[1];
|
||||||
|
@ -1059,7 +913,6 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
|
||||||
List *result = NIL;
|
List *result = NIL;
|
||||||
|
|
||||||
Assert(OidIsValid(schemaid));
|
Assert(OidIsValid(schemaid));
|
||||||
AssertObjectTypeValid(objectType);
|
|
||||||
|
|
||||||
classRel = table_open(RelationRelationId, AccessShareLock);
|
classRel = table_open(RelationRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
@ -1080,16 +933,9 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
relkind = get_rel_relkind(relid);
|
relkind = get_rel_relkind(relid);
|
||||||
|
if (relkind == RELKIND_RELATION)
|
||||||
/* Skip if the relkind does not match FOR ALL TABLES / SEQUENCES. */
|
result = lappend_oid(result, relid);
|
||||||
if (!pub_object_type_matches_relkind(objectType, relkind))
|
else if (relkind == RELKIND_PARTITIONED_TABLE)
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the object is a partitioned table, lookup all the child relations
|
|
||||||
* (if requested). Otherwise just add the object to the list.
|
|
||||||
*/
|
|
||||||
if (relkind == RELKIND_PARTITIONED_TABLE)
|
|
||||||
{
|
{
|
||||||
List *partitionrels = NIL;
|
List *partitionrels = NIL;
|
||||||
|
|
||||||
|
@ -1102,11 +948,7 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
|
||||||
pub_partopt,
|
pub_partopt,
|
||||||
relForm->oid);
|
relForm->oid);
|
||||||
result = list_concat_unique_oid(result, partitionrels);
|
result = list_concat_unique_oid(result, partitionrels);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* non-partitioned tables and sequences */
|
|
||||||
result = lappend_oid(result, relid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table_endscan(scan);
|
table_endscan(scan);
|
||||||
|
@ -1116,67 +958,27 @@ GetSchemaPublicationRelations(Oid schemaid, char objectType,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gets the list of all relations published by FOR ALL TABLES IN SCHEMA
|
* Gets the list of all relations published by FOR ALL TABLES IN SCHEMA
|
||||||
* or FOR ALL SEQUENCES IN SCHEMA publication.
|
* publication.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
GetAllSchemaPublicationRelations(Oid pubid, char objectType,
|
GetAllSchemaPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
|
||||||
PublicationPartOpt pub_partopt)
|
|
||||||
{
|
{
|
||||||
List *result = NIL;
|
List *result = NIL;
|
||||||
List *pubschemalist = GetPublicationSchemas(pubid, objectType);
|
List *pubschemalist = GetPublicationSchemas(pubid);
|
||||||
ListCell *cell;
|
ListCell *cell;
|
||||||
|
|
||||||
AssertObjectTypeValid(objectType);
|
|
||||||
|
|
||||||
foreach(cell, pubschemalist)
|
foreach(cell, pubschemalist)
|
||||||
{
|
{
|
||||||
Oid schemaid = lfirst_oid(cell);
|
Oid schemaid = lfirst_oid(cell);
|
||||||
List *schemaRels = NIL;
|
List *schemaRels = NIL;
|
||||||
|
|
||||||
schemaRels = GetSchemaPublicationRelations(schemaid, objectType,
|
schemaRels = GetSchemaPublicationRelations(schemaid, pub_partopt);
|
||||||
pub_partopt);
|
|
||||||
result = list_concat(result, schemaRels);
|
result = list_concat(result, schemaRels);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Gets list of all relation published by FOR ALL SEQUENCES publication(s).
|
|
||||||
*/
|
|
||||||
List *
|
|
||||||
GetAllSequencesPublicationRelations(void)
|
|
||||||
{
|
|
||||||
Relation classRel;
|
|
||||||
ScanKeyData key[1];
|
|
||||||
TableScanDesc scan;
|
|
||||||
HeapTuple tuple;
|
|
||||||
List *result = NIL;
|
|
||||||
|
|
||||||
classRel = table_open(RelationRelationId, AccessShareLock);
|
|
||||||
|
|
||||||
ScanKeyInit(&key[0],
|
|
||||||
Anum_pg_class_relkind,
|
|
||||||
BTEqualStrategyNumber, F_CHAREQ,
|
|
||||||
CharGetDatum(RELKIND_SEQUENCE));
|
|
||||||
|
|
||||||
scan = table_beginscan_catalog(classRel, 1, key);
|
|
||||||
|
|
||||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
|
||||||
{
|
|
||||||
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
|
|
||||||
Oid relid = relForm->oid;
|
|
||||||
|
|
||||||
if (is_publishable_class(relid, relForm))
|
|
||||||
result = lappend_oid(result, relid);
|
|
||||||
}
|
|
||||||
|
|
||||||
table_endscan(scan);
|
|
||||||
|
|
||||||
table_close(classRel, AccessShareLock);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get publication using oid
|
* Get publication using oid
|
||||||
*
|
*
|
||||||
|
@ -1199,12 +1001,10 @@ GetPublication(Oid pubid)
|
||||||
pub->oid = pubid;
|
pub->oid = pubid;
|
||||||
pub->name = pstrdup(NameStr(pubform->pubname));
|
pub->name = pstrdup(NameStr(pubform->pubname));
|
||||||
pub->alltables = pubform->puballtables;
|
pub->alltables = pubform->puballtables;
|
||||||
pub->allsequences = pubform->puballsequences;
|
|
||||||
pub->pubactions.pubinsert = pubform->pubinsert;
|
pub->pubactions.pubinsert = pubform->pubinsert;
|
||||||
pub->pubactions.pubupdate = pubform->pubupdate;
|
pub->pubactions.pubupdate = pubform->pubupdate;
|
||||||
pub->pubactions.pubdelete = pubform->pubdelete;
|
pub->pubactions.pubdelete = pubform->pubdelete;
|
||||||
pub->pubactions.pubtruncate = pubform->pubtruncate;
|
pub->pubactions.pubtruncate = pubform->pubtruncate;
|
||||||
pub->pubactions.pubsequence = pubform->pubsequence;
|
|
||||||
pub->pubviaroot = pubform->pubviaroot;
|
pub->pubviaroot = pubform->pubviaroot;
|
||||||
|
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
|
@ -1315,12 +1115,10 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
|
||||||
*schemarelids;
|
*schemarelids;
|
||||||
|
|
||||||
relids = GetPublicationRelations(publication->oid,
|
relids = GetPublicationRelations(publication->oid,
|
||||||
PUB_OBJTYPE_TABLE,
|
|
||||||
publication->pubviaroot ?
|
publication->pubviaroot ?
|
||||||
PUBLICATION_PART_ROOT :
|
PUBLICATION_PART_ROOT :
|
||||||
PUBLICATION_PART_LEAF);
|
PUBLICATION_PART_LEAF);
|
||||||
schemarelids = GetAllSchemaPublicationRelations(publication->oid,
|
schemarelids = GetAllSchemaPublicationRelations(publication->oid,
|
||||||
PUB_OBJTYPE_TABLE,
|
|
||||||
publication->pubviaroot ?
|
publication->pubviaroot ?
|
||||||
PUBLICATION_PART_ROOT :
|
PUBLICATION_PART_ROOT :
|
||||||
PUBLICATION_PART_LEAF);
|
PUBLICATION_PART_LEAF);
|
||||||
|
@ -1356,71 +1154,3 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
SRF_RETURN_DONE(funcctx);
|
SRF_RETURN_DONE(funcctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns Oids of sequences in a publication.
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
pg_get_publication_sequences(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
FuncCallContext *funcctx;
|
|
||||||
char *pubname = text_to_cstring(PG_GETARG_TEXT_PP(0));
|
|
||||||
Publication *publication;
|
|
||||||
List *sequences;
|
|
||||||
|
|
||||||
/* stuff done only on the first call of the function */
|
|
||||||
if (SRF_IS_FIRSTCALL())
|
|
||||||
{
|
|
||||||
MemoryContext oldcontext;
|
|
||||||
|
|
||||||
/* create a function context for cross-call persistence */
|
|
||||||
funcctx = SRF_FIRSTCALL_INIT();
|
|
||||||
|
|
||||||
/* switch to memory context appropriate for multiple function calls */
|
|
||||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
|
||||||
|
|
||||||
publication = GetPublicationByName(pubname, false);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Publications support partitioned tables, although all changes are
|
|
||||||
* replicated using leaf partition identity and schema, so we only
|
|
||||||
* need those.
|
|
||||||
*/
|
|
||||||
if (publication->allsequences)
|
|
||||||
sequences = GetAllSequencesPublicationRelations();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
List *relids,
|
|
||||||
*schemarelids;
|
|
||||||
|
|
||||||
relids = GetPublicationRelations(publication->oid,
|
|
||||||
PUB_OBJTYPE_SEQUENCE,
|
|
||||||
publication->pubviaroot ?
|
|
||||||
PUBLICATION_PART_ROOT :
|
|
||||||
PUBLICATION_PART_LEAF);
|
|
||||||
schemarelids = GetAllSchemaPublicationRelations(publication->oid,
|
|
||||||
PUB_OBJTYPE_SEQUENCE,
|
|
||||||
publication->pubviaroot ?
|
|
||||||
PUBLICATION_PART_ROOT :
|
|
||||||
PUBLICATION_PART_LEAF);
|
|
||||||
sequences = list_concat_unique_oid(relids, schemarelids);
|
|
||||||
}
|
|
||||||
|
|
||||||
funcctx->user_fctx = (void *) sequences;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* stuff done on every call of the function */
|
|
||||||
funcctx = SRF_PERCALL_SETUP();
|
|
||||||
sequences = (List *) funcctx->user_fctx;
|
|
||||||
|
|
||||||
if (funcctx->call_cntr < list_length(sequences))
|
|
||||||
{
|
|
||||||
Oid relid = list_nth_oid(sequences, funcctx->call_cntr);
|
|
||||||
|
|
||||||
SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid));
|
|
||||||
}
|
|
||||||
|
|
||||||
SRF_RETURN_DONE(funcctx);
|
|
||||||
}
|
|
||||||
|
|
|
@ -374,16 +374,6 @@ CREATE VIEW pg_publication_tables AS
|
||||||
pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
||||||
WHERE C.oid = GPT.relid;
|
WHERE C.oid = GPT.relid;
|
||||||
|
|
||||||
CREATE VIEW pg_publication_sequences AS
|
|
||||||
SELECT
|
|
||||||
P.pubname AS pubname,
|
|
||||||
N.nspname AS schemaname,
|
|
||||||
C.relname AS sequencename
|
|
||||||
FROM pg_publication P,
|
|
||||||
LATERAL pg_get_publication_sequences(P.pubname) GPS,
|
|
||||||
pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
|
||||||
WHERE C.oid = GPS.relid;
|
|
||||||
|
|
||||||
CREATE VIEW pg_locks AS
|
CREATE VIEW pg_locks AS
|
||||||
SELECT * FROM pg_lock_status() AS L;
|
SELECT * FROM pg_lock_status() AS L;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
#include "access/genam.h"
|
#include "access/genam.h"
|
||||||
#include "access/htup_details.h"
|
#include "access/htup_details.h"
|
||||||
#include "access/relation.h"
|
|
||||||
#include "access/table.h"
|
#include "access/table.h"
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
|
@ -68,17 +67,15 @@ typedef struct rf_context
|
||||||
} rf_context;
|
} rf_context;
|
||||||
|
|
||||||
static List *OpenRelIdList(List *relids);
|
static List *OpenRelIdList(List *relids);
|
||||||
static List *OpenRelationList(List *rels, char objectType);
|
static List *OpenTableList(List *tables);
|
||||||
static void CloseRelationList(List *rels);
|
static void CloseTableList(List *rels);
|
||||||
static void LockSchemaList(List *schemalist);
|
static void LockSchemaList(List *schemalist);
|
||||||
static void PublicationAddRelations(Oid pubid, List *rels, bool if_not_exists,
|
static void PublicationAddTables(Oid pubid, List *rels, bool if_not_exists,
|
||||||
AlterPublicationStmt *stmt);
|
AlterPublicationStmt *stmt);
|
||||||
static void PublicationDropRelations(Oid pubid, List *rels, bool missing_ok);
|
static void PublicationDropTables(Oid pubid, List *rels, bool missing_ok);
|
||||||
static void PublicationAddSchemas(Oid pubid, List *schemas, char objectType,
|
static void PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists,
|
||||||
bool if_not_exists, AlterPublicationStmt *stmt);
|
AlterPublicationStmt *stmt);
|
||||||
static void PublicationDropSchemas(Oid pubid, List *schemas, char objectType,
|
static void PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok);
|
||||||
bool missing_ok);
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
parse_publication_options(ParseState *pstate,
|
parse_publication_options(ParseState *pstate,
|
||||||
|
@ -98,7 +95,6 @@ parse_publication_options(ParseState *pstate,
|
||||||
pubactions->pubupdate = true;
|
pubactions->pubupdate = true;
|
||||||
pubactions->pubdelete = true;
|
pubactions->pubdelete = true;
|
||||||
pubactions->pubtruncate = true;
|
pubactions->pubtruncate = true;
|
||||||
pubactions->pubsequence = true;
|
|
||||||
*publish_via_partition_root = false;
|
*publish_via_partition_root = false;
|
||||||
|
|
||||||
/* Parse options */
|
/* Parse options */
|
||||||
|
@ -123,7 +119,6 @@ parse_publication_options(ParseState *pstate,
|
||||||
pubactions->pubupdate = false;
|
pubactions->pubupdate = false;
|
||||||
pubactions->pubdelete = false;
|
pubactions->pubdelete = false;
|
||||||
pubactions->pubtruncate = false;
|
pubactions->pubtruncate = false;
|
||||||
pubactions->pubsequence = false;
|
|
||||||
|
|
||||||
*publish_given = true;
|
*publish_given = true;
|
||||||
publish = defGetString(defel);
|
publish = defGetString(defel);
|
||||||
|
@ -146,8 +141,6 @@ parse_publication_options(ParseState *pstate,
|
||||||
pubactions->pubdelete = true;
|
pubactions->pubdelete = true;
|
||||||
else if (strcmp(publish_opt, "truncate") == 0)
|
else if (strcmp(publish_opt, "truncate") == 0)
|
||||||
pubactions->pubtruncate = true;
|
pubactions->pubtruncate = true;
|
||||||
else if (strcmp(publish_opt, "sequence") == 0)
|
|
||||||
pubactions->pubsequence = true;
|
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
@ -174,8 +167,7 @@ parse_publication_options(ParseState *pstate,
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
|
ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
|
||||||
List **tables, List **sequences,
|
List **rels, List **schemas)
|
||||||
List **tables_schemas, List **sequences_schemas)
|
|
||||||
{
|
{
|
||||||
ListCell *cell;
|
ListCell *cell;
|
||||||
PublicationObjSpec *pubobj;
|
PublicationObjSpec *pubobj;
|
||||||
|
@ -193,22 +185,13 @@ ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
|
||||||
switch (pubobj->pubobjtype)
|
switch (pubobj->pubobjtype)
|
||||||
{
|
{
|
||||||
case PUBLICATIONOBJ_TABLE:
|
case PUBLICATIONOBJ_TABLE:
|
||||||
*tables = lappend(*tables, pubobj->pubtable);
|
*rels = lappend(*rels, pubobj->pubtable);
|
||||||
break;
|
|
||||||
case PUBLICATIONOBJ_SEQUENCE:
|
|
||||||
*sequences = lappend(*sequences, pubobj->pubtable);
|
|
||||||
break;
|
break;
|
||||||
case PUBLICATIONOBJ_TABLES_IN_SCHEMA:
|
case PUBLICATIONOBJ_TABLES_IN_SCHEMA:
|
||||||
schemaid = get_namespace_oid(pubobj->name, false);
|
schemaid = get_namespace_oid(pubobj->name, false);
|
||||||
|
|
||||||
/* Filter out duplicates if user specifies "sch1, sch1" */
|
/* Filter out duplicates if user specifies "sch1, sch1" */
|
||||||
*tables_schemas = list_append_unique_oid(*tables_schemas, schemaid);
|
*schemas = list_append_unique_oid(*schemas, schemaid);
|
||||||
break;
|
|
||||||
case PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA:
|
|
||||||
schemaid = get_namespace_oid(pubobj->name, false);
|
|
||||||
|
|
||||||
/* Filter out duplicates if user specifies "sch1, sch1" */
|
|
||||||
*sequences_schemas = list_append_unique_oid(*sequences_schemas, schemaid);
|
|
||||||
break;
|
break;
|
||||||
case PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA:
|
case PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA:
|
||||||
search_path = fetch_search_path(false);
|
search_path = fetch_search_path(false);
|
||||||
|
@ -221,20 +204,7 @@ ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
|
||||||
list_free(search_path);
|
list_free(search_path);
|
||||||
|
|
||||||
/* Filter out duplicates if user specifies "sch1, sch1" */
|
/* Filter out duplicates if user specifies "sch1, sch1" */
|
||||||
*tables_schemas = list_append_unique_oid(*tables_schemas, schemaid);
|
*schemas = list_append_unique_oid(*schemas, schemaid);
|
||||||
break;
|
|
||||||
case PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA:
|
|
||||||
search_path = fetch_search_path(false);
|
|
||||||
if (search_path == NIL) /* nothing valid in search_path? */
|
|
||||||
ereport(ERROR,
|
|
||||||
errcode(ERRCODE_UNDEFINED_SCHEMA),
|
|
||||||
errmsg("no schema has been selected for CURRENT_SCHEMA"));
|
|
||||||
|
|
||||||
schemaid = linitial_oid(search_path);
|
|
||||||
list_free(search_path);
|
|
||||||
|
|
||||||
/* Filter out duplicates if user specifies "sch1, sch1" */
|
|
||||||
*sequences_schemas = list_append_unique_oid(*sequences_schemas, schemaid);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* shouldn't happen */
|
/* shouldn't happen */
|
||||||
|
@ -270,14 +240,6 @@ CheckObjSchemaNotAlreadyInPublication(List *rels, List *schemaidlist,
|
||||||
errdetail("Table \"%s\" in schema \"%s\" is already part of the publication, adding the same schema is not supported.",
|
errdetail("Table \"%s\" in schema \"%s\" is already part of the publication, adding the same schema is not supported.",
|
||||||
RelationGetRelationName(rel),
|
RelationGetRelationName(rel),
|
||||||
get_namespace_name(relSchemaId)));
|
get_namespace_name(relSchemaId)));
|
||||||
else if (checkobjtype == PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA)
|
|
||||||
ereport(ERROR,
|
|
||||||
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("cannot add schema \"%s\" to publication",
|
|
||||||
get_namespace_name(relSchemaId)),
|
|
||||||
errdetail("Sequence \"%s\" in schema \"%s\" is already part of the publication, adding the same schema is not supported.",
|
|
||||||
RelationGetRelationName(rel),
|
|
||||||
get_namespace_name(relSchemaId)));
|
|
||||||
else if (checkobjtype == PUBLICATIONOBJ_TABLE)
|
else if (checkobjtype == PUBLICATIONOBJ_TABLE)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
@ -286,14 +248,6 @@ CheckObjSchemaNotAlreadyInPublication(List *rels, List *schemaidlist,
|
||||||
RelationGetRelationName(rel)),
|
RelationGetRelationName(rel)),
|
||||||
errdetail("Table's schema \"%s\" is already part of the publication or part of the specified schema list.",
|
errdetail("Table's schema \"%s\" is already part of the publication or part of the specified schema list.",
|
||||||
get_namespace_name(relSchemaId)));
|
get_namespace_name(relSchemaId)));
|
||||||
else if (checkobjtype == PUBLICATIONOBJ_SEQUENCE)
|
|
||||||
ereport(ERROR,
|
|
||||||
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("cannot add relation \"%s.%s\" to publication",
|
|
||||||
get_namespace_name(relSchemaId),
|
|
||||||
RelationGetRelationName(rel)),
|
|
||||||
errdetail("Sequence's schema \"%s\" is already part of the publication or part of the specified schema list.",
|
|
||||||
get_namespace_name(relSchemaId)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -799,7 +753,6 @@ CheckPubRelationColumnList(List *tables, const char *queryString,
|
||||||
ObjectAddress
|
ObjectAddress
|
||||||
CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
|
CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
|
||||||
Relation rel;
|
Relation rel;
|
||||||
ObjectAddress myself;
|
ObjectAddress myself;
|
||||||
Oid puboid;
|
Oid puboid;
|
||||||
|
@ -811,23 +764,8 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
|
||||||
bool publish_via_partition_root_given;
|
bool publish_via_partition_root_given;
|
||||||
bool publish_via_partition_root;
|
bool publish_via_partition_root;
|
||||||
AclResult aclresult;
|
AclResult aclresult;
|
||||||
List *tables = NIL;
|
List *relations = NIL;
|
||||||
List *sequences = NIL;
|
List *schemaidlist = NIL;
|
||||||
List *tables_schemaidlist = NIL;
|
|
||||||
List *sequences_schemaidlist = NIL;
|
|
||||||
|
|
||||||
bool for_all_tables = false;
|
|
||||||
bool for_all_sequences = false;
|
|
||||||
|
|
||||||
/* Translate the list of object types (represented by strings) to bool flags. */
|
|
||||||
foreach (lc, stmt->for_all_objects)
|
|
||||||
{
|
|
||||||
char *val = strVal(lfirst(lc));
|
|
||||||
if (strcmp(val, "tables") == 0)
|
|
||||||
for_all_tables = true;
|
|
||||||
else if (strcmp(val, "sequences") == 0)
|
|
||||||
for_all_sequences = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* must have CREATE privilege on database */
|
/* must have CREATE privilege on database */
|
||||||
aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
|
aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
|
||||||
|
@ -836,17 +774,11 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
|
||||||
get_database_name(MyDatabaseId));
|
get_database_name(MyDatabaseId));
|
||||||
|
|
||||||
/* FOR ALL TABLES requires superuser */
|
/* FOR ALL TABLES requires superuser */
|
||||||
if (for_all_tables && !superuser())
|
if (stmt->for_all_tables && !superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
errmsg("must be superuser to create FOR ALL TABLES publication")));
|
errmsg("must be superuser to create FOR ALL TABLES publication")));
|
||||||
|
|
||||||
/* FOR ALL SEQUENCES requires superuser */
|
|
||||||
if (for_all_sequences && !superuser())
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
||||||
errmsg("must be superuser to create FOR ALL SEQUENCES publication")));
|
|
||||||
|
|
||||||
rel = table_open(PublicationRelationId, RowExclusiveLock);
|
rel = table_open(PublicationRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
/* Check if name is used */
|
/* Check if name is used */
|
||||||
|
@ -878,9 +810,7 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
|
||||||
Anum_pg_publication_oid);
|
Anum_pg_publication_oid);
|
||||||
values[Anum_pg_publication_oid - 1] = ObjectIdGetDatum(puboid);
|
values[Anum_pg_publication_oid - 1] = ObjectIdGetDatum(puboid);
|
||||||
values[Anum_pg_publication_puballtables - 1] =
|
values[Anum_pg_publication_puballtables - 1] =
|
||||||
BoolGetDatum(for_all_tables);
|
BoolGetDatum(stmt->for_all_tables);
|
||||||
values[Anum_pg_publication_puballsequences - 1] =
|
|
||||||
BoolGetDatum(for_all_sequences);
|
|
||||||
values[Anum_pg_publication_pubinsert - 1] =
|
values[Anum_pg_publication_pubinsert - 1] =
|
||||||
BoolGetDatum(pubactions.pubinsert);
|
BoolGetDatum(pubactions.pubinsert);
|
||||||
values[Anum_pg_publication_pubupdate - 1] =
|
values[Anum_pg_publication_pubupdate - 1] =
|
||||||
|
@ -889,8 +819,6 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
|
||||||
BoolGetDatum(pubactions.pubdelete);
|
BoolGetDatum(pubactions.pubdelete);
|
||||||
values[Anum_pg_publication_pubtruncate - 1] =
|
values[Anum_pg_publication_pubtruncate - 1] =
|
||||||
BoolGetDatum(pubactions.pubtruncate);
|
BoolGetDatum(pubactions.pubtruncate);
|
||||||
values[Anum_pg_publication_pubsequence - 1] =
|
|
||||||
BoolGetDatum(pubactions.pubsequence);
|
|
||||||
values[Anum_pg_publication_pubviaroot - 1] =
|
values[Anum_pg_publication_pubviaroot - 1] =
|
||||||
BoolGetDatum(publish_via_partition_root);
|
BoolGetDatum(publish_via_partition_root);
|
||||||
|
|
||||||
|
@ -908,42 +836,28 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
/* Associate objects with the publication. */
|
/* Associate objects with the publication. */
|
||||||
if (for_all_tables || for_all_sequences)
|
if (stmt->for_all_tables)
|
||||||
{
|
{
|
||||||
/* Invalidate relcache so that publication info is rebuilt. */
|
/* Invalidate relcache so that publication info is rebuilt. */
|
||||||
CacheInvalidateRelcacheAll();
|
CacheInvalidateRelcacheAll();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/*
|
|
||||||
* If the publication might have either tables or sequences (directly or
|
|
||||||
* through a schema), process that.
|
|
||||||
*/
|
|
||||||
if (!for_all_tables || !for_all_sequences)
|
|
||||||
{
|
{
|
||||||
ObjectsInPublicationToOids(stmt->pubobjects, pstate,
|
ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
|
||||||
&tables, &sequences,
|
&schemaidlist);
|
||||||
&tables_schemaidlist,
|
|
||||||
&sequences_schemaidlist);
|
|
||||||
|
|
||||||
/* FOR ALL TABLES IN SCHEMA requires superuser */
|
/* FOR ALL TABLES IN SCHEMA requires superuser */
|
||||||
if (list_length(tables_schemaidlist) > 0 && !superuser())
|
if (list_length(schemaidlist) > 0 && !superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication"));
|
errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication"));
|
||||||
|
|
||||||
/* FOR ALL SEQUENCES IN SCHEMA requires superuser */
|
if (list_length(relations) > 0)
|
||||||
if (list_length(sequences_schemaidlist) > 0 && !superuser())
|
|
||||||
ereport(ERROR,
|
|
||||||
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
||||||
errmsg("must be superuser to create FOR ALL SEQUENCES IN SCHEMA publication"));
|
|
||||||
|
|
||||||
/* tables added directly */
|
|
||||||
if (list_length(tables) > 0)
|
|
||||||
{
|
{
|
||||||
List *rels;
|
List *rels;
|
||||||
|
|
||||||
rels = OpenRelationList(tables, PUB_OBJTYPE_TABLE);
|
rels = OpenTableList(relations);
|
||||||
CheckObjSchemaNotAlreadyInPublication(rels, tables_schemaidlist,
|
CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
|
||||||
PUBLICATIONOBJ_TABLE);
|
PUBLICATIONOBJ_TABLE);
|
||||||
|
|
||||||
TransformPubWhereClauses(rels, pstate->p_sourcetext,
|
TransformPubWhereClauses(rels, pstate->p_sourcetext,
|
||||||
|
@ -952,46 +866,18 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
|
||||||
CheckPubRelationColumnList(rels, pstate->p_sourcetext,
|
CheckPubRelationColumnList(rels, pstate->p_sourcetext,
|
||||||
publish_via_partition_root);
|
publish_via_partition_root);
|
||||||
|
|
||||||
PublicationAddRelations(puboid, rels, true, NULL);
|
PublicationAddTables(puboid, rels, true, NULL);
|
||||||
CloseRelationList(rels);
|
CloseTableList(rels);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sequences added directly */
|
if (list_length(schemaidlist) > 0)
|
||||||
if (list_length(sequences) > 0)
|
|
||||||
{
|
|
||||||
List *rels;
|
|
||||||
|
|
||||||
rels = OpenRelationList(sequences, PUB_OBJTYPE_SEQUENCE);
|
|
||||||
CheckObjSchemaNotAlreadyInPublication(rels, sequences_schemaidlist,
|
|
||||||
PUBLICATIONOBJ_SEQUENCE);
|
|
||||||
PublicationAddRelations(puboid, rels, true, NULL);
|
|
||||||
CloseRelationList(rels);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tables added through a schema */
|
|
||||||
if (list_length(tables_schemaidlist) > 0)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Schema lock is held until the publication is created to prevent
|
* Schema lock is held until the publication is created to prevent
|
||||||
* concurrent schema deletion.
|
* concurrent schema deletion.
|
||||||
*/
|
*/
|
||||||
LockSchemaList(tables_schemaidlist);
|
LockSchemaList(schemaidlist);
|
||||||
PublicationAddSchemas(puboid,
|
PublicationAddSchemas(puboid, schemaidlist, true, NULL);
|
||||||
tables_schemaidlist, PUB_OBJTYPE_TABLE,
|
|
||||||
true, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sequences added through a schema */
|
|
||||||
if (list_length(sequences_schemaidlist) > 0)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Schema lock is held until the publication is created to prevent
|
|
||||||
* concurrent schema deletion.
|
|
||||||
*/
|
|
||||||
LockSchemaList(sequences_schemaidlist);
|
|
||||||
PublicationAddSchemas(puboid,
|
|
||||||
sequences_schemaidlist, PUB_OBJTYPE_SEQUENCE,
|
|
||||||
true, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1055,7 +941,6 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
|
||||||
AccessShareLock);
|
AccessShareLock);
|
||||||
|
|
||||||
root_relids = GetPublicationRelations(pubform->oid,
|
root_relids = GetPublicationRelations(pubform->oid,
|
||||||
PUB_OBJTYPE_TABLE,
|
|
||||||
PUBLICATION_PART_ROOT);
|
PUBLICATION_PART_ROOT);
|
||||||
|
|
||||||
foreach(lc, root_relids)
|
foreach(lc, root_relids)
|
||||||
|
@ -1135,9 +1020,6 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
|
||||||
|
|
||||||
values[Anum_pg_publication_pubtruncate - 1] = BoolGetDatum(pubactions.pubtruncate);
|
values[Anum_pg_publication_pubtruncate - 1] = BoolGetDatum(pubactions.pubtruncate);
|
||||||
replaces[Anum_pg_publication_pubtruncate - 1] = true;
|
replaces[Anum_pg_publication_pubtruncate - 1] = true;
|
||||||
|
|
||||||
values[Anum_pg_publication_pubsequence - 1] = BoolGetDatum(pubactions.pubsequence);
|
|
||||||
replaces[Anum_pg_publication_pubsequence - 1] = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publish_via_partition_root_given)
|
if (publish_via_partition_root_given)
|
||||||
|
@ -1157,7 +1039,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
|
||||||
pubform = (Form_pg_publication) GETSTRUCT(tup);
|
pubform = (Form_pg_publication) GETSTRUCT(tup);
|
||||||
|
|
||||||
/* Invalidate the relcache. */
|
/* Invalidate the relcache. */
|
||||||
if (pubform->puballtables || pubform->puballsequences)
|
if (pubform->puballtables)
|
||||||
{
|
{
|
||||||
CacheInvalidateRelcacheAll();
|
CacheInvalidateRelcacheAll();
|
||||||
}
|
}
|
||||||
|
@ -1173,7 +1055,6 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
|
||||||
*/
|
*/
|
||||||
if (root_relids == NIL)
|
if (root_relids == NIL)
|
||||||
relids = GetPublicationRelations(pubform->oid,
|
relids = GetPublicationRelations(pubform->oid,
|
||||||
PUB_OBJTYPE_TABLE,
|
|
||||||
PUBLICATION_PART_ALL);
|
PUBLICATION_PART_ALL);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1187,20 +1068,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
|
||||||
lfirst_oid(lc));
|
lfirst_oid(lc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tables */
|
|
||||||
schemarelids = GetAllSchemaPublicationRelations(pubform->oid,
|
schemarelids = GetAllSchemaPublicationRelations(pubform->oid,
|
||||||
PUB_OBJTYPE_TABLE,
|
|
||||||
PUBLICATION_PART_ALL);
|
|
||||||
relids = list_concat_unique_oid(relids, schemarelids);
|
|
||||||
|
|
||||||
/* sequences */
|
|
||||||
relids = list_concat_unique_oid(relids,
|
|
||||||
GetPublicationRelations(pubform->oid,
|
|
||||||
PUB_OBJTYPE_SEQUENCE,
|
|
||||||
PUBLICATION_PART_ALL));
|
|
||||||
|
|
||||||
schemarelids = GetAllSchemaPublicationRelations(pubform->oid,
|
|
||||||
PUB_OBJTYPE_SEQUENCE,
|
|
||||||
PUBLICATION_PART_ALL);
|
PUBLICATION_PART_ALL);
|
||||||
relids = list_concat_unique_oid(relids, schemarelids);
|
relids = list_concat_unique_oid(relids, schemarelids);
|
||||||
|
|
||||||
|
@ -1255,7 +1123,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
|
||||||
if (!tables && stmt->action != AP_SetObjects)
|
if (!tables && stmt->action != AP_SetObjects)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rels = OpenRelationList(tables, PUB_OBJTYPE_TABLE);
|
rels = OpenTableList(tables);
|
||||||
|
|
||||||
if (stmt->action == AP_AddObjects)
|
if (stmt->action == AP_AddObjects)
|
||||||
{
|
{
|
||||||
|
@ -1265,9 +1133,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
|
||||||
* Check if the relation is member of the existing schema in the
|
* Check if the relation is member of the existing schema in the
|
||||||
* publication or member of the schema list specified.
|
* publication or member of the schema list specified.
|
||||||
*/
|
*/
|
||||||
schemas = list_concat_copy(schemaidlist,
|
schemas = list_concat_copy(schemaidlist, GetPublicationSchemas(pubid));
|
||||||
GetPublicationSchemas(pubid,
|
|
||||||
PUB_OBJTYPE_TABLE));
|
|
||||||
CheckObjSchemaNotAlreadyInPublication(rels, schemas,
|
CheckObjSchemaNotAlreadyInPublication(rels, schemas,
|
||||||
PUBLICATIONOBJ_TABLE);
|
PUBLICATIONOBJ_TABLE);
|
||||||
|
|
||||||
|
@ -1275,14 +1141,13 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
|
||||||
|
|
||||||
CheckPubRelationColumnList(rels, queryString, pubform->pubviaroot);
|
CheckPubRelationColumnList(rels, queryString, pubform->pubviaroot);
|
||||||
|
|
||||||
PublicationAddRelations(pubid, rels, false, stmt);
|
PublicationAddTables(pubid, rels, false, stmt);
|
||||||
}
|
}
|
||||||
else if (stmt->action == AP_DropObjects)
|
else if (stmt->action == AP_DropObjects)
|
||||||
PublicationDropRelations(pubid, rels, false);
|
PublicationDropTables(pubid, rels, false);
|
||||||
else /* AP_SetObjects */
|
else /* AP_SetObjects */
|
||||||
{
|
{
|
||||||
List *oldrelids = GetPublicationRelations(pubid,
|
List *oldrelids = GetPublicationRelations(pubid,
|
||||||
PUB_OBJTYPE_TABLE,
|
|
||||||
PUBLICATION_PART_ROOT);
|
PUBLICATION_PART_ROOT);
|
||||||
List *delrels = NIL;
|
List *delrels = NIL;
|
||||||
ListCell *oldlc;
|
ListCell *oldlc;
|
||||||
|
@ -1401,18 +1266,18 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* And drop them. */
|
/* And drop them. */
|
||||||
PublicationDropRelations(pubid, delrels, true);
|
PublicationDropTables(pubid, delrels, true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't bother calculating the difference for adding, we'll catch and
|
* Don't bother calculating the difference for adding, we'll catch and
|
||||||
* skip existing ones when doing catalog update.
|
* skip existing ones when doing catalog update.
|
||||||
*/
|
*/
|
||||||
PublicationAddRelations(pubid, rels, true, stmt);
|
PublicationAddTables(pubid, rels, true, stmt);
|
||||||
|
|
||||||
CloseRelationList(delrels);
|
CloseTableList(delrels);
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseRelationList(rels);
|
CloseTableList(rels);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1422,8 +1287,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
AlterPublicationSchemas(AlterPublicationStmt *stmt,
|
AlterPublicationSchemas(AlterPublicationStmt *stmt,
|
||||||
HeapTuple tup, List *schemaidlist,
|
HeapTuple tup, List *schemaidlist)
|
||||||
char objectType)
|
|
||||||
{
|
{
|
||||||
Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
|
Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
|
||||||
|
|
||||||
|
@ -1445,20 +1309,20 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
|
||||||
List *rels;
|
List *rels;
|
||||||
List *reloids;
|
List *reloids;
|
||||||
|
|
||||||
reloids = GetPublicationRelations(pubform->oid, objectType, PUBLICATION_PART_ROOT);
|
reloids = GetPublicationRelations(pubform->oid, PUBLICATION_PART_ROOT);
|
||||||
rels = OpenRelIdList(reloids);
|
rels = OpenRelIdList(reloids);
|
||||||
|
|
||||||
CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
|
CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
|
||||||
PUBLICATIONOBJ_TABLES_IN_SCHEMA);
|
PUBLICATIONOBJ_TABLES_IN_SCHEMA);
|
||||||
|
|
||||||
CloseRelationList(rels);
|
CloseTableList(rels);
|
||||||
PublicationAddSchemas(pubform->oid, schemaidlist, objectType, false, stmt);
|
PublicationAddSchemas(pubform->oid, schemaidlist, false, stmt);
|
||||||
}
|
}
|
||||||
else if (stmt->action == AP_DropObjects)
|
else if (stmt->action == AP_DropObjects)
|
||||||
PublicationDropSchemas(pubform->oid, schemaidlist, objectType, false);
|
PublicationDropSchemas(pubform->oid, schemaidlist, false);
|
||||||
else /* AP_SetObjects */
|
else /* AP_SetObjects */
|
||||||
{
|
{
|
||||||
List *oldschemaids = GetPublicationSchemas(pubform->oid, objectType);
|
List *oldschemaids = GetPublicationSchemas(pubform->oid);
|
||||||
List *delschemas = NIL;
|
List *delschemas = NIL;
|
||||||
|
|
||||||
/* Identify which schemas should be dropped */
|
/* Identify which schemas should be dropped */
|
||||||
|
@ -1471,13 +1335,13 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
|
||||||
LockSchemaList(delschemas);
|
LockSchemaList(delschemas);
|
||||||
|
|
||||||
/* And drop them */
|
/* And drop them */
|
||||||
PublicationDropSchemas(pubform->oid, delschemas, objectType, true);
|
PublicationDropSchemas(pubform->oid, delschemas, true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't bother calculating the difference for adding, we'll catch and
|
* Don't bother calculating the difference for adding, we'll catch and
|
||||||
* skip existing ones when doing catalog update.
|
* skip existing ones when doing catalog update.
|
||||||
*/
|
*/
|
||||||
PublicationAddSchemas(pubform->oid, schemaidlist, objectType, true, stmt);
|
PublicationAddSchemas(pubform->oid, schemaidlist, true, stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1487,13 +1351,12 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
|
CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
|
||||||
List *tables, List *tables_schemaidlist,
|
List *tables, List *schemaidlist)
|
||||||
List *sequences, List *sequences_schemaidlist)
|
|
||||||
{
|
{
|
||||||
Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
|
Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
|
||||||
|
|
||||||
if ((stmt->action == AP_AddObjects || stmt->action == AP_SetObjects) &&
|
if ((stmt->action == AP_AddObjects || stmt->action == AP_SetObjects) &&
|
||||||
(tables_schemaidlist || sequences_schemaidlist) && !superuser())
|
schemaidlist && !superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
errmsg("must be superuser to add or set schemas")));
|
errmsg("must be superuser to add or set schemas")));
|
||||||
|
@ -1502,24 +1365,13 @@ CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
|
||||||
* Check that user is allowed to manipulate the publication tables in
|
* Check that user is allowed to manipulate the publication tables in
|
||||||
* schema
|
* schema
|
||||||
*/
|
*/
|
||||||
if (tables_schemaidlist && pubform->puballtables)
|
if (schemaidlist && pubform->puballtables)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||||
errmsg("publication \"%s\" is defined as FOR ALL TABLES",
|
errmsg("publication \"%s\" is defined as FOR ALL TABLES",
|
||||||
NameStr(pubform->pubname)),
|
NameStr(pubform->pubname)),
|
||||||
errdetail("Tables from schema cannot be added to, dropped from, or set on FOR ALL TABLES publications.")));
|
errdetail("Tables from schema cannot be added to, dropped from, or set on FOR ALL TABLES publications.")));
|
||||||
|
|
||||||
/*
|
|
||||||
* Check that user is allowed to manipulate the publication sequences in
|
|
||||||
* schema
|
|
||||||
*/
|
|
||||||
if (sequences_schemaidlist && pubform->puballsequences)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
||||||
errmsg("publication \"%s\" is defined as FOR ALL SEQUENCES",
|
|
||||||
NameStr(pubform->pubname)),
|
|
||||||
errdetail("Sequences from schema cannot be added to, dropped from, or set on FOR ALL SEQUENCES publications.")));
|
|
||||||
|
|
||||||
/* Check that user is allowed to manipulate the publication tables. */
|
/* Check that user is allowed to manipulate the publication tables. */
|
||||||
if (tables && pubform->puballtables)
|
if (tables && pubform->puballtables)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -1527,108 +1379,6 @@ CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
|
||||||
errmsg("publication \"%s\" is defined as FOR ALL TABLES",
|
errmsg("publication \"%s\" is defined as FOR ALL TABLES",
|
||||||
NameStr(pubform->pubname)),
|
NameStr(pubform->pubname)),
|
||||||
errdetail("Tables cannot be added to or dropped from FOR ALL TABLES publications.")));
|
errdetail("Tables cannot be added to or dropped from FOR ALL TABLES publications.")));
|
||||||
|
|
||||||
/* Check that user is allowed to manipulate the publication sequences. */
|
|
||||||
if (sequences && pubform->puballsequences)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
||||||
errmsg("publication \"%s\" is defined as FOR ALL SEQUENCES",
|
|
||||||
NameStr(pubform->pubname)),
|
|
||||||
errdetail("Sequences cannot be added to or dropped from FOR ALL SEQUENCES publications.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add or remove sequence to/from publication.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
AlterPublicationSequences(AlterPublicationStmt *stmt, HeapTuple tup,
|
|
||||||
List *sequences, List *schemaidlist)
|
|
||||||
{
|
|
||||||
List *rels = NIL;
|
|
||||||
Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
|
|
||||||
Oid pubid = pubform->oid;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* It is quite possible that for the SET case user has not specified any
|
|
||||||
* tables in which case we need to remove all the existing tables.
|
|
||||||
*/
|
|
||||||
if (!sequences && stmt->action != AP_SetObjects)
|
|
||||||
return;
|
|
||||||
|
|
||||||
rels = OpenRelationList(sequences, PUB_OBJTYPE_SEQUENCE);
|
|
||||||
|
|
||||||
if (stmt->action == AP_AddObjects)
|
|
||||||
{
|
|
||||||
List *schemas = NIL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if the relation is member of the existing schema in the
|
|
||||||
* publication or member of the schema list specified.
|
|
||||||
*/
|
|
||||||
schemas = list_concat_copy(schemaidlist,
|
|
||||||
GetPublicationSchemas(pubid,
|
|
||||||
PUB_OBJTYPE_SEQUENCE));
|
|
||||||
CheckObjSchemaNotAlreadyInPublication(rels, schemas,
|
|
||||||
PUBLICATIONOBJ_SEQUENCE);
|
|
||||||
PublicationAddRelations(pubid, rels, false, stmt);
|
|
||||||
}
|
|
||||||
else if (stmt->action == AP_DropObjects)
|
|
||||||
PublicationDropRelations(pubid, rels, false);
|
|
||||||
else /* DEFELEM_SET */
|
|
||||||
{
|
|
||||||
List *oldrelids = GetPublicationRelations(pubid,
|
|
||||||
PUB_OBJTYPE_SEQUENCE,
|
|
||||||
PUBLICATION_PART_ROOT);
|
|
||||||
List *delrels = NIL;
|
|
||||||
ListCell *oldlc;
|
|
||||||
|
|
||||||
CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
|
|
||||||
PUBLICATIONOBJ_SEQUENCE);
|
|
||||||
|
|
||||||
/* Calculate which relations to drop. */
|
|
||||||
foreach(oldlc, oldrelids)
|
|
||||||
{
|
|
||||||
Oid oldrelid = lfirst_oid(oldlc);
|
|
||||||
ListCell *newlc;
|
|
||||||
PublicationRelInfo *oldrel;
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
foreach(newlc, rels)
|
|
||||||
{
|
|
||||||
PublicationRelInfo *newpubrel;
|
|
||||||
|
|
||||||
newpubrel = (PublicationRelInfo *) lfirst(newlc);
|
|
||||||
if (RelationGetRelid(newpubrel->relation) == oldrelid)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Not yet in the list, open it and add to the list */
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
oldrel = palloc(sizeof(PublicationRelInfo));
|
|
||||||
oldrel->whereClause = NULL;
|
|
||||||
oldrel->columns = NULL;
|
|
||||||
oldrel->relation = table_open(oldrelid,
|
|
||||||
ShareUpdateExclusiveLock);
|
|
||||||
delrels = lappend(delrels, oldrel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* And drop them. */
|
|
||||||
PublicationDropRelations(pubid, delrels, true);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Don't bother calculating the difference for adding, we'll catch and
|
|
||||||
* skip existing ones when doing catalog update.
|
|
||||||
*/
|
|
||||||
PublicationAddRelations(pubid, rels, true, stmt);
|
|
||||||
|
|
||||||
CloseRelationList(delrels);
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseRelationList(rels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1666,20 +1416,14 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
|
||||||
AlterPublicationOptions(pstate, stmt, rel, tup);
|
AlterPublicationOptions(pstate, stmt, rel, tup);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
List *tables = NIL;
|
List *relations = NIL;
|
||||||
List *sequences = NIL;
|
List *schemaidlist = NIL;
|
||||||
List *tables_schemaidlist = NIL;
|
|
||||||
List *sequences_schemaidlist = NIL;
|
|
||||||
Oid pubid = pubform->oid;
|
Oid pubid = pubform->oid;
|
||||||
|
|
||||||
ObjectsInPublicationToOids(stmt->pubobjects, pstate,
|
ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
|
||||||
&tables, &sequences,
|
&schemaidlist);
|
||||||
&tables_schemaidlist,
|
|
||||||
&sequences_schemaidlist);
|
|
||||||
|
|
||||||
CheckAlterPublication(stmt, tup,
|
CheckAlterPublication(stmt, tup, relations, schemaidlist);
|
||||||
tables, tables_schemaidlist,
|
|
||||||
sequences, sequences_schemaidlist);
|
|
||||||
|
|
||||||
heap_freetuple(tup);
|
heap_freetuple(tup);
|
||||||
|
|
||||||
|
@ -1707,16 +1451,9 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
|
||||||
errmsg("publication \"%s\" does not exist",
|
errmsg("publication \"%s\" does not exist",
|
||||||
stmt->pubname));
|
stmt->pubname));
|
||||||
|
|
||||||
AlterPublicationTables(stmt, tup, tables, tables_schemaidlist,
|
AlterPublicationTables(stmt, tup, relations, schemaidlist,
|
||||||
pstate->p_sourcetext);
|
pstate->p_sourcetext);
|
||||||
|
AlterPublicationSchemas(stmt, tup, schemaidlist);
|
||||||
AlterPublicationSequences(stmt, tup, sequences, sequences_schemaidlist);
|
|
||||||
|
|
||||||
AlterPublicationSchemas(stmt, tup, tables_schemaidlist,
|
|
||||||
PUB_OBJTYPE_TABLE);
|
|
||||||
|
|
||||||
AlterPublicationSchemas(stmt, tup, sequences_schemaidlist,
|
|
||||||
PUB_OBJTYPE_SEQUENCE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cleanup. */
|
/* Cleanup. */
|
||||||
|
@ -1784,7 +1521,7 @@ RemovePublicationById(Oid pubid)
|
||||||
pubform = (Form_pg_publication) GETSTRUCT(tup);
|
pubform = (Form_pg_publication) GETSTRUCT(tup);
|
||||||
|
|
||||||
/* Invalidate relcache so that publication info is rebuilt. */
|
/* Invalidate relcache so that publication info is rebuilt. */
|
||||||
if (pubform->puballtables || pubform->puballsequences)
|
if (pubform->puballtables)
|
||||||
CacheInvalidateRelcacheAll();
|
CacheInvalidateRelcacheAll();
|
||||||
|
|
||||||
CatalogTupleDelete(rel, &tup->t_self);
|
CatalogTupleDelete(rel, &tup->t_self);
|
||||||
|
@ -1820,7 +1557,6 @@ RemovePublicationSchemaById(Oid psoid)
|
||||||
* partitions.
|
* partitions.
|
||||||
*/
|
*/
|
||||||
schemaRels = GetSchemaPublicationRelations(pubsch->pnnspid,
|
schemaRels = GetSchemaPublicationRelations(pubsch->pnnspid,
|
||||||
pubsch->pntype,
|
|
||||||
PUBLICATION_PART_ALL);
|
PUBLICATION_PART_ALL);
|
||||||
InvalidatePublicationRels(schemaRels);
|
InvalidatePublicationRels(schemaRels);
|
||||||
|
|
||||||
|
@ -1863,10 +1599,10 @@ OpenRelIdList(List *relids)
|
||||||
* add them to a publication.
|
* add them to a publication.
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
OpenRelationList(List *rels, char objectType)
|
OpenTableList(List *tables)
|
||||||
{
|
{
|
||||||
List *relids = NIL;
|
List *relids = NIL;
|
||||||
List *result = NIL;
|
List *rels = NIL;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
List *relids_with_rf = NIL;
|
List *relids_with_rf = NIL;
|
||||||
List *relids_with_collist = NIL;
|
List *relids_with_collist = NIL;
|
||||||
|
@ -1874,35 +1610,19 @@ OpenRelationList(List *rels, char objectType)
|
||||||
/*
|
/*
|
||||||
* Open, share-lock, and check all the explicitly-specified relations
|
* Open, share-lock, and check all the explicitly-specified relations
|
||||||
*/
|
*/
|
||||||
foreach(lc, rels)
|
foreach(lc, tables)
|
||||||
{
|
{
|
||||||
PublicationTable *t = lfirst_node(PublicationTable, lc);
|
PublicationTable *t = lfirst_node(PublicationTable, lc);
|
||||||
bool recurse = t->relation->inh;
|
bool recurse = t->relation->inh;
|
||||||
Relation rel;
|
Relation rel;
|
||||||
Oid myrelid;
|
Oid myrelid;
|
||||||
PublicationRelInfo *pub_rel;
|
PublicationRelInfo *pub_rel;
|
||||||
char myrelkind;
|
|
||||||
|
|
||||||
/* Allow query cancel in case this takes a long time */
|
/* Allow query cancel in case this takes a long time */
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
rel = table_openrv(t->relation, ShareUpdateExclusiveLock);
|
rel = table_openrv(t->relation, ShareUpdateExclusiveLock);
|
||||||
myrelid = RelationGetRelid(rel);
|
myrelid = RelationGetRelid(rel);
|
||||||
myrelkind = get_rel_relkind(myrelid);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure the relkind matches the expected object type. This may
|
|
||||||
* happen e.g. when adding a sequence using ADD TABLE or a table
|
|
||||||
* using ADD SEQUENCE).
|
|
||||||
*
|
|
||||||
* XXX We let through unsupported object types (views etc.). Those
|
|
||||||
* will be caught later in check_publication_add_relation.
|
|
||||||
*/
|
|
||||||
if (pub_get_object_type_for_relkind(myrelkind) != PUB_OBJTYPE_UNSUPPORTED &&
|
|
||||||
pub_get_object_type_for_relkind(myrelkind) != objectType)
|
|
||||||
ereport(ERROR,
|
|
||||||
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("object type does not match type expected by command"));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Filter out duplicates if user specifies "foo, foo".
|
* Filter out duplicates if user specifies "foo, foo".
|
||||||
|
@ -1935,7 +1655,7 @@ OpenRelationList(List *rels, char objectType)
|
||||||
pub_rel->relation = rel;
|
pub_rel->relation = rel;
|
||||||
pub_rel->whereClause = t->whereClause;
|
pub_rel->whereClause = t->whereClause;
|
||||||
pub_rel->columns = t->columns;
|
pub_rel->columns = t->columns;
|
||||||
result = lappend(result, pub_rel);
|
rels = lappend(rels, pub_rel);
|
||||||
relids = lappend_oid(relids, myrelid);
|
relids = lappend_oid(relids, myrelid);
|
||||||
|
|
||||||
if (t->whereClause)
|
if (t->whereClause)
|
||||||
|
@ -2004,9 +1724,10 @@ OpenRelationList(List *rels, char objectType)
|
||||||
pub_rel->relation = rel;
|
pub_rel->relation = rel;
|
||||||
/* child inherits WHERE clause from parent */
|
/* child inherits WHERE clause from parent */
|
||||||
pub_rel->whereClause = t->whereClause;
|
pub_rel->whereClause = t->whereClause;
|
||||||
|
|
||||||
/* child inherits column list from parent */
|
/* child inherits column list from parent */
|
||||||
pub_rel->columns = t->columns;
|
pub_rel->columns = t->columns;
|
||||||
result = lappend(result, pub_rel);
|
rels = lappend(rels, pub_rel);
|
||||||
relids = lappend_oid(relids, childrelid);
|
relids = lappend_oid(relids, childrelid);
|
||||||
|
|
||||||
if (t->whereClause)
|
if (t->whereClause)
|
||||||
|
@ -2021,14 +1742,14 @@ OpenRelationList(List *rels, char objectType)
|
||||||
list_free(relids);
|
list_free(relids);
|
||||||
list_free(relids_with_rf);
|
list_free(relids_with_rf);
|
||||||
|
|
||||||
return result;
|
return rels;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Close all relations in the list.
|
* Close all relations in the list.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CloseRelationList(List *rels)
|
CloseTableList(List *rels)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
|
@ -2076,12 +1797,12 @@ LockSchemaList(List *schemalist)
|
||||||
* Add listed tables to the publication.
|
* Add listed tables to the publication.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
PublicationAddRelations(Oid pubid, List *rels, bool if_not_exists,
|
PublicationAddTables(Oid pubid, List *rels, bool if_not_exists,
|
||||||
AlterPublicationStmt *stmt)
|
AlterPublicationStmt *stmt)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
Assert(!stmt || !stmt->for_all_objects);
|
Assert(!stmt || !stmt->for_all_tables);
|
||||||
|
|
||||||
foreach(lc, rels)
|
foreach(lc, rels)
|
||||||
{
|
{
|
||||||
|
@ -2110,7 +1831,7 @@ PublicationAddRelations(Oid pubid, List *rels, bool if_not_exists,
|
||||||
* Remove listed tables from the publication.
|
* Remove listed tables from the publication.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
PublicationDropRelations(Oid pubid, List *rels, bool missing_ok)
|
PublicationDropTables(Oid pubid, List *rels, bool missing_ok)
|
||||||
{
|
{
|
||||||
ObjectAddress obj;
|
ObjectAddress obj;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
@ -2155,19 +1876,19 @@ PublicationDropRelations(Oid pubid, List *rels, bool missing_ok)
|
||||||
* Add listed schemas to the publication.
|
* Add listed schemas to the publication.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
PublicationAddSchemas(Oid pubid, List *schemas, char objectType,
|
PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists,
|
||||||
bool if_not_exists, AlterPublicationStmt *stmt)
|
AlterPublicationStmt *stmt)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
Assert(!stmt || !stmt->for_all_objects);
|
Assert(!stmt || !stmt->for_all_tables);
|
||||||
|
|
||||||
foreach(lc, schemas)
|
foreach(lc, schemas)
|
||||||
{
|
{
|
||||||
Oid schemaid = lfirst_oid(lc);
|
Oid schemaid = lfirst_oid(lc);
|
||||||
ObjectAddress obj;
|
ObjectAddress obj;
|
||||||
|
|
||||||
obj = publication_add_schema(pubid, schemaid, objectType, if_not_exists);
|
obj = publication_add_schema(pubid, schemaid, if_not_exists);
|
||||||
if (stmt)
|
if (stmt)
|
||||||
{
|
{
|
||||||
EventTriggerCollectSimpleCommand(obj, InvalidObjectAddress,
|
EventTriggerCollectSimpleCommand(obj, InvalidObjectAddress,
|
||||||
|
@ -2183,7 +1904,7 @@ PublicationAddSchemas(Oid pubid, List *schemas, char objectType,
|
||||||
* Remove listed schemas from the publication.
|
* Remove listed schemas from the publication.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
PublicationDropSchemas(Oid pubid, List *schemas, char objectType, bool missing_ok)
|
PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok)
|
||||||
{
|
{
|
||||||
ObjectAddress obj;
|
ObjectAddress obj;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
@ -2193,11 +1914,10 @@ PublicationDropSchemas(Oid pubid, List *schemas, char objectType, bool missing_o
|
||||||
{
|
{
|
||||||
Oid schemaid = lfirst_oid(lc);
|
Oid schemaid = lfirst_oid(lc);
|
||||||
|
|
||||||
psid = GetSysCacheOid3(PUBLICATIONNAMESPACEMAP,
|
psid = GetSysCacheOid2(PUBLICATIONNAMESPACEMAP,
|
||||||
Anum_pg_publication_namespace_oid,
|
Anum_pg_publication_namespace_oid,
|
||||||
ObjectIdGetDatum(schemaid),
|
ObjectIdGetDatum(schemaid),
|
||||||
ObjectIdGetDatum(pubid),
|
ObjectIdGetDatum(pubid));
|
||||||
CharGetDatum(objectType));
|
|
||||||
if (!OidIsValid(psid))
|
if (!OidIsValid(psid))
|
||||||
{
|
{
|
||||||
if (missing_ok)
|
if (missing_ok)
|
||||||
|
@ -2252,13 +1972,6 @@ AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
|
||||||
NameStr(form->pubname)),
|
NameStr(form->pubname)),
|
||||||
errhint("The owner of a FOR ALL TABLES publication must be a superuser.")));
|
errhint("The owner of a FOR ALL TABLES publication must be a superuser.")));
|
||||||
|
|
||||||
if (form->puballsequences && !superuser_arg(newOwnerId))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
||||||
errmsg("permission denied to change owner of publication \"%s\"",
|
|
||||||
NameStr(form->pubname)),
|
|
||||||
errhint("The owner of a FOR ALL SEQUENCES publication must be a superuser.")));
|
|
||||||
|
|
||||||
if (!superuser_arg(newOwnerId) && is_schema_publication(form->oid))
|
if (!superuser_arg(newOwnerId) && is_schema_publication(form->oid))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
|
|
@ -332,160 +332,6 @@ ResetSequence(Oid seq_relid)
|
||||||
relation_close(seq_rel, NoLock);
|
relation_close(seq_rel, NoLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Update the sequence state by modifying the existing sequence data row.
|
|
||||||
*
|
|
||||||
* This keeps the same relfilenode, so the behavior is non-transactional.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
SetSequence_non_transactional(Oid seqrelid, int64 last_value, int64 log_cnt, bool is_called)
|
|
||||||
{
|
|
||||||
SeqTable elm;
|
|
||||||
Relation seqrel;
|
|
||||||
Buffer buf;
|
|
||||||
HeapTupleData seqdatatuple;
|
|
||||||
Form_pg_sequence_data seq;
|
|
||||||
|
|
||||||
/* open and lock sequence */
|
|
||||||
init_sequence(seqrelid, &elm, &seqrel);
|
|
||||||
|
|
||||||
/* lock page' buffer and read tuple */
|
|
||||||
seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
|
|
||||||
|
|
||||||
/* check the comment above nextval_internal()'s equivalent call. */
|
|
||||||
if (RelationNeedsWAL(seqrel))
|
|
||||||
{
|
|
||||||
GetTopTransactionId();
|
|
||||||
|
|
||||||
if (XLogLogicalInfoActive())
|
|
||||||
GetCurrentTransactionId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ready to change the on-disk (or really, in-buffer) tuple */
|
|
||||||
START_CRIT_SECTION();
|
|
||||||
|
|
||||||
seq->last_value = last_value;
|
|
||||||
seq->is_called = is_called;
|
|
||||||
seq->log_cnt = log_cnt;
|
|
||||||
|
|
||||||
MarkBufferDirty(buf);
|
|
||||||
|
|
||||||
/* XLOG stuff */
|
|
||||||
if (RelationNeedsWAL(seqrel))
|
|
||||||
{
|
|
||||||
xl_seq_rec xlrec;
|
|
||||||
XLogRecPtr recptr;
|
|
||||||
Page page = BufferGetPage(buf);
|
|
||||||
|
|
||||||
XLogBeginInsert();
|
|
||||||
XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
|
|
||||||
|
|
||||||
xlrec.node = seqrel->rd_node;
|
|
||||||
xlrec.created = false;
|
|
||||||
|
|
||||||
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
|
|
||||||
XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
|
|
||||||
|
|
||||||
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
|
|
||||||
|
|
||||||
PageSetLSN(page, recptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
END_CRIT_SECTION();
|
|
||||||
|
|
||||||
UnlockReleaseBuffer(buf);
|
|
||||||
|
|
||||||
/* Clear local cache so that we don't think we have cached numbers */
|
|
||||||
/* Note that we do not change the currval() state */
|
|
||||||
elm->cached = elm->last;
|
|
||||||
|
|
||||||
relation_close(seqrel, NoLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Update the sequence state by creating a new relfilenode.
|
|
||||||
*
|
|
||||||
* This creates a new relfilenode, to allow transactional behavior.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
SetSequence_transactional(Oid seq_relid, int64 last_value, int64 log_cnt, bool is_called)
|
|
||||||
{
|
|
||||||
SeqTable elm;
|
|
||||||
Relation seqrel;
|
|
||||||
Buffer buf;
|
|
||||||
HeapTupleData seqdatatuple;
|
|
||||||
Form_pg_sequence_data seq;
|
|
||||||
HeapTuple tuple;
|
|
||||||
|
|
||||||
/* open and lock sequence */
|
|
||||||
init_sequence(seq_relid, &elm, &seqrel);
|
|
||||||
|
|
||||||
/* lock page' buffer and read tuple */
|
|
||||||
seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
|
|
||||||
|
|
||||||
/* Copy the existing sequence tuple. */
|
|
||||||
tuple = heap_copytuple(&seqdatatuple);
|
|
||||||
|
|
||||||
/* Now we're done with the old page */
|
|
||||||
UnlockReleaseBuffer(buf);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Modify the copied tuple to update the sequence state (similar to what
|
|
||||||
* ResetSequence does).
|
|
||||||
*/
|
|
||||||
seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
|
|
||||||
seq->last_value = last_value;
|
|
||||||
seq->is_called = is_called;
|
|
||||||
seq->log_cnt = log_cnt;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a new storage file for the sequence - this is needed for the
|
|
||||||
* transactional behavior.
|
|
||||||
*/
|
|
||||||
RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ensure sequence's relfrozenxid is at 0, since it won't contain any
|
|
||||||
* unfrozen XIDs. Same with relminmxid, since a sequence will never
|
|
||||||
* contain multixacts.
|
|
||||||
*/
|
|
||||||
Assert(seqrel->rd_rel->relfrozenxid == InvalidTransactionId);
|
|
||||||
Assert(seqrel->rd_rel->relminmxid == InvalidMultiXactId);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Insert the modified tuple into the new storage file. This does all the
|
|
||||||
* necessary WAL-logging etc.
|
|
||||||
*/
|
|
||||||
fill_seq_with_data(seqrel, tuple);
|
|
||||||
|
|
||||||
/* Clear local cache so that we don't think we have cached numbers */
|
|
||||||
/* Note that we do not change the currval() state */
|
|
||||||
elm->cached = elm->last;
|
|
||||||
|
|
||||||
relation_close(seqrel, NoLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set a sequence to a specified internal state.
|
|
||||||
*
|
|
||||||
* The change is made transactionally, so that on failure of the current
|
|
||||||
* transaction, the sequence will be restored to its previous state.
|
|
||||||
* We do that by creating a whole new relfilenode for the sequence; so this
|
|
||||||
* works much like the rewriting forms of ALTER TABLE.
|
|
||||||
*
|
|
||||||
* Caller is assumed to have acquired AccessExclusiveLock on the sequence,
|
|
||||||
* which must not be released until end of transaction. Caller is also
|
|
||||||
* responsible for permissions checking.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
SetSequence(Oid seq_relid, bool transactional, int64 last_value, int64 log_cnt, bool is_called)
|
|
||||||
{
|
|
||||||
if (transactional)
|
|
||||||
SetSequence_transactional(seq_relid, last_value, log_cnt, is_called);
|
|
||||||
else
|
|
||||||
SetSequence_non_transactional(seq_relid, last_value, log_cnt, is_called);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize a sequence's relation with the specified tuple as content
|
* Initialize a sequence's relation with the specified tuple as content
|
||||||
*
|
*
|
||||||
|
@ -552,13 +398,8 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
|
||||||
|
|
||||||
/* check the comment above nextval_internal()'s equivalent call. */
|
/* check the comment above nextval_internal()'s equivalent call. */
|
||||||
if (RelationNeedsWAL(rel))
|
if (RelationNeedsWAL(rel))
|
||||||
{
|
|
||||||
GetTopTransactionId();
|
GetTopTransactionId();
|
||||||
|
|
||||||
if (XLogLogicalInfoActive())
|
|
||||||
GetCurrentTransactionId();
|
|
||||||
}
|
|
||||||
|
|
||||||
START_CRIT_SECTION();
|
START_CRIT_SECTION();
|
||||||
|
|
||||||
MarkBufferDirty(buf);
|
MarkBufferDirty(buf);
|
||||||
|
@ -578,7 +419,6 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
|
||||||
XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
|
XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
|
||||||
|
|
||||||
xlrec.node = rel->rd_node;
|
xlrec.node = rel->rd_node;
|
||||||
xlrec.created = true;
|
|
||||||
|
|
||||||
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
|
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
|
||||||
XLogRegisterData((char *) tuple->t_data, tuple->t_len);
|
XLogRegisterData((char *) tuple->t_data, tuple->t_len);
|
||||||
|
@ -958,28 +798,10 @@ nextval_internal(Oid relid, bool check_permissions)
|
||||||
* It's sufficient to ensure the toplevel transaction has an xid, no need
|
* It's sufficient to ensure the toplevel transaction has an xid, no need
|
||||||
* to assign xids subxacts, that'll already trigger an appropriate wait.
|
* to assign xids subxacts, that'll already trigger an appropriate wait.
|
||||||
* (Have to do that here, so we're outside the critical section)
|
* (Have to do that here, so we're outside the critical section)
|
||||||
*
|
|
||||||
* We have to ensure we have a proper XID, which will be included in
|
|
||||||
* the XLOG record by XLogRecordAssemble. Otherwise the first nextval()
|
|
||||||
* in a subxact (without any preceding changes) would get XID 0, and it
|
|
||||||
* would then be impossible to decide which top xact it belongs to.
|
|
||||||
* It'd also trigger assert in DecodeSequence. We only do that with
|
|
||||||
* wal_level=logical, though.
|
|
||||||
*
|
|
||||||
* XXX This might seem unnecessary, because if there's no XID the xact
|
|
||||||
* couldn't have done anything important yet, e.g. it could not have
|
|
||||||
* created a sequence. But that's incorrect, because of subxacts. The
|
|
||||||
* current subtransaction might not have done anything yet (thus no XID),
|
|
||||||
* but an earlier one might have created the sequence.
|
|
||||||
*/
|
*/
|
||||||
if (logit && RelationNeedsWAL(seqrel))
|
if (logit && RelationNeedsWAL(seqrel))
|
||||||
{
|
|
||||||
GetTopTransactionId();
|
GetTopTransactionId();
|
||||||
|
|
||||||
if (XLogLogicalInfoActive())
|
|
||||||
GetCurrentTransactionId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ready to change the on-disk (or really, in-buffer) tuple */
|
/* ready to change the on-disk (or really, in-buffer) tuple */
|
||||||
START_CRIT_SECTION();
|
START_CRIT_SECTION();
|
||||||
|
|
||||||
|
@ -1015,7 +837,6 @@ nextval_internal(Oid relid, bool check_permissions)
|
||||||
seq->log_cnt = 0;
|
seq->log_cnt = 0;
|
||||||
|
|
||||||
xlrec.node = seqrel->rd_node;
|
xlrec.node = seqrel->rd_node;
|
||||||
xlrec.created = false;
|
|
||||||
|
|
||||||
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
|
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
|
||||||
XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
|
XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
|
||||||
|
@ -1181,13 +1002,8 @@ do_setval(Oid relid, int64 next, bool iscalled)
|
||||||
|
|
||||||
/* check the comment above nextval_internal()'s equivalent call. */
|
/* check the comment above nextval_internal()'s equivalent call. */
|
||||||
if (RelationNeedsWAL(seqrel))
|
if (RelationNeedsWAL(seqrel))
|
||||||
{
|
|
||||||
GetTopTransactionId();
|
GetTopTransactionId();
|
||||||
|
|
||||||
if (XLogLogicalInfoActive())
|
|
||||||
GetCurrentTransactionId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ready to change the on-disk (or really, in-buffer) tuple */
|
/* ready to change the on-disk (or really, in-buffer) tuple */
|
||||||
START_CRIT_SECTION();
|
START_CRIT_SECTION();
|
||||||
|
|
||||||
|
@ -1208,8 +1024,6 @@ do_setval(Oid relid, int64 next, bool iscalled)
|
||||||
XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
|
XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
|
||||||
|
|
||||||
xlrec.node = seqrel->rd_node;
|
xlrec.node = seqrel->rd_node;
|
||||||
xlrec.created = false;
|
|
||||||
|
|
||||||
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
|
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
|
||||||
XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
|
XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,6 @@ typedef struct SubOpts
|
||||||
} SubOpts;
|
} SubOpts;
|
||||||
|
|
||||||
static List *fetch_table_list(WalReceiverConn *wrconn, List *publications);
|
static List *fetch_table_list(WalReceiverConn *wrconn, List *publications);
|
||||||
static List *fetch_sequence_list(WalReceiverConn *wrconn, List *publications);
|
|
||||||
static void check_duplicates_in_publist(List *publist, Datum *datums);
|
static void check_duplicates_in_publist(List *publist, Datum *datums);
|
||||||
static List *merge_publications(List *oldpublist, List *newpublist, bool addpub, const char *subname);
|
static List *merge_publications(List *oldpublist, List *newpublist, bool addpub, const char *subname);
|
||||||
static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err);
|
static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err);
|
||||||
|
@ -639,9 +638,9 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
|
||||||
{
|
{
|
||||||
char *err;
|
char *err;
|
||||||
WalReceiverConn *wrconn;
|
WalReceiverConn *wrconn;
|
||||||
List *relations;
|
List *tables;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
char sync_state;
|
char table_state;
|
||||||
|
|
||||||
/* Try to connect to the publisher. */
|
/* Try to connect to the publisher. */
|
||||||
wrconn = walrcv_connect(conninfo, true, stmt->subname, &err);
|
wrconn = walrcv_connect(conninfo, true, stmt->subname, &err);
|
||||||
|
@ -658,17 +657,14 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
|
||||||
* Set sync state based on if we were asked to do data copy or
|
* Set sync state based on if we were asked to do data copy or
|
||||||
* not.
|
* not.
|
||||||
*/
|
*/
|
||||||
sync_state = opts.copy_data ? SUBREL_STATE_INIT : SUBREL_STATE_READY;
|
table_state = opts.copy_data ? SUBREL_STATE_INIT : SUBREL_STATE_READY;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the table and sequence list from publisher and build
|
* Get the table list from publisher and build local table status
|
||||||
* local relation sync status info.
|
* info.
|
||||||
*/
|
*/
|
||||||
relations = fetch_table_list(wrconn, publications);
|
tables = fetch_table_list(wrconn, publications);
|
||||||
relations = list_concat(relations,
|
foreach(lc, tables)
|
||||||
fetch_sequence_list(wrconn, publications));
|
|
||||||
|
|
||||||
foreach(lc, relations)
|
|
||||||
{
|
{
|
||||||
RangeVar *rv = (RangeVar *) lfirst(lc);
|
RangeVar *rv = (RangeVar *) lfirst(lc);
|
||||||
Oid relid;
|
Oid relid;
|
||||||
|
@ -679,7 +675,7 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
|
||||||
CheckSubscriptionRelkind(get_rel_relkind(relid),
|
CheckSubscriptionRelkind(get_rel_relkind(relid),
|
||||||
rv->schemaname, rv->relname);
|
rv->schemaname, rv->relname);
|
||||||
|
|
||||||
AddSubscriptionRelState(subid, relid, sync_state,
|
AddSubscriptionRelState(subid, relid, table_state,
|
||||||
InvalidXLogRecPtr);
|
InvalidXLogRecPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,12 +701,12 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
|
||||||
*
|
*
|
||||||
* Note that if tables were specified but copy_data is false
|
* Note that if tables were specified but copy_data is false
|
||||||
* then it is safe to enable two_phase up-front because those
|
* then it is safe to enable two_phase up-front because those
|
||||||
* relations are already initially in READY state. When the
|
* tables are already initially in READY state. When the
|
||||||
* subscription has no relations, we leave the twophase state
|
* subscription has no tables, we leave the twophase state as
|
||||||
* as PENDING, to allow ALTER SUBSCRIPTION ... REFRESH
|
* PENDING, to allow ALTER SUBSCRIPTION ... REFRESH
|
||||||
* PUBLICATION to work.
|
* PUBLICATION to work.
|
||||||
*/
|
*/
|
||||||
if (opts.twophase && !opts.copy_data && relations != NIL)
|
if (opts.twophase && !opts.copy_data && tables != NIL)
|
||||||
twophase_enabled = true;
|
twophase_enabled = true;
|
||||||
|
|
||||||
walrcv_create_slot(wrconn, opts.slot_name, false, twophase_enabled,
|
walrcv_create_slot(wrconn, opts.slot_name, false, twophase_enabled,
|
||||||
|
@ -786,10 +782,8 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data,
|
||||||
if (validate_publications)
|
if (validate_publications)
|
||||||
check_publications(wrconn, validate_publications);
|
check_publications(wrconn, validate_publications);
|
||||||
|
|
||||||
/* Get the list of relations from publisher. */
|
/* Get the table list from publisher. */
|
||||||
pubrel_names = fetch_table_list(wrconn, sub->publications);
|
pubrel_names = fetch_table_list(wrconn, sub->publications);
|
||||||
pubrel_names = list_concat(pubrel_names,
|
|
||||||
fetch_sequence_list(wrconn, sub->publications));
|
|
||||||
|
|
||||||
/* Get local table list. */
|
/* Get local table list. */
|
||||||
subrel_states = GetSubscriptionRelations(sub->oid);
|
subrel_states = GetSubscriptionRelations(sub->oid);
|
||||||
|
@ -1813,75 +1807,6 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications)
|
||||||
return tablelist;
|
return tablelist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the list of sequences which belong to specified publications on the
|
|
||||||
* publisher connection.
|
|
||||||
*/
|
|
||||||
static List *
|
|
||||||
fetch_sequence_list(WalReceiverConn *wrconn, List *publications)
|
|
||||||
{
|
|
||||||
WalRcvExecResult *res;
|
|
||||||
StringInfoData cmd;
|
|
||||||
TupleTableSlot *slot;
|
|
||||||
Oid tableRow[2] = {TEXTOID, TEXTOID};
|
|
||||||
ListCell *lc;
|
|
||||||
bool first;
|
|
||||||
List *tablelist = NIL;
|
|
||||||
|
|
||||||
Assert(list_length(publications) > 0);
|
|
||||||
|
|
||||||
initStringInfo(&cmd);
|
|
||||||
appendStringInfoString(&cmd, "SELECT DISTINCT s.schemaname, s.sequencename\n"
|
|
||||||
" FROM pg_catalog.pg_publication_sequences s\n"
|
|
||||||
" WHERE s.pubname IN (");
|
|
||||||
first = true;
|
|
||||||
foreach(lc, publications)
|
|
||||||
{
|
|
||||||
char *pubname = strVal(lfirst(lc));
|
|
||||||
|
|
||||||
if (first)
|
|
||||||
first = false;
|
|
||||||
else
|
|
||||||
appendStringInfoString(&cmd, ", ");
|
|
||||||
|
|
||||||
appendStringInfoString(&cmd, quote_literal_cstr(pubname));
|
|
||||||
}
|
|
||||||
appendStringInfoChar(&cmd, ')');
|
|
||||||
|
|
||||||
res = walrcv_exec(wrconn, cmd.data, 2, tableRow);
|
|
||||||
pfree(cmd.data);
|
|
||||||
|
|
||||||
if (res->status != WALRCV_OK_TUPLES)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errmsg("could not receive list of replicated sequences from the publisher: %s",
|
|
||||||
res->err)));
|
|
||||||
|
|
||||||
/* Process sequences. */
|
|
||||||
slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple);
|
|
||||||
while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
|
|
||||||
{
|
|
||||||
char *nspname;
|
|
||||||
char *relname;
|
|
||||||
bool isnull;
|
|
||||||
RangeVar *rv;
|
|
||||||
|
|
||||||
nspname = TextDatumGetCString(slot_getattr(slot, 1, &isnull));
|
|
||||||
Assert(!isnull);
|
|
||||||
relname = TextDatumGetCString(slot_getattr(slot, 2, &isnull));
|
|
||||||
Assert(!isnull);
|
|
||||||
|
|
||||||
rv = makeRangeVar(nspname, relname, -1);
|
|
||||||
tablelist = lappend(tablelist, rv);
|
|
||||||
|
|
||||||
ExecClearTuple(slot);
|
|
||||||
}
|
|
||||||
ExecDropSingleTupleTableSlot(slot);
|
|
||||||
|
|
||||||
walrcv_clear_result(res);
|
|
||||||
|
|
||||||
return tablelist;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is to report the connection failure while dropping replication slots.
|
* This is to report the connection failure while dropping replication slots.
|
||||||
* Here, we report the WARNING for all tablesync slots so that user can drop
|
* Here, we report the WARNING for all tablesync slots so that user can drop
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
#include "catalog/pg_inherits.h"
|
#include "catalog/pg_inherits.h"
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_publication_namespace.h"
|
|
||||||
#include "catalog/pg_statistic_ext.h"
|
#include "catalog/pg_statistic_ext.h"
|
||||||
#include "catalog/pg_tablespace.h"
|
#include "catalog/pg_tablespace.h"
|
||||||
#include "catalog/pg_trigger.h"
|
#include "catalog/pg_trigger.h"
|
||||||
|
@ -16409,14 +16408,11 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
|
||||||
* Check that setting the relation to a different schema won't result in a
|
* Check that setting the relation to a different schema won't result in a
|
||||||
* publication having both a schema and the same schema's table, as this
|
* publication having both a schema and the same schema's table, as this
|
||||||
* is not supported.
|
* is not supported.
|
||||||
*
|
|
||||||
* XXX We do this for tables and sequences, but it's better to keep the two
|
|
||||||
* blocks separate, to make the strings easier to translate.
|
|
||||||
*/
|
*/
|
||||||
if (stmt->objectType == OBJECT_TABLE)
|
if (stmt->objectType == OBJECT_TABLE)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
List *schemaPubids = GetSchemaPublications(nspOid, PUB_OBJTYPE_TABLE);
|
List *schemaPubids = GetSchemaPublications(nspOid);
|
||||||
List *relPubids = GetRelationPublications(RelationGetRelid(rel));
|
List *relPubids = GetRelationPublications(RelationGetRelid(rel));
|
||||||
|
|
||||||
foreach(lc, relPubids)
|
foreach(lc, relPubids)
|
||||||
|
@ -16434,27 +16430,6 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
|
||||||
get_publication_name(pubid, false)));
|
get_publication_name(pubid, false)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (stmt->objectType == OBJECT_SEQUENCE)
|
|
||||||
{
|
|
||||||
ListCell *lc;
|
|
||||||
List *schemaPubids = GetSchemaPublications(nspOid, PUB_OBJTYPE_SEQUENCE);
|
|
||||||
List *relPubids = GetRelationPublications(RelationGetRelid(rel));
|
|
||||||
|
|
||||||
foreach(lc, relPubids)
|
|
||||||
{
|
|
||||||
Oid pubid = lfirst_oid(lc);
|
|
||||||
|
|
||||||
if (list_member_oid(schemaPubids, pubid))
|
|
||||||
ereport(ERROR,
|
|
||||||
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("cannot move sequence \"%s\" to schema \"%s\"",
|
|
||||||
RelationGetRelationName(rel), stmt->newschema),
|
|
||||||
errdetail("The schema \"%s\" and same schema's sequence \"%s\" cannot be part of the same publication \"%s\".",
|
|
||||||
stmt->newschema,
|
|
||||||
RelationGetRelationName(rel),
|
|
||||||
get_publication_name(pubid, false)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* common checks on switching namespaces */
|
/* common checks on switching namespaces */
|
||||||
CheckSetNamespace(oldNspOid, nspOid);
|
CheckSetNamespace(oldNspOid, nspOid);
|
||||||
|
|
|
@ -649,9 +649,7 @@ void
|
||||||
CheckSubscriptionRelkind(char relkind, const char *nspname,
|
CheckSubscriptionRelkind(char relkind, const char *nspname,
|
||||||
const char *relname)
|
const char *relname)
|
||||||
{
|
{
|
||||||
if (relkind != RELKIND_RELATION &&
|
if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
|
||||||
relkind != RELKIND_PARTITIONED_TABLE &&
|
|
||||||
relkind != RELKIND_SEQUENCE)
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
errmsg("cannot use relation \"%s.%s\" as logical replication target",
|
errmsg("cannot use relation \"%s.%s\" as logical replication target",
|
||||||
|
|
|
@ -5390,7 +5390,7 @@ _copyCreatePublicationStmt(const CreatePublicationStmt *from)
|
||||||
COPY_STRING_FIELD(pubname);
|
COPY_STRING_FIELD(pubname);
|
||||||
COPY_NODE_FIELD(options);
|
COPY_NODE_FIELD(options);
|
||||||
COPY_NODE_FIELD(pubobjects);
|
COPY_NODE_FIELD(pubobjects);
|
||||||
COPY_NODE_FIELD(for_all_objects);
|
COPY_SCALAR_FIELD(for_all_tables);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
@ -5403,7 +5403,7 @@ _copyAlterPublicationStmt(const AlterPublicationStmt *from)
|
||||||
COPY_STRING_FIELD(pubname);
|
COPY_STRING_FIELD(pubname);
|
||||||
COPY_NODE_FIELD(options);
|
COPY_NODE_FIELD(options);
|
||||||
COPY_NODE_FIELD(pubobjects);
|
COPY_NODE_FIELD(pubobjects);
|
||||||
COPY_NODE_FIELD(for_all_objects);
|
COPY_SCALAR_FIELD(for_all_tables);
|
||||||
COPY_SCALAR_FIELD(action);
|
COPY_SCALAR_FIELD(action);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
|
|
|
@ -2688,7 +2688,7 @@ _equalCreatePublicationStmt(const CreatePublicationStmt *a,
|
||||||
COMPARE_STRING_FIELD(pubname);
|
COMPARE_STRING_FIELD(pubname);
|
||||||
COMPARE_NODE_FIELD(options);
|
COMPARE_NODE_FIELD(options);
|
||||||
COMPARE_NODE_FIELD(pubobjects);
|
COMPARE_NODE_FIELD(pubobjects);
|
||||||
COMPARE_NODE_FIELD(for_all_objects);
|
COMPARE_SCALAR_FIELD(for_all_tables);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2700,7 +2700,7 @@ _equalAlterPublicationStmt(const AlterPublicationStmt *a,
|
||||||
COMPARE_STRING_FIELD(pubname);
|
COMPARE_STRING_FIELD(pubname);
|
||||||
COMPARE_NODE_FIELD(options);
|
COMPARE_NODE_FIELD(options);
|
||||||
COMPARE_NODE_FIELD(pubobjects);
|
COMPARE_NODE_FIELD(pubobjects);
|
||||||
COMPARE_NODE_FIELD(for_all_objects);
|
COMPARE_SCALAR_FIELD(for_all_tables);
|
||||||
COMPARE_SCALAR_FIELD(action);
|
COMPARE_SCALAR_FIELD(action);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -455,7 +455,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||||
transform_element_list transform_type_list
|
transform_element_list transform_type_list
|
||||||
TriggerTransitions TriggerReferencing
|
TriggerTransitions TriggerReferencing
|
||||||
vacuum_relation_list opt_vacuum_relation_list
|
vacuum_relation_list opt_vacuum_relation_list
|
||||||
drop_option_list pub_obj_list pub_obj_type_list
|
drop_option_list pub_obj_list
|
||||||
|
|
||||||
%type <node> opt_routine_body
|
%type <node> opt_routine_body
|
||||||
%type <groupclause> group_clause
|
%type <groupclause> group_clause
|
||||||
|
@ -588,7 +588,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||||
%type <node> var_value zone_value
|
%type <node> var_value zone_value
|
||||||
%type <rolespec> auth_ident RoleSpec opt_granted_by
|
%type <rolespec> auth_ident RoleSpec opt_granted_by
|
||||||
%type <publicationobjectspec> PublicationObjSpec
|
%type <publicationobjectspec> PublicationObjSpec
|
||||||
%type <node> pub_obj_type
|
|
||||||
|
|
||||||
%type <keyword> unreserved_keyword type_func_name_keyword
|
%type <keyword> unreserved_keyword type_func_name_keyword
|
||||||
%type <keyword> col_name_keyword reserved_keyword
|
%type <keyword> col_name_keyword reserved_keyword
|
||||||
|
@ -9863,10 +9862,13 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
|
||||||
*
|
*
|
||||||
* CREATE PUBLICATION FOR ALL TABLES [WITH options]
|
* CREATE PUBLICATION FOR ALL TABLES [WITH options]
|
||||||
*
|
*
|
||||||
* CREATE PUBLICATION FOR ALL SEQUENCES [WITH options]
|
|
||||||
*
|
|
||||||
* CREATE PUBLICATION FOR pub_obj [, ...] [WITH options]
|
* CREATE PUBLICATION FOR pub_obj [, ...] [WITH options]
|
||||||
*
|
*
|
||||||
|
* pub_obj is one of:
|
||||||
|
*
|
||||||
|
* TABLE table [, ...]
|
||||||
|
* ALL TABLES IN SCHEMA schema [, ...]
|
||||||
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
CreatePublicationStmt:
|
CreatePublicationStmt:
|
||||||
|
@ -9877,12 +9879,12 @@ CreatePublicationStmt:
|
||||||
n->options = $4;
|
n->options = $4;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| CREATE PUBLICATION name FOR ALL pub_obj_type_list opt_definition
|
| CREATE PUBLICATION name FOR ALL TABLES opt_definition
|
||||||
{
|
{
|
||||||
CreatePublicationStmt *n = makeNode(CreatePublicationStmt);
|
CreatePublicationStmt *n = makeNode(CreatePublicationStmt);
|
||||||
n->pubname = $3;
|
n->pubname = $3;
|
||||||
n->options = $7;
|
n->options = $7;
|
||||||
n->for_all_objects = $6;
|
n->for_all_tables = true;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| CREATE PUBLICATION name FOR pub_obj_list opt_definition
|
| CREATE PUBLICATION name FOR pub_obj_list opt_definition
|
||||||
|
@ -9932,26 +9934,6 @@ PublicationObjSpec:
|
||||||
$$->pubobjtype = PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA;
|
$$->pubobjtype = PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA;
|
||||||
$$->location = @5;
|
$$->location = @5;
|
||||||
}
|
}
|
||||||
| SEQUENCE relation_expr
|
|
||||||
{
|
|
||||||
$$ = makeNode(PublicationObjSpec);
|
|
||||||
$$->pubobjtype = PUBLICATIONOBJ_SEQUENCE;
|
|
||||||
$$->pubtable = makeNode(PublicationTable);
|
|
||||||
$$->pubtable->relation = $2;
|
|
||||||
}
|
|
||||||
| ALL SEQUENCES IN_P SCHEMA ColId
|
|
||||||
{
|
|
||||||
$$ = makeNode(PublicationObjSpec);
|
|
||||||
$$->pubobjtype = PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA;
|
|
||||||
$$->name = $5;
|
|
||||||
$$->location = @5;
|
|
||||||
}
|
|
||||||
| ALL SEQUENCES IN_P SCHEMA CURRENT_SCHEMA
|
|
||||||
{
|
|
||||||
$$ = makeNode(PublicationObjSpec);
|
|
||||||
$$->pubobjtype = PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA;
|
|
||||||
$$->location = @5;
|
|
||||||
}
|
|
||||||
| ColId opt_column_list OptWhereClause
|
| ColId opt_column_list OptWhereClause
|
||||||
{
|
{
|
||||||
$$ = makeNode(PublicationObjSpec);
|
$$ = makeNode(PublicationObjSpec);
|
||||||
|
@ -10013,19 +9995,6 @@ pub_obj_list: PublicationObjSpec
|
||||||
{ $$ = lappend($1, $3); }
|
{ $$ = lappend($1, $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
pub_obj_type: TABLES
|
|
||||||
{ $$ = (Node *) makeString("tables"); }
|
|
||||||
| SEQUENCES
|
|
||||||
{ $$ = (Node *) makeString("sequences"); }
|
|
||||||
;
|
|
||||||
|
|
||||||
pub_obj_type_list: pub_obj_type
|
|
||||||
{ $$ = list_make1($1); }
|
|
||||||
| pub_obj_type_list ',' pub_obj_type
|
|
||||||
{ $$ = lappend($1, $3); }
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* ALTER PUBLICATION name SET ( options )
|
* ALTER PUBLICATION name SET ( options )
|
||||||
|
@ -10036,6 +10005,11 @@ pub_obj_type_list: pub_obj_type
|
||||||
*
|
*
|
||||||
* ALTER PUBLICATION name SET pub_obj [, ...]
|
* ALTER PUBLICATION name SET pub_obj [, ...]
|
||||||
*
|
*
|
||||||
|
* pub_obj is one of:
|
||||||
|
*
|
||||||
|
* TABLE table_name [, ...]
|
||||||
|
* ALL TABLES IN SCHEMA schema_name [, ...]
|
||||||
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
AlterPublicationStmt:
|
AlterPublicationStmt:
|
||||||
|
@ -18757,8 +18731,7 @@ preprocess_pubobj_list(List *pubobjspec_list, core_yyscan_t yyscanner)
|
||||||
if (pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION)
|
if (pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION)
|
||||||
pubobj->pubobjtype = prevobjtype;
|
pubobj->pubobjtype = prevobjtype;
|
||||||
|
|
||||||
if (pubobj->pubobjtype == PUBLICATIONOBJ_TABLE ||
|
if (pubobj->pubobjtype == PUBLICATIONOBJ_TABLE)
|
||||||
pubobj->pubobjtype == PUBLICATIONOBJ_SEQUENCE)
|
|
||||||
{
|
{
|
||||||
/* relation name or pubtable must be set for this type of object */
|
/* relation name or pubtable must be set for this type of object */
|
||||||
if (!pubobj->name && !pubobj->pubtable)
|
if (!pubobj->name && !pubobj->pubtable)
|
||||||
|
@ -18809,30 +18782,6 @@ preprocess_pubobj_list(List *pubobjspec_list, core_yyscan_t yyscanner)
|
||||||
errmsg("invalid schema name at or near"),
|
errmsg("invalid schema name at or near"),
|
||||||
parser_errposition(pubobj->location));
|
parser_errposition(pubobj->location));
|
||||||
}
|
}
|
||||||
else if (pubobj->pubobjtype == PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA ||
|
|
||||||
pubobj->pubobjtype == PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA)
|
|
||||||
{
|
|
||||||
/* WHERE clause is not allowed on a schema object */
|
|
||||||
if (pubobj->pubtable && pubobj->pubtable->whereClause)
|
|
||||||
ereport(ERROR,
|
|
||||||
errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("WHERE clause not allowed for schema"),
|
|
||||||
parser_errposition(pubobj->location));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We can distinguish between the different type of schema
|
|
||||||
* objects based on whether name and pubtable is set.
|
|
||||||
*/
|
|
||||||
if (pubobj->name)
|
|
||||||
pubobj->pubobjtype = PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA;
|
|
||||||
else if (!pubobj->name && !pubobj->pubtable)
|
|
||||||
pubobj->pubobjtype = PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA;
|
|
||||||
else
|
|
||||||
ereport(ERROR,
|
|
||||||
errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("invalid schema name at or near"),
|
|
||||||
parser_errposition(pubobj->location));
|
|
||||||
}
|
|
||||||
|
|
||||||
prevobjtype = pubobj->pubobjtype;
|
prevobjtype = pubobj->pubobjtype;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
#include "replication/reorderbuffer.h"
|
#include "replication/reorderbuffer.h"
|
||||||
#include "replication/snapbuild.h"
|
#include "replication/snapbuild.h"
|
||||||
#include "storage/standby.h"
|
#include "storage/standby.h"
|
||||||
#include "commands/sequence.h"
|
|
||||||
|
|
||||||
/* individual record(group)'s handlers */
|
/* individual record(group)'s handlers */
|
||||||
static void DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
|
static void DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
|
||||||
|
@ -64,7 +63,6 @@ static void DecodePrepare(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
|
||||||
|
|
||||||
/* common function to decode tuples */
|
/* common function to decode tuples */
|
||||||
static void DecodeXLogTuple(char *data, Size len, ReorderBufferTupleBuf *tup);
|
static void DecodeXLogTuple(char *data, Size len, ReorderBufferTupleBuf *tup);
|
||||||
static void DecodeSeqTuple(char *data, Size len, ReorderBufferTupleBuf *tuple);
|
|
||||||
|
|
||||||
/* helper functions for decoding transactions */
|
/* helper functions for decoding transactions */
|
||||||
static inline bool FilterPrepare(LogicalDecodingContext *ctx,
|
static inline bool FilterPrepare(LogicalDecodingContext *ctx,
|
||||||
|
@ -1252,132 +1250,3 @@ DecodeTXNNeedSkip(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
|
||||||
(txn_dbid != InvalidOid && txn_dbid != ctx->slot->data.database) ||
|
(txn_dbid != InvalidOid && txn_dbid != ctx->slot->data.database) ||
|
||||||
ctx->fast_forward || FilterByOrigin(ctx, origin_id));
|
ctx->fast_forward || FilterByOrigin(ctx, origin_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* DecodeSeqTuple
|
|
||||||
* decode tuple describing the sequence increment
|
|
||||||
*
|
|
||||||
* Sequences are represented as a table with a single row, which gets updated
|
|
||||||
* by nextval(). The tuple is stored in WAL right after the xl_seq_rec, so we
|
|
||||||
* simply copy it into the tuplebuf (similar to seq_redo).
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
DecodeSeqTuple(char *data, Size len, ReorderBufferTupleBuf *tuple)
|
|
||||||
{
|
|
||||||
int datalen = len - sizeof(xl_seq_rec) - SizeofHeapTupleHeader;
|
|
||||||
|
|
||||||
Assert(datalen >= 0);
|
|
||||||
|
|
||||||
tuple->tuple.t_len = datalen + SizeofHeapTupleHeader;
|
|
||||||
|
|
||||||
ItemPointerSetInvalid(&tuple->tuple.t_self);
|
|
||||||
|
|
||||||
tuple->tuple.t_tableOid = InvalidOid;
|
|
||||||
|
|
||||||
memcpy(((char *) tuple->tuple.t_data),
|
|
||||||
data + sizeof(xl_seq_rec),
|
|
||||||
SizeofHeapTupleHeader);
|
|
||||||
|
|
||||||
memcpy(((char *) tuple->tuple.t_data) + SizeofHeapTupleHeader,
|
|
||||||
data + sizeof(xl_seq_rec) + SizeofHeapTupleHeader,
|
|
||||||
datalen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle sequence decode
|
|
||||||
*
|
|
||||||
* Decoding sequences is a bit tricky, because while most sequence actions
|
|
||||||
* are non-transactional (not subject to rollback), some need to be handled
|
|
||||||
* as transactional.
|
|
||||||
*
|
|
||||||
* By default, a sequence increment is non-transactional - we must not queue
|
|
||||||
* it in a transaction as other changes, because the transaction might get
|
|
||||||
* rolled back and we'd discard the increment. The downstream would not be
|
|
||||||
* notified about the increment, which is wrong.
|
|
||||||
*
|
|
||||||
* On the other hand, the sequence may be created in a transaction. In this
|
|
||||||
* case we *should* queue the change as other changes in the transaction,
|
|
||||||
* because we don't want to send the increments for unknown sequence to the
|
|
||||||
* plugin - it might get confused about which sequence it's related to etc.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
sequence_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
|
|
||||||
{
|
|
||||||
SnapBuild *builder = ctx->snapshot_builder;
|
|
||||||
ReorderBufferTupleBuf *tuplebuf;
|
|
||||||
RelFileNode target_node;
|
|
||||||
XLogReaderState *r = buf->record;
|
|
||||||
char *tupledata = NULL;
|
|
||||||
Size tuplelen;
|
|
||||||
Size datalen = 0;
|
|
||||||
TransactionId xid = XLogRecGetXid(r);
|
|
||||||
uint8 info = XLogRecGetInfo(buf->record) & ~XLR_INFO_MASK;
|
|
||||||
xl_seq_rec *xlrec;
|
|
||||||
Snapshot snapshot;
|
|
||||||
RepOriginId origin_id = XLogRecGetOrigin(r);
|
|
||||||
bool transactional;
|
|
||||||
|
|
||||||
/* only decode changes flagged with XLOG_SEQ_LOG */
|
|
||||||
if (info != XLOG_SEQ_LOG)
|
|
||||||
elog(ERROR, "unexpected RM_SEQ_ID record type: %u", info);
|
|
||||||
|
|
||||||
ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(r), buf->origptr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we don't have snapshot or we are just fast-forwarding, there is no
|
|
||||||
* point in decoding messages.
|
|
||||||
*/
|
|
||||||
if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT ||
|
|
||||||
ctx->fast_forward)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* only interested in our database */
|
|
||||||
XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
|
|
||||||
if (target_node.dbNode != ctx->slot->data.database)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* output plugin doesn't look for this origin, no need to queue */
|
|
||||||
if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
tupledata = XLogRecGetData(r);
|
|
||||||
datalen = XLogRecGetDataLen(r);
|
|
||||||
tuplelen = datalen - SizeOfHeapHeader - sizeof(xl_seq_rec);
|
|
||||||
|
|
||||||
/* extract the WAL record, with "created" flag */
|
|
||||||
xlrec = (xl_seq_rec *) XLogRecGetData(r);
|
|
||||||
|
|
||||||
/* XXX how could we have sequence change without data? */
|
|
||||||
if(!datalen || !tupledata)
|
|
||||||
return;
|
|
||||||
|
|
||||||
tuplebuf = ReorderBufferGetTupleBuf(ctx->reorder, tuplelen);
|
|
||||||
DecodeSeqTuple(tupledata, datalen, tuplebuf);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Should we handle the sequence increment as transactional or not?
|
|
||||||
*
|
|
||||||
* If the sequence was created in a still-running transaction, treat
|
|
||||||
* it as transactional and queue the increments. Otherwise it needs
|
|
||||||
* to be treated as non-transactional, in which case we send it to
|
|
||||||
* the plugin right away.
|
|
||||||
*/
|
|
||||||
transactional = ReorderBufferSequenceIsTransactional(ctx->reorder,
|
|
||||||
target_node,
|
|
||||||
xlrec->created);
|
|
||||||
|
|
||||||
/* Skip the change if already processed (per the snapshot). */
|
|
||||||
if (transactional &&
|
|
||||||
!SnapBuildProcessChange(builder, xid, buf->origptr))
|
|
||||||
return;
|
|
||||||
else if (!transactional &&
|
|
||||||
(SnapBuildCurrentState(builder) != SNAPBUILD_CONSISTENT ||
|
|
||||||
SnapBuildXactNeedsSkip(builder, buf->origptr)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Queue the increment (or send immediately if not transactional). */
|
|
||||||
snapshot = SnapBuildGetOrBuildSnapshot(builder, xid);
|
|
||||||
ReorderBufferQueueSequence(ctx->reorder, xid, snapshot, buf->endptr,
|
|
||||||
origin_id, target_node, transactional,
|
|
||||||
xlrec->created, tuplebuf);
|
|
||||||
}
|
|
||||||
|
|
|
@ -73,10 +73,6 @@ static void truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
||||||
static void message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
static void message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
||||||
XLogRecPtr message_lsn, bool transactional,
|
XLogRecPtr message_lsn, bool transactional,
|
||||||
const char *prefix, Size message_size, const char *message);
|
const char *prefix, Size message_size, const char *message);
|
||||||
static void sequence_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn, Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called);
|
|
||||||
|
|
||||||
/* streaming callbacks */
|
/* streaming callbacks */
|
||||||
static void stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
static void stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
||||||
|
@ -94,10 +90,6 @@ static void stream_change_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn
|
||||||
static void stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
static void stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
||||||
XLogRecPtr message_lsn, bool transactional,
|
XLogRecPtr message_lsn, bool transactional,
|
||||||
const char *prefix, Size message_size, const char *message);
|
const char *prefix, Size message_size, const char *message);
|
||||||
static void stream_sequence_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn, Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called);
|
|
||||||
static void stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
static void stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
||||||
int nrelations, Relation relations[], ReorderBufferChange *change);
|
int nrelations, Relation relations[], ReorderBufferChange *change);
|
||||||
|
|
||||||
|
@ -226,7 +218,6 @@ StartupDecodingContext(List *output_plugin_options,
|
||||||
ctx->reorder->apply_truncate = truncate_cb_wrapper;
|
ctx->reorder->apply_truncate = truncate_cb_wrapper;
|
||||||
ctx->reorder->commit = commit_cb_wrapper;
|
ctx->reorder->commit = commit_cb_wrapper;
|
||||||
ctx->reorder->message = message_cb_wrapper;
|
ctx->reorder->message = message_cb_wrapper;
|
||||||
ctx->reorder->sequence = sequence_cb_wrapper;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To support streaming, we require start/stop/abort/commit/change
|
* To support streaming, we require start/stop/abort/commit/change
|
||||||
|
@ -243,7 +234,6 @@ StartupDecodingContext(List *output_plugin_options,
|
||||||
(ctx->callbacks.stream_commit_cb != NULL) ||
|
(ctx->callbacks.stream_commit_cb != NULL) ||
|
||||||
(ctx->callbacks.stream_change_cb != NULL) ||
|
(ctx->callbacks.stream_change_cb != NULL) ||
|
||||||
(ctx->callbacks.stream_message_cb != NULL) ||
|
(ctx->callbacks.stream_message_cb != NULL) ||
|
||||||
(ctx->callbacks.stream_sequence_cb != NULL) ||
|
|
||||||
(ctx->callbacks.stream_truncate_cb != NULL);
|
(ctx->callbacks.stream_truncate_cb != NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -261,7 +251,6 @@ StartupDecodingContext(List *output_plugin_options,
|
||||||
ctx->reorder->stream_commit = stream_commit_cb_wrapper;
|
ctx->reorder->stream_commit = stream_commit_cb_wrapper;
|
||||||
ctx->reorder->stream_change = stream_change_cb_wrapper;
|
ctx->reorder->stream_change = stream_change_cb_wrapper;
|
||||||
ctx->reorder->stream_message = stream_message_cb_wrapper;
|
ctx->reorder->stream_message = stream_message_cb_wrapper;
|
||||||
ctx->reorder->stream_sequence = stream_sequence_cb_wrapper;
|
|
||||||
ctx->reorder->stream_truncate = stream_truncate_cb_wrapper;
|
ctx->reorder->stream_truncate = stream_truncate_cb_wrapper;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1216,42 +1205,6 @@ message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
||||||
error_context_stack = errcallback.previous;
|
error_context_stack = errcallback.previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
sequence_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn, Relation rel, bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called)
|
|
||||||
{
|
|
||||||
LogicalDecodingContext *ctx = cache->private_data;
|
|
||||||
LogicalErrorCallbackState state;
|
|
||||||
ErrorContextCallback errcallback;
|
|
||||||
|
|
||||||
Assert(!ctx->fast_forward);
|
|
||||||
|
|
||||||
if (ctx->callbacks.sequence_cb == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Push callback + info on the error context stack */
|
|
||||||
state.ctx = ctx;
|
|
||||||
state.callback_name = "sequence";
|
|
||||||
state.report_location = sequence_lsn;
|
|
||||||
errcallback.callback = output_plugin_error_callback;
|
|
||||||
errcallback.arg = (void *) &state;
|
|
||||||
errcallback.previous = error_context_stack;
|
|
||||||
error_context_stack = &errcallback;
|
|
||||||
|
|
||||||
/* set output state */
|
|
||||||
ctx->accept_writes = true;
|
|
||||||
ctx->write_xid = txn != NULL ? txn->xid : InvalidTransactionId;
|
|
||||||
ctx->write_location = sequence_lsn;
|
|
||||||
|
|
||||||
/* do the actual work: call callback */
|
|
||||||
ctx->callbacks.sequence_cb(ctx, txn, sequence_lsn, rel, transactional,
|
|
||||||
last_value, log_cnt, is_called);
|
|
||||||
|
|
||||||
/* Pop the error context stack */
|
|
||||||
error_context_stack = errcallback.previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
||||||
XLogRecPtr first_lsn)
|
XLogRecPtr first_lsn)
|
||||||
|
@ -1557,47 +1510,6 @@ stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
||||||
error_context_stack = errcallback.previous;
|
error_context_stack = errcallback.previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
stream_sequence_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn, Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called)
|
|
||||||
{
|
|
||||||
LogicalDecodingContext *ctx = cache->private_data;
|
|
||||||
LogicalErrorCallbackState state;
|
|
||||||
ErrorContextCallback errcallback;
|
|
||||||
|
|
||||||
Assert(!ctx->fast_forward);
|
|
||||||
|
|
||||||
/* We're only supposed to call this when streaming is supported. */
|
|
||||||
Assert(ctx->streaming);
|
|
||||||
|
|
||||||
/* this callback is optional */
|
|
||||||
if (ctx->callbacks.stream_sequence_cb == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Push callback + info on the error context stack */
|
|
||||||
state.ctx = ctx;
|
|
||||||
state.callback_name = "stream_sequence";
|
|
||||||
state.report_location = sequence_lsn;
|
|
||||||
errcallback.callback = output_plugin_error_callback;
|
|
||||||
errcallback.arg = (void *) &state;
|
|
||||||
errcallback.previous = error_context_stack;
|
|
||||||
error_context_stack = &errcallback;
|
|
||||||
|
|
||||||
/* set output state */
|
|
||||||
ctx->accept_writes = true;
|
|
||||||
ctx->write_xid = txn != NULL ? txn->xid : InvalidTransactionId;
|
|
||||||
ctx->write_location = sequence_lsn;
|
|
||||||
|
|
||||||
/* do the actual work: call callback */
|
|
||||||
ctx->callbacks.sequence_cb(ctx, txn, sequence_lsn, rel, transactional,
|
|
||||||
last_value, log_cnt, is_called);
|
|
||||||
|
|
||||||
/* Pop the error context stack */
|
|
||||||
error_context_stack = errcallback.previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
|
||||||
int nrelations, Relation relations[],
|
int nrelations, Relation relations[],
|
||||||
|
|
|
@ -662,56 +662,6 @@ logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
|
||||||
pq_sendbytes(out, message, sz);
|
pq_sendbytes(out, message, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Write SEQUENCE to stream
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
logicalrep_write_sequence(StringInfo out, Relation rel, TransactionId xid,
|
|
||||||
XLogRecPtr lsn, bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called)
|
|
||||||
{
|
|
||||||
uint8 flags = 0;
|
|
||||||
char *relname;
|
|
||||||
|
|
||||||
pq_sendbyte(out, LOGICAL_REP_MSG_SEQUENCE);
|
|
||||||
|
|
||||||
/* transaction ID (if not valid, we're not streaming) */
|
|
||||||
if (TransactionIdIsValid(xid))
|
|
||||||
pq_sendint32(out, xid);
|
|
||||||
|
|
||||||
pq_sendint8(out, flags);
|
|
||||||
pq_sendint64(out, lsn);
|
|
||||||
|
|
||||||
logicalrep_write_namespace(out, RelationGetNamespace(rel));
|
|
||||||
relname = RelationGetRelationName(rel);
|
|
||||||
pq_sendstring(out, relname);
|
|
||||||
|
|
||||||
pq_sendint8(out, transactional);
|
|
||||||
pq_sendint64(out, last_value);
|
|
||||||
pq_sendint64(out, log_cnt);
|
|
||||||
pq_sendint8(out, is_called);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read SEQUENCE from the stream.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
logicalrep_read_sequence(StringInfo in, LogicalRepSequence *seqdata)
|
|
||||||
{
|
|
||||||
/* XXX skipping flags and lsn */
|
|
||||||
pq_getmsgint(in, 1);
|
|
||||||
pq_getmsgint64(in);
|
|
||||||
|
|
||||||
/* Read relation name from stream */
|
|
||||||
seqdata->nspname = pstrdup(logicalrep_read_namespace(in));
|
|
||||||
seqdata->seqname = pstrdup(pq_getmsgstring(in));
|
|
||||||
|
|
||||||
seqdata->transactional = pq_getmsgint(in, 1);
|
|
||||||
seqdata->last_value = pq_getmsgint64(in);
|
|
||||||
seqdata->log_cnt = pq_getmsgint64(in);
|
|
||||||
seqdata->is_called = pq_getmsgint(in, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write relation description to the output stream.
|
* Write relation description to the output stream.
|
||||||
*/
|
*/
|
||||||
|
@ -1286,8 +1236,6 @@ logicalrep_message_type(LogicalRepMsgType action)
|
||||||
return "STREAM ABORT";
|
return "STREAM ABORT";
|
||||||
case LOGICAL_REP_MSG_STREAM_PREPARE:
|
case LOGICAL_REP_MSG_STREAM_PREPARE:
|
||||||
return "STREAM PREPARE";
|
return "STREAM PREPARE";
|
||||||
case LOGICAL_REP_MSG_SEQUENCE:
|
|
||||||
return "SEQUENCE";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elog(ERROR, "invalid logical replication message type \"%c\"", action);
|
elog(ERROR, "invalid logical replication message type \"%c\"", action);
|
||||||
|
|
|
@ -77,40 +77,6 @@
|
||||||
* a bit more memory to the oldest subtransactions, because it's likely
|
* a bit more memory to the oldest subtransactions, because it's likely
|
||||||
* they are the source for the next sequence of changes.
|
* they are the source for the next sequence of changes.
|
||||||
*
|
*
|
||||||
* When decoding sequences, we differentiate between a sequences created
|
|
||||||
* in a (running) transaction, and sequences created in other (already
|
|
||||||
* committed) transactions. Changes for sequences created in the same
|
|
||||||
* top-level transaction are treated as "transactional" i.e. just like
|
|
||||||
* any other change from that transaction (and discarded in case of a
|
|
||||||
* rollback). Changes for sequences created earlier are treated as not
|
|
||||||
* transactional - are processed immediately, as if performed outside
|
|
||||||
* any transaction (and thus not rolled back).
|
|
||||||
*
|
|
||||||
* This mixed behavior is necessary - sequences are non-transactional
|
|
||||||
* (e.g. ROLLBACK does not undo the sequence increments). But for new
|
|
||||||
* sequences, we need to handle them in a transactional way, because if
|
|
||||||
* we ever get some DDL support, the sequence won't exist until the
|
|
||||||
* transaction gets applied. So we need to ensure the increments don't
|
|
||||||
* happen until the sequence gets created.
|
|
||||||
*
|
|
||||||
* To differentiate which sequences are "old" and which were created
|
|
||||||
* in a still-running transaction, we track sequences created in running
|
|
||||||
* transactions in a hash table. Sequences are identified by relfilenode,
|
|
||||||
* and we track XID of the (sub)transaction that created it. This means
|
|
||||||
* that if a transaction does something that changes the relfilenode
|
|
||||||
* (like an alter / reset of a sequence), the new relfilenode will be
|
|
||||||
* treated as if created in the transaction. The list of sequences gets
|
|
||||||
* discarded when the transaction completes (commit/rollback).
|
|
||||||
*
|
|
||||||
* We don't use the XID to check if it's the same top-level transaction.
|
|
||||||
* It's enough to know it was created in an in-progress transaction,
|
|
||||||
* and we know it must be the current one because otherwise it wouldn't
|
|
||||||
* see the sequence object.
|
|
||||||
*
|
|
||||||
* The XID may be valid even for non-transactional sequences - we simply
|
|
||||||
* keep the XID logged to WAL, it's up to the reorderbuffer to decide if
|
|
||||||
* the increment is transactional.
|
|
||||||
*
|
|
||||||
* -------------------------------------------------------------------------
|
* -------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
@ -125,7 +91,6 @@
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "access/xlog_internal.h"
|
#include "access/xlog_internal.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "commands/sequence.h"
|
|
||||||
#include "lib/binaryheap.h"
|
#include "lib/binaryheap.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
|
@ -151,13 +116,6 @@ typedef struct ReorderBufferTXNByIdEnt
|
||||||
ReorderBufferTXN *txn;
|
ReorderBufferTXN *txn;
|
||||||
} ReorderBufferTXNByIdEnt;
|
} ReorderBufferTXNByIdEnt;
|
||||||
|
|
||||||
/* entry for hash table we use to track sequences created in running xacts */
|
|
||||||
typedef struct ReorderBufferSequenceEnt
|
|
||||||
{
|
|
||||||
RelFileNode rnode;
|
|
||||||
TransactionId xid;
|
|
||||||
} ReorderBufferSequenceEnt;
|
|
||||||
|
|
||||||
/* data structures for (relfilenode, ctid) => (cmin, cmax) mapping */
|
/* data structures for (relfilenode, ctid) => (cmin, cmax) mapping */
|
||||||
typedef struct ReorderBufferTupleCidKey
|
typedef struct ReorderBufferTupleCidKey
|
||||||
{
|
{
|
||||||
|
@ -388,14 +346,6 @@ ReorderBufferAllocate(void)
|
||||||
buffer->by_txn = hash_create("ReorderBufferByXid", 1000, &hash_ctl,
|
buffer->by_txn = hash_create("ReorderBufferByXid", 1000, &hash_ctl,
|
||||||
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
|
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
|
||||||
|
|
||||||
/* hash table of sequences, mapping relfilenode to XID of transaction */
|
|
||||||
hash_ctl.keysize = sizeof(RelFileNode);
|
|
||||||
hash_ctl.entrysize = sizeof(ReorderBufferSequenceEnt);
|
|
||||||
hash_ctl.hcxt = buffer->context;
|
|
||||||
|
|
||||||
buffer->sequences = hash_create("ReorderBufferSequenceHash", 1000, &hash_ctl,
|
|
||||||
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
|
|
||||||
|
|
||||||
buffer->by_txn_last_xid = InvalidTransactionId;
|
buffer->by_txn_last_xid = InvalidTransactionId;
|
||||||
buffer->by_txn_last_txn = NULL;
|
buffer->by_txn_last_txn = NULL;
|
||||||
|
|
||||||
|
@ -582,13 +532,6 @@ ReorderBufferReturnChange(ReorderBuffer *rb, ReorderBufferChange *change,
|
||||||
change->data.truncate.relids = NULL;
|
change->data.truncate.relids = NULL;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REORDER_BUFFER_CHANGE_SEQUENCE:
|
|
||||||
if (change->data.sequence.tuple)
|
|
||||||
{
|
|
||||||
ReorderBufferReturnTupleBuf(rb, change->data.sequence.tuple);
|
|
||||||
change->data.sequence.tuple = NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
||||||
|
@ -923,230 +866,6 @@ ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Treat the sequence increment as transactional?
|
|
||||||
*
|
|
||||||
* The hash table tracks all sequences created in in-progress transactions,
|
|
||||||
* so we simply do a lookup (the sequence is identified by relfilende). If
|
|
||||||
* we find a match, the increment should be handled as transactional.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
ReorderBufferSequenceIsTransactional(ReorderBuffer *rb,
|
|
||||||
RelFileNode rnode, bool created)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
if (created)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
hash_search(rb->sequences,
|
|
||||||
(void *) &rnode,
|
|
||||||
HASH_FIND,
|
|
||||||
&found);
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Cleanup sequences created in in-progress transactions.
|
|
||||||
*
|
|
||||||
* There's no way to search by XID, so we simply do a seqscan of all
|
|
||||||
* the entries in the hash table. Hopefully there are only a couple
|
|
||||||
* entries in most cases - people generally don't create many new
|
|
||||||
* sequences over and over.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
ReorderBufferSequenceCleanup(ReorderBuffer *rb, TransactionId xid)
|
|
||||||
{
|
|
||||||
HASH_SEQ_STATUS scan_status;
|
|
||||||
ReorderBufferSequenceEnt *ent;
|
|
||||||
|
|
||||||
hash_seq_init(&scan_status, rb->sequences);
|
|
||||||
while ((ent = (ReorderBufferSequenceEnt *) hash_seq_search(&scan_status)) != NULL)
|
|
||||||
{
|
|
||||||
/* skip sequences not from this transaction */
|
|
||||||
if (ent->xid != xid)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
(void) hash_search(rb->sequences,
|
|
||||||
(void *) &(ent->rnode),
|
|
||||||
HASH_REMOVE, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A transactional sequence increment is queued to be processed upon commit
|
|
||||||
* and a non-transactional increment gets processed immediately.
|
|
||||||
*
|
|
||||||
* A sequence update may be both transactional and non-transactional. When
|
|
||||||
* created in a running transaction, treat it as transactional and queue
|
|
||||||
* the change in it. Otherwise treat it as non-transactional, so that we
|
|
||||||
* don't forget the increment in case of a rollback.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ReorderBufferQueueSequence(ReorderBuffer *rb, TransactionId xid,
|
|
||||||
Snapshot snapshot, XLogRecPtr lsn, RepOriginId origin_id,
|
|
||||||
RelFileNode rnode, bool transactional, bool created,
|
|
||||||
ReorderBufferTupleBuf *tuplebuf)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Change needs to be handled as transactional, because the sequence was
|
|
||||||
* created in a transaction that is still running. In that case all the
|
|
||||||
* changes need to be queued in that transaction, we must not send them
|
|
||||||
* to the downstream until the transaction commits.
|
|
||||||
*
|
|
||||||
* There's a bit of a trouble with subtransactions - we can't queue it
|
|
||||||
* into the subxact, because it might be rolled back and we'd lose the
|
|
||||||
* increment. We need to queue it into the same (sub)xact that created
|
|
||||||
* the sequence, which is why we track the XID in the hash table.
|
|
||||||
*/
|
|
||||||
if (transactional)
|
|
||||||
{
|
|
||||||
MemoryContext oldcontext;
|
|
||||||
ReorderBufferChange *change;
|
|
||||||
|
|
||||||
/* lookup sequence by relfilenode */
|
|
||||||
ReorderBufferSequenceEnt *ent;
|
|
||||||
bool found;
|
|
||||||
|
|
||||||
/* transactional changes require a transaction */
|
|
||||||
Assert(xid != InvalidTransactionId);
|
|
||||||
|
|
||||||
/* search the lookup table (we ignore the return value, found is enough) */
|
|
||||||
ent = hash_search(rb->sequences,
|
|
||||||
(void *) &rnode,
|
|
||||||
created ? HASH_ENTER : HASH_FIND,
|
|
||||||
&found);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If this is the "create" increment, we must not have found any
|
|
||||||
* pre-existing entry in the hash table (i.e. there must not be
|
|
||||||
* any conflicting sequence).
|
|
||||||
*/
|
|
||||||
Assert(!(created && found));
|
|
||||||
|
|
||||||
/* But we must have either created or found an existing entry. */
|
|
||||||
Assert(created || found);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When creating the sequence, remember the XID of the transaction
|
|
||||||
* that created id.
|
|
||||||
*/
|
|
||||||
if (created)
|
|
||||||
ent->xid = xid;
|
|
||||||
|
|
||||||
/* XXX Maybe check that we're still in the same top-level xact? */
|
|
||||||
|
|
||||||
/* OK, allocate and queue the change */
|
|
||||||
oldcontext = MemoryContextSwitchTo(rb->context);
|
|
||||||
|
|
||||||
change = ReorderBufferGetChange(rb);
|
|
||||||
|
|
||||||
change->action = REORDER_BUFFER_CHANGE_SEQUENCE;
|
|
||||||
change->origin_id = origin_id;
|
|
||||||
|
|
||||||
memcpy(&change->data.sequence.relnode, &rnode, sizeof(RelFileNode));
|
|
||||||
|
|
||||||
change->data.sequence.tuple = tuplebuf;
|
|
||||||
|
|
||||||
/* add it to the same subxact that created the sequence */
|
|
||||||
ReorderBufferQueueChange(rb, ent->xid, lsn, change, false);
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This increment is for a sequence that was not created in any
|
|
||||||
* running transaction, so we treat it as non-transactional and
|
|
||||||
* just send it to the output plugin directly.
|
|
||||||
*/
|
|
||||||
ReorderBufferTXN *txn = NULL;
|
|
||||||
volatile Snapshot snapshot_now = snapshot;
|
|
||||||
bool using_subtxn;
|
|
||||||
|
|
||||||
#ifdef USE_ASSERT_CHECKING
|
|
||||||
/* All "creates" have to be handled as transactional. */
|
|
||||||
Assert(!created);
|
|
||||||
|
|
||||||
/* Make sure the sequence is not in the hash table. */
|
|
||||||
{
|
|
||||||
bool found;
|
|
||||||
hash_search(rb->sequences,
|
|
||||||
(void *) &rnode,
|
|
||||||
HASH_FIND, &found);
|
|
||||||
Assert(!found);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (xid != InvalidTransactionId)
|
|
||||||
txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
|
|
||||||
|
|
||||||
/* setup snapshot to allow catalog access */
|
|
||||||
SetupHistoricSnapshot(snapshot_now, NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Decoding needs access to syscaches et al., which in turn use
|
|
||||||
* heavyweight locks and such. Thus we need to have enough state around to
|
|
||||||
* keep track of those. The easiest way is to simply use a transaction
|
|
||||||
* internally. That also allows us to easily enforce that nothing writes
|
|
||||||
* to the database by checking for xid assignments.
|
|
||||||
*
|
|
||||||
* When we're called via the SQL SRF there's already a transaction
|
|
||||||
* started, so start an explicit subtransaction there.
|
|
||||||
*/
|
|
||||||
using_subtxn = IsTransactionOrTransactionBlock();
|
|
||||||
|
|
||||||
PG_TRY();
|
|
||||||
{
|
|
||||||
Relation relation;
|
|
||||||
HeapTuple tuple;
|
|
||||||
Form_pg_sequence_data seq;
|
|
||||||
Oid reloid;
|
|
||||||
|
|
||||||
if (using_subtxn)
|
|
||||||
BeginInternalSubTransaction("sequence");
|
|
||||||
else
|
|
||||||
StartTransactionCommand();
|
|
||||||
|
|
||||||
reloid = RelidByRelfilenode(rnode.spcNode, rnode.relNode);
|
|
||||||
|
|
||||||
if (reloid == InvalidOid)
|
|
||||||
elog(ERROR, "could not map filenode \"%s\" to relation OID",
|
|
||||||
relpathperm(rnode,
|
|
||||||
MAIN_FORKNUM));
|
|
||||||
|
|
||||||
relation = RelationIdGetRelation(reloid);
|
|
||||||
tuple = &tuplebuf->tuple;
|
|
||||||
seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
|
|
||||||
|
|
||||||
rb->sequence(rb, txn, lsn, relation, transactional,
|
|
||||||
seq->last_value, seq->log_cnt, seq->is_called);
|
|
||||||
|
|
||||||
RelationClose(relation);
|
|
||||||
|
|
||||||
TeardownHistoricSnapshot(false);
|
|
||||||
|
|
||||||
AbortCurrentTransaction();
|
|
||||||
|
|
||||||
if (using_subtxn)
|
|
||||||
RollbackAndReleaseCurrentSubTransaction();
|
|
||||||
}
|
|
||||||
PG_CATCH();
|
|
||||||
{
|
|
||||||
TeardownHistoricSnapshot(true);
|
|
||||||
|
|
||||||
AbortCurrentTransaction();
|
|
||||||
|
|
||||||
if (using_subtxn)
|
|
||||||
RollbackAndReleaseCurrentSubTransaction();
|
|
||||||
|
|
||||||
PG_RE_THROW();
|
|
||||||
}
|
|
||||||
PG_END_TRY();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AssertTXNLsnOrder
|
* AssertTXNLsnOrder
|
||||||
* Verify LSN ordering of transaction lists in the reorderbuffer
|
* Verify LSN ordering of transaction lists in the reorderbuffer
|
||||||
|
@ -1823,9 +1542,6 @@ ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
|
||||||
&found);
|
&found);
|
||||||
Assert(found);
|
Assert(found);
|
||||||
|
|
||||||
/* Remove sequences created in this transaction (if any). */
|
|
||||||
ReorderBufferSequenceCleanup(rb, txn->xid);
|
|
||||||
|
|
||||||
/* remove entries spilled to disk */
|
/* remove entries spilled to disk */
|
||||||
if (rbtxn_is_serialized(txn))
|
if (rbtxn_is_serialized(txn))
|
||||||
ReorderBufferRestoreCleanup(rb, txn);
|
ReorderBufferRestoreCleanup(rb, txn);
|
||||||
|
@ -2241,29 +1957,6 @@ ReorderBufferApplyMessage(ReorderBuffer *rb, ReorderBufferTXN *txn,
|
||||||
change->data.msg.message);
|
change->data.msg.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Helper function for ReorderBufferProcessTXN for applying sequences.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
ReorderBufferApplySequence(ReorderBuffer *rb, ReorderBufferTXN *txn,
|
|
||||||
Relation relation, ReorderBufferChange *change,
|
|
||||||
bool streaming)
|
|
||||||
{
|
|
||||||
HeapTuple tuple;
|
|
||||||
Form_pg_sequence_data seq;
|
|
||||||
|
|
||||||
tuple = &change->data.sequence.tuple->tuple;
|
|
||||||
seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
|
|
||||||
|
|
||||||
/* Only ever called from ReorderBufferApplySequence, so transational. */
|
|
||||||
if (streaming)
|
|
||||||
rb->stream_sequence(rb, txn, change->lsn, relation, true,
|
|
||||||
seq->last_value, seq->log_cnt, seq->is_called);
|
|
||||||
else
|
|
||||||
rb->sequence(rb, txn, change->lsn, relation, true,
|
|
||||||
seq->last_value, seq->log_cnt, seq->is_called);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Function to store the command id and snapshot at the end of the current
|
* Function to store the command id and snapshot at the end of the current
|
||||||
* stream so that we can reuse the same while sending the next stream.
|
* stream so that we can reuse the same while sending the next stream.
|
||||||
|
@ -2706,31 +2399,6 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
|
||||||
elog(ERROR, "tuplecid value in changequeue");
|
elog(ERROR, "tuplecid value in changequeue");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case REORDER_BUFFER_CHANGE_SEQUENCE:
|
|
||||||
Assert(snapshot_now);
|
|
||||||
|
|
||||||
reloid = RelidByRelfilenode(change->data.sequence.relnode.spcNode,
|
|
||||||
change->data.sequence.relnode.relNode);
|
|
||||||
|
|
||||||
if (reloid == InvalidOid)
|
|
||||||
elog(ERROR, "could not map filenode \"%s\" to relation OID",
|
|
||||||
relpathperm(change->data.sequence.relnode,
|
|
||||||
MAIN_FORKNUM));
|
|
||||||
|
|
||||||
relation = RelationIdGetRelation(reloid);
|
|
||||||
|
|
||||||
if (!RelationIsValid(relation))
|
|
||||||
elog(ERROR, "could not open relation with OID %u (for filenode \"%s\")",
|
|
||||||
reloid,
|
|
||||||
relpathperm(change->data.sequence.relnode,
|
|
||||||
MAIN_FORKNUM));
|
|
||||||
|
|
||||||
if (RelationIsLogicallyLogged(relation))
|
|
||||||
ReorderBufferApplySequence(rb, txn, relation, change, streaming);
|
|
||||||
|
|
||||||
RelationClose(relation);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4115,39 +3783,6 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
|
||||||
memcpy(data, change->data.truncate.relids, size);
|
memcpy(data, change->data.truncate.relids, size);
|
||||||
data += size;
|
data += size;
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case REORDER_BUFFER_CHANGE_SEQUENCE:
|
|
||||||
{
|
|
||||||
char *data;
|
|
||||||
ReorderBufferTupleBuf *tup;
|
|
||||||
Size len = 0;
|
|
||||||
|
|
||||||
tup = change->data.sequence.tuple;
|
|
||||||
|
|
||||||
if (tup)
|
|
||||||
{
|
|
||||||
sz += sizeof(HeapTupleData);
|
|
||||||
len = tup->tuple.t_len;
|
|
||||||
sz += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* make sure we have enough space */
|
|
||||||
ReorderBufferSerializeReserve(rb, sz);
|
|
||||||
|
|
||||||
data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
|
|
||||||
/* might have been reallocated above */
|
|
||||||
ondisk = (ReorderBufferDiskChange *) rb->outbuf;
|
|
||||||
|
|
||||||
if (len)
|
|
||||||
{
|
|
||||||
memcpy(data, &tup->tuple, sizeof(HeapTupleData));
|
|
||||||
data += sizeof(HeapTupleData);
|
|
||||||
|
|
||||||
memcpy(data, tup->tuple.t_data, len);
|
|
||||||
data += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
||||||
|
@ -4412,22 +4047,6 @@ ReorderBufferChangeSize(ReorderBufferChange *change)
|
||||||
{
|
{
|
||||||
sz += sizeof(Oid) * change->data.truncate.nrelids;
|
sz += sizeof(Oid) * change->data.truncate.nrelids;
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case REORDER_BUFFER_CHANGE_SEQUENCE:
|
|
||||||
{
|
|
||||||
ReorderBufferTupleBuf *tup;
|
|
||||||
Size len = 0;
|
|
||||||
|
|
||||||
tup = change->data.sequence.tuple;
|
|
||||||
|
|
||||||
if (tup)
|
|
||||||
{
|
|
||||||
sz += sizeof(HeapTupleData);
|
|
||||||
len = tup->tuple.t_len;
|
|
||||||
sz += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
||||||
|
@ -4729,30 +4348,6 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case REORDER_BUFFER_CHANGE_SEQUENCE:
|
|
||||||
if (change->data.sequence.tuple)
|
|
||||||
{
|
|
||||||
uint32 tuplelen = ((HeapTuple) data)->t_len;
|
|
||||||
|
|
||||||
change->data.sequence.tuple =
|
|
||||||
ReorderBufferGetTupleBuf(rb, tuplelen - SizeofHeapTupleHeader);
|
|
||||||
|
|
||||||
/* restore ->tuple */
|
|
||||||
memcpy(&change->data.sequence.tuple->tuple, data,
|
|
||||||
sizeof(HeapTupleData));
|
|
||||||
data += sizeof(HeapTupleData);
|
|
||||||
|
|
||||||
/* reset t_data pointer into the new tuplebuf */
|
|
||||||
change->data.sequence.tuple->tuple.t_data =
|
|
||||||
ReorderBufferTupleBufData(change->data.sequence.tuple);
|
|
||||||
|
|
||||||
/* restore tuple data itself */
|
|
||||||
memcpy(change->data.sequence.tuple->tuple.t_data, data, tuplelen);
|
|
||||||
data += tuplelen;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
||||||
|
|
|
@ -100,7 +100,6 @@
|
||||||
#include "catalog/pg_subscription_rel.h"
|
#include "catalog/pg_subscription_rel.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/copy.h"
|
#include "commands/copy.h"
|
||||||
#include "commands/sequence.h"
|
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "parser/parse_relation.h"
|
#include "parser/parse_relation.h"
|
||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
|
@ -1137,95 +1136,6 @@ copy_table(Relation rel)
|
||||||
logicalrep_rel_close(relmapentry, NoLock);
|
logicalrep_rel_close(relmapentry, NoLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Fetch sequence data (current state) from the remote node.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
fetch_sequence_data(char *nspname, char *relname,
|
|
||||||
int64 *last_value, int64 *log_cnt, bool *is_called)
|
|
||||||
{
|
|
||||||
WalRcvExecResult *res;
|
|
||||||
StringInfoData cmd;
|
|
||||||
TupleTableSlot *slot;
|
|
||||||
Oid tableRow[3] = {INT8OID, INT8OID, BOOLOID};
|
|
||||||
|
|
||||||
initStringInfo(&cmd);
|
|
||||||
appendStringInfo(&cmd, "SELECT last_value, log_cnt, is_called\n"
|
|
||||||
" FROM %s", quote_qualified_identifier(nspname, relname));
|
|
||||||
|
|
||||||
res = walrcv_exec(LogRepWorkerWalRcvConn, cmd.data, 3, tableRow);
|
|
||||||
pfree(cmd.data);
|
|
||||||
|
|
||||||
if (res->status != WALRCV_OK_TUPLES)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errmsg("could not receive list of replicated tables from the publisher: %s",
|
|
||||||
res->err)));
|
|
||||||
|
|
||||||
/* Process the sequence. */
|
|
||||||
slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple);
|
|
||||||
while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
|
|
||||||
{
|
|
||||||
bool isnull;
|
|
||||||
|
|
||||||
*last_value = DatumGetInt64(slot_getattr(slot, 1, &isnull));
|
|
||||||
Assert(!isnull);
|
|
||||||
|
|
||||||
*log_cnt = DatumGetInt64(slot_getattr(slot, 2, &isnull));
|
|
||||||
Assert(!isnull);
|
|
||||||
|
|
||||||
*is_called = DatumGetBool(slot_getattr(slot, 3, &isnull));
|
|
||||||
Assert(!isnull);
|
|
||||||
|
|
||||||
ExecClearTuple(slot);
|
|
||||||
}
|
|
||||||
ExecDropSingleTupleTableSlot(slot);
|
|
||||||
|
|
||||||
walrcv_clear_result(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy existing data of a sequence from publisher.
|
|
||||||
*
|
|
||||||
* Caller is responsible for locking the local relation.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
copy_sequence(Relation rel)
|
|
||||||
{
|
|
||||||
LogicalRepRelMapEntry *relmapentry;
|
|
||||||
LogicalRepRelation lrel;
|
|
||||||
List *qual = NIL;
|
|
||||||
StringInfoData cmd;
|
|
||||||
int64 last_value = 0,
|
|
||||||
log_cnt = 0;
|
|
||||||
bool is_called = 0;
|
|
||||||
|
|
||||||
/* Get the publisher relation info. */
|
|
||||||
fetch_remote_table_info(get_namespace_name(RelationGetNamespace(rel)),
|
|
||||||
RelationGetRelationName(rel), &lrel, &qual);
|
|
||||||
|
|
||||||
/* sequences don't have row filters */
|
|
||||||
Assert(!qual);
|
|
||||||
|
|
||||||
/* Put the relation into relmap. */
|
|
||||||
logicalrep_relmap_update(&lrel);
|
|
||||||
|
|
||||||
/* Map the publisher relation to local one. */
|
|
||||||
relmapentry = logicalrep_rel_open(lrel.remoteid, NoLock);
|
|
||||||
Assert(rel == relmapentry->localrel);
|
|
||||||
|
|
||||||
/* Start copy on the publisher. */
|
|
||||||
initStringInfo(&cmd);
|
|
||||||
|
|
||||||
Assert(lrel.relkind == RELKIND_SEQUENCE);
|
|
||||||
|
|
||||||
fetch_sequence_data(lrel.nspname, lrel.relname, &last_value, &log_cnt, &is_called);
|
|
||||||
|
|
||||||
/* tablesync sets the sequences in non-transactional way */
|
|
||||||
SetSequence(RelationGetRelid(rel), false, last_value, log_cnt, is_called);
|
|
||||||
|
|
||||||
logicalrep_rel_close(relmapentry, NoLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine the tablesync slot name.
|
* Determine the tablesync slot name.
|
||||||
*
|
*
|
||||||
|
@ -1487,21 +1397,10 @@ LogicalRepSyncTableStart(XLogRecPtr *origin_startpos)
|
||||||
originname)));
|
originname)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do the right action depending on the relation kind. */
|
/* Now do the initial data copy */
|
||||||
if (get_rel_relkind(RelationGetRelid(rel)) == RELKIND_SEQUENCE)
|
PushActiveSnapshot(GetTransactionSnapshot());
|
||||||
{
|
copy_table(rel);
|
||||||
/* Now do the initial sequence copy */
|
PopActiveSnapshot();
|
||||||
PushActiveSnapshot(GetTransactionSnapshot());
|
|
||||||
copy_sequence(rel);
|
|
||||||
PopActiveSnapshot();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Now do the initial data copy */
|
|
||||||
PushActiveSnapshot(GetTransactionSnapshot());
|
|
||||||
copy_table(rel);
|
|
||||||
PopActiveSnapshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
res = walrcv_exec(LogRepWorkerWalRcvConn, "COMMIT", 0, NULL);
|
res = walrcv_exec(LogRepWorkerWalRcvConn, "COMMIT", 0, NULL);
|
||||||
if (res->status != WALRCV_OK_COMMAND)
|
if (res->status != WALRCV_OK_COMMAND)
|
||||||
|
|
|
@ -143,7 +143,6 @@
|
||||||
#include "catalog/pg_subscription.h"
|
#include "catalog/pg_subscription.h"
|
||||||
#include "catalog/pg_subscription_rel.h"
|
#include "catalog/pg_subscription_rel.h"
|
||||||
#include "catalog/pg_tablespace.h"
|
#include "catalog/pg_tablespace.h"
|
||||||
#include "commands/sequence.h"
|
|
||||||
#include "commands/tablecmds.h"
|
#include "commands/tablecmds.h"
|
||||||
#include "commands/tablespace.h"
|
#include "commands/tablespace.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
|
@ -1144,57 +1143,6 @@ apply_handle_origin(StringInfo s)
|
||||||
errmsg_internal("ORIGIN message sent out of order")));
|
errmsg_internal("ORIGIN message sent out of order")));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle SEQUENCE message.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
apply_handle_sequence(StringInfo s)
|
|
||||||
{
|
|
||||||
LogicalRepSequence seq;
|
|
||||||
Oid relid;
|
|
||||||
|
|
||||||
if (handle_streamed_transaction(LOGICAL_REP_MSG_SEQUENCE, s))
|
|
||||||
return;
|
|
||||||
|
|
||||||
logicalrep_read_sequence(s, &seq);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Non-transactional sequence updates should not be part of a remote
|
|
||||||
* transaction. There should not be any running transaction.
|
|
||||||
*/
|
|
||||||
Assert((!seq.transactional) || in_remote_transaction);
|
|
||||||
Assert(!(!seq.transactional && in_remote_transaction));
|
|
||||||
Assert(!(!seq.transactional && IsTransactionState()));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure we're in a transaction (needed by SetSequence). For
|
|
||||||
* non-transactional updates we're guaranteed to start a new one,
|
|
||||||
* and we'll commit it at the end.
|
|
||||||
*/
|
|
||||||
if (!IsTransactionState())
|
|
||||||
{
|
|
||||||
StartTransactionCommand();
|
|
||||||
maybe_reread_subscription();
|
|
||||||
}
|
|
||||||
|
|
||||||
relid = RangeVarGetRelid(makeRangeVar(seq.nspname,
|
|
||||||
seq.seqname, -1),
|
|
||||||
RowExclusiveLock, false);
|
|
||||||
|
|
||||||
/* lock the sequence in AccessExclusiveLock, as expected by SetSequence */
|
|
||||||
LockRelationOid(relid, AccessExclusiveLock);
|
|
||||||
|
|
||||||
/* apply the sequence change */
|
|
||||||
SetSequence(relid, seq.transactional, seq.last_value, seq.log_cnt, seq.is_called);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Commit the per-stream transaction (we only do this when not in
|
|
||||||
* remote transaction, i.e. for non-transactional sequence updates.
|
|
||||||
*/
|
|
||||||
if (!in_remote_transaction)
|
|
||||||
CommitTransactionCommand();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle STREAM START message.
|
* Handle STREAM START message.
|
||||||
*/
|
*/
|
||||||
|
@ -2563,10 +2511,6 @@ apply_dispatch(StringInfo s)
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LOGICAL_REP_MSG_SEQUENCE:
|
|
||||||
apply_handle_sequence(s);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case LOGICAL_REP_MSG_STREAM_START:
|
case LOGICAL_REP_MSG_STREAM_START:
|
||||||
apply_handle_stream_start(s);
|
apply_handle_stream_start(s);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include "access/tupconvert.h"
|
#include "access/tupconvert.h"
|
||||||
#include "catalog/partition.h"
|
#include "catalog/partition.h"
|
||||||
#include "catalog/pg_publication.h"
|
#include "catalog/pg_publication.h"
|
||||||
#include "catalog/pg_publication_namespace.h"
|
|
||||||
#include "catalog/pg_publication_rel.h"
|
#include "catalog/pg_publication_rel.h"
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
|
@ -55,10 +54,6 @@ static void pgoutput_message(LogicalDecodingContext *ctx,
|
||||||
ReorderBufferTXN *txn, XLogRecPtr message_lsn,
|
ReorderBufferTXN *txn, XLogRecPtr message_lsn,
|
||||||
bool transactional, const char *prefix,
|
bool transactional, const char *prefix,
|
||||||
Size sz, const char *message);
|
Size sz, const char *message);
|
||||||
static void pgoutput_sequence(LogicalDecodingContext *ctx,
|
|
||||||
ReorderBufferTXN *txn, XLogRecPtr sequence_lsn,
|
|
||||||
Relation relation, bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called);
|
|
||||||
static bool pgoutput_origin_filter(LogicalDecodingContext *ctx,
|
static bool pgoutput_origin_filter(LogicalDecodingContext *ctx,
|
||||||
RepOriginId origin_id);
|
RepOriginId origin_id);
|
||||||
static void pgoutput_begin_prepare_txn(LogicalDecodingContext *ctx,
|
static void pgoutput_begin_prepare_txn(LogicalDecodingContext *ctx,
|
||||||
|
@ -260,7 +255,6 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
|
||||||
cb->change_cb = pgoutput_change;
|
cb->change_cb = pgoutput_change;
|
||||||
cb->truncate_cb = pgoutput_truncate;
|
cb->truncate_cb = pgoutput_truncate;
|
||||||
cb->message_cb = pgoutput_message;
|
cb->message_cb = pgoutput_message;
|
||||||
cb->sequence_cb = pgoutput_sequence;
|
|
||||||
cb->commit_cb = pgoutput_commit_txn;
|
cb->commit_cb = pgoutput_commit_txn;
|
||||||
|
|
||||||
cb->begin_prepare_cb = pgoutput_begin_prepare_txn;
|
cb->begin_prepare_cb = pgoutput_begin_prepare_txn;
|
||||||
|
@ -277,7 +271,6 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
|
||||||
cb->stream_commit_cb = pgoutput_stream_commit;
|
cb->stream_commit_cb = pgoutput_stream_commit;
|
||||||
cb->stream_change_cb = pgoutput_change;
|
cb->stream_change_cb = pgoutput_change;
|
||||||
cb->stream_message_cb = pgoutput_message;
|
cb->stream_message_cb = pgoutput_message;
|
||||||
cb->stream_sequence_cb = pgoutput_sequence;
|
|
||||||
cb->stream_truncate_cb = pgoutput_truncate;
|
cb->stream_truncate_cb = pgoutput_truncate;
|
||||||
/* transaction streaming - two-phase commit */
|
/* transaction streaming - two-phase commit */
|
||||||
cb->stream_prepare_cb = pgoutput_stream_prepare_txn;
|
cb->stream_prepare_cb = pgoutput_stream_prepare_txn;
|
||||||
|
@ -291,7 +284,6 @@ parse_output_parameters(List *options, PGOutputData *data)
|
||||||
bool publication_names_given = false;
|
bool publication_names_given = false;
|
||||||
bool binary_option_given = false;
|
bool binary_option_given = false;
|
||||||
bool messages_option_given = false;
|
bool messages_option_given = false;
|
||||||
bool sequences_option_given = false;
|
|
||||||
bool streaming_given = false;
|
bool streaming_given = false;
|
||||||
bool two_phase_option_given = false;
|
bool two_phase_option_given = false;
|
||||||
|
|
||||||
|
@ -299,7 +291,6 @@ parse_output_parameters(List *options, PGOutputData *data)
|
||||||
data->streaming = false;
|
data->streaming = false;
|
||||||
data->messages = false;
|
data->messages = false;
|
||||||
data->two_phase = false;
|
data->two_phase = false;
|
||||||
data->sequences = true;
|
|
||||||
|
|
||||||
foreach(lc, options)
|
foreach(lc, options)
|
||||||
{
|
{
|
||||||
|
@ -368,16 +359,6 @@ parse_output_parameters(List *options, PGOutputData *data)
|
||||||
|
|
||||||
data->messages = defGetBoolean(defel);
|
data->messages = defGetBoolean(defel);
|
||||||
}
|
}
|
||||||
else if (strcmp(defel->defname, "sequences") == 0)
|
|
||||||
{
|
|
||||||
if (sequences_option_given)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("conflicting or redundant options")));
|
|
||||||
sequences_option_given = true;
|
|
||||||
|
|
||||||
data->sequences = defGetBoolean(defel);
|
|
||||||
}
|
|
||||||
else if (strcmp(defel->defname, "streaming") == 0)
|
else if (strcmp(defel->defname, "streaming") == 0)
|
||||||
{
|
{
|
||||||
if (streaming_given)
|
if (streaming_given)
|
||||||
|
@ -1709,64 +1690,6 @@ pgoutput_message(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
|
||||||
OutputPluginWrite(ctx, true);
|
OutputPluginWrite(ctx, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
pgoutput_sequence(LogicalDecodingContext *ctx,
|
|
||||||
ReorderBufferTXN *txn, XLogRecPtr sequence_lsn,
|
|
||||||
Relation relation, bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt, bool is_called)
|
|
||||||
{
|
|
||||||
PGOutputData *data = (PGOutputData *) ctx->output_plugin_private;
|
|
||||||
TransactionId xid = InvalidTransactionId;
|
|
||||||
RelationSyncEntry *relentry;
|
|
||||||
|
|
||||||
if (!data->sequences)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!is_publishable_relation(relation))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remember the xid for the message in streaming mode. See
|
|
||||||
* pgoutput_change.
|
|
||||||
*/
|
|
||||||
if (in_streaming)
|
|
||||||
xid = txn->xid;
|
|
||||||
|
|
||||||
relentry = get_rel_sync_entry(data, relation);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* First check the sequence filter.
|
|
||||||
*
|
|
||||||
* We handle just REORDER_BUFFER_CHANGE_SEQUENCE here.
|
|
||||||
*/
|
|
||||||
if (!relentry->pubactions.pubsequence)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Output BEGIN if we haven't yet. Avoid for non-transactional
|
|
||||||
* sequence changes.
|
|
||||||
*/
|
|
||||||
if (transactional)
|
|
||||||
{
|
|
||||||
PGOutputTxnData *txndata = (PGOutputTxnData *) txn->output_plugin_private;
|
|
||||||
|
|
||||||
/* Send BEGIN if we haven't yet */
|
|
||||||
if (txndata && !txndata->sent_begin_txn)
|
|
||||||
pgoutput_send_begin(ctx, txn);
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
|
||||||
logicalrep_write_sequence(ctx->out,
|
|
||||||
relation,
|
|
||||||
xid,
|
|
||||||
sequence_lsn,
|
|
||||||
transactional,
|
|
||||||
last_value,
|
|
||||||
log_cnt,
|
|
||||||
is_called);
|
|
||||||
OutputPluginWrite(ctx, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Currently we always forward.
|
* Currently we always forward.
|
||||||
*/
|
*/
|
||||||
|
@ -2052,8 +1975,7 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
|
||||||
entry->schema_sent = false;
|
entry->schema_sent = false;
|
||||||
entry->streamed_txns = NIL;
|
entry->streamed_txns = NIL;
|
||||||
entry->pubactions.pubinsert = entry->pubactions.pubupdate =
|
entry->pubactions.pubinsert = entry->pubactions.pubupdate =
|
||||||
entry->pubactions.pubdelete = entry->pubactions.pubtruncate =
|
entry->pubactions.pubdelete = entry->pubactions.pubtruncate = false;
|
||||||
entry->pubactions.pubsequence = false;
|
|
||||||
entry->new_slot = NULL;
|
entry->new_slot = NULL;
|
||||||
entry->old_slot = NULL;
|
entry->old_slot = NULL;
|
||||||
memset(entry->exprstate, 0, sizeof(entry->exprstate));
|
memset(entry->exprstate, 0, sizeof(entry->exprstate));
|
||||||
|
@ -2068,18 +1990,18 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
|
||||||
{
|
{
|
||||||
Oid schemaId = get_rel_namespace(relid);
|
Oid schemaId = get_rel_namespace(relid);
|
||||||
List *pubids = GetRelationPublications(relid);
|
List *pubids = GetRelationPublications(relid);
|
||||||
char relkind = get_rel_relkind(relid);
|
|
||||||
char objectType = pub_get_object_type_for_relkind(relkind);
|
|
||||||
/*
|
/*
|
||||||
* We don't acquire a lock on the namespace system table as we build
|
* We don't acquire a lock on the namespace system table as we build
|
||||||
* the cache entry using a historic snapshot and all the later changes
|
* the cache entry using a historic snapshot and all the later changes
|
||||||
* are absorbed while decoding WAL.
|
* are absorbed while decoding WAL.
|
||||||
*/
|
*/
|
||||||
List *schemaPubids = GetSchemaPublications(schemaId, objectType);
|
List *schemaPubids = GetSchemaPublications(schemaId);
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
Oid publish_as_relid = relid;
|
Oid publish_as_relid = relid;
|
||||||
int publish_ancestor_level = 0;
|
int publish_ancestor_level = 0;
|
||||||
bool am_partition = get_rel_relispartition(relid);
|
bool am_partition = get_rel_relispartition(relid);
|
||||||
|
char relkind = get_rel_relkind(relid);
|
||||||
List *rel_publications = NIL;
|
List *rel_publications = NIL;
|
||||||
|
|
||||||
/* Reload publications if needed before use. */
|
/* Reload publications if needed before use. */
|
||||||
|
@ -2111,7 +2033,6 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
|
||||||
entry->pubactions.pubupdate = false;
|
entry->pubactions.pubupdate = false;
|
||||||
entry->pubactions.pubdelete = false;
|
entry->pubactions.pubdelete = false;
|
||||||
entry->pubactions.pubtruncate = false;
|
entry->pubactions.pubtruncate = false;
|
||||||
entry->pubactions.pubsequence = false;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tuple slots cleanups. (Will be rebuilt later if needed).
|
* Tuple slots cleanups. (Will be rebuilt later if needed).
|
||||||
|
@ -2159,11 +2080,9 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is a FOR ALL TABLES publication, pick the partition root
|
* If this is a FOR ALL TABLES publication, pick the partition root
|
||||||
* and set the ancestor level accordingly. If this is a FOR ALL
|
* and set the ancestor level accordingly.
|
||||||
* SEQUENCES publication, we publish it too but we don't need to
|
|
||||||
* pick the partition root etc.
|
|
||||||
*/
|
*/
|
||||||
if (pub->alltables || pub->allsequences)
|
if (pub->alltables)
|
||||||
{
|
{
|
||||||
publish = true;
|
publish = true;
|
||||||
if (pub->pubviaroot && am_partition)
|
if (pub->pubviaroot && am_partition)
|
||||||
|
@ -2227,7 +2146,6 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
|
||||||
entry->pubactions.pubupdate |= pub->pubactions.pubupdate;
|
entry->pubactions.pubupdate |= pub->pubactions.pubupdate;
|
||||||
entry->pubactions.pubdelete |= pub->pubactions.pubdelete;
|
entry->pubactions.pubdelete |= pub->pubactions.pubdelete;
|
||||||
entry->pubactions.pubtruncate |= pub->pubactions.pubtruncate;
|
entry->pubactions.pubtruncate |= pub->pubactions.pubtruncate;
|
||||||
entry->pubactions.pubsequence |= pub->pubactions.pubsequence;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We want to publish the changes as the top-most ancestor
|
* We want to publish the changes as the top-most ancestor
|
||||||
|
|
|
@ -56,7 +56,6 @@
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_publication.h"
|
#include "catalog/pg_publication.h"
|
||||||
#include "catalog/pg_publication_namespace.h"
|
|
||||||
#include "catalog/pg_rewrite.h"
|
#include "catalog/pg_rewrite.h"
|
||||||
#include "catalog/pg_shseclabel.h"
|
#include "catalog/pg_shseclabel.h"
|
||||||
#include "catalog/pg_statistic_ext.h"
|
#include "catalog/pg_statistic_ext.h"
|
||||||
|
@ -5568,8 +5567,6 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
|
||||||
Oid schemaid;
|
Oid schemaid;
|
||||||
List *ancestors = NIL;
|
List *ancestors = NIL;
|
||||||
Oid relid = RelationGetRelid(relation);
|
Oid relid = RelationGetRelid(relation);
|
||||||
char relkind = relation->rd_rel->relkind;
|
|
||||||
char objType;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If not publishable, it publishes no actions. (pgoutput_change() will
|
* If not publishable, it publishes no actions. (pgoutput_change() will
|
||||||
|
@ -5600,15 +5597,8 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
|
||||||
/* Fetch the publication membership info. */
|
/* Fetch the publication membership info. */
|
||||||
puboids = GetRelationPublications(relid);
|
puboids = GetRelationPublications(relid);
|
||||||
schemaid = RelationGetNamespace(relation);
|
schemaid = RelationGetNamespace(relation);
|
||||||
objType = pub_get_object_type_for_relkind(relkind);
|
puboids = list_concat_unique_oid(puboids, GetSchemaPublications(schemaid));
|
||||||
|
|
||||||
puboids = list_concat_unique_oid(puboids,
|
|
||||||
GetSchemaPublications(schemaid, objType));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If this is a partion (and thus a table), lookup all ancestors and track
|
|
||||||
* all publications them too.
|
|
||||||
*/
|
|
||||||
if (relation->rd_rel->relispartition)
|
if (relation->rd_rel->relispartition)
|
||||||
{
|
{
|
||||||
/* Add publications that the ancestors are in too. */
|
/* Add publications that the ancestors are in too. */
|
||||||
|
@ -5620,23 +5610,12 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
|
||||||
|
|
||||||
puboids = list_concat_unique_oid(puboids,
|
puboids = list_concat_unique_oid(puboids,
|
||||||
GetRelationPublications(ancestor));
|
GetRelationPublications(ancestor));
|
||||||
|
|
||||||
/* include all publications publishing schema of all ancestors */
|
|
||||||
schemaid = get_rel_namespace(ancestor);
|
schemaid = get_rel_namespace(ancestor);
|
||||||
puboids = list_concat_unique_oid(puboids,
|
puboids = list_concat_unique_oid(puboids,
|
||||||
GetSchemaPublications(schemaid,
|
GetSchemaPublications(schemaid));
|
||||||
PUB_OBJTYPE_TABLE));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
|
||||||
/*
|
|
||||||
* Consider also FOR ALL TABLES and FOR ALL SEQUENCES publications,
|
|
||||||
* depending on the relkind of the relation.
|
|
||||||
*/
|
|
||||||
if (relation->rd_rel->relkind == RELKIND_SEQUENCE)
|
|
||||||
puboids = list_concat_unique_oid(puboids, GetAllSequencesPublications());
|
|
||||||
else
|
|
||||||
puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
|
|
||||||
|
|
||||||
foreach(lc, puboids)
|
foreach(lc, puboids)
|
||||||
{
|
{
|
||||||
|
@ -5655,7 +5634,6 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
|
||||||
pubdesc->pubactions.pubupdate |= pubform->pubupdate;
|
pubdesc->pubactions.pubupdate |= pubform->pubupdate;
|
||||||
pubdesc->pubactions.pubdelete |= pubform->pubdelete;
|
pubdesc->pubactions.pubdelete |= pubform->pubdelete;
|
||||||
pubdesc->pubactions.pubtruncate |= pubform->pubtruncate;
|
pubdesc->pubactions.pubtruncate |= pubform->pubtruncate;
|
||||||
pubdesc->pubactions.pubsequence |= pubform->pubsequence;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if all columns referenced in the filter expression are part of
|
* Check if all columns referenced in the filter expression are part of
|
||||||
|
|
|
@ -653,12 +653,12 @@ static const struct cachedesc cacheinfo[] = {
|
||||||
64
|
64
|
||||||
},
|
},
|
||||||
{PublicationNamespaceRelationId, /* PUBLICATIONNAMESPACEMAP */
|
{PublicationNamespaceRelationId, /* PUBLICATIONNAMESPACEMAP */
|
||||||
PublicationNamespacePnnspidPnpubidPntypeIndexId,
|
PublicationNamespacePnnspidPnpubidIndexId,
|
||||||
3,
|
2,
|
||||||
{
|
{
|
||||||
Anum_pg_publication_namespace_pnnspid,
|
Anum_pg_publication_namespace_pnnspid,
|
||||||
Anum_pg_publication_namespace_pnpubid,
|
Anum_pg_publication_namespace_pnpubid,
|
||||||
Anum_pg_publication_namespace_pntype,
|
0,
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
64
|
64
|
||||||
|
|
|
@ -3814,12 +3814,10 @@ getPublications(Archive *fout, int *numPublications)
|
||||||
int i_pubname;
|
int i_pubname;
|
||||||
int i_pubowner;
|
int i_pubowner;
|
||||||
int i_puballtables;
|
int i_puballtables;
|
||||||
int i_puballsequences;
|
|
||||||
int i_pubinsert;
|
int i_pubinsert;
|
||||||
int i_pubupdate;
|
int i_pubupdate;
|
||||||
int i_pubdelete;
|
int i_pubdelete;
|
||||||
int i_pubtruncate;
|
int i_pubtruncate;
|
||||||
int i_pubsequence;
|
|
||||||
int i_pubviaroot;
|
int i_pubviaroot;
|
||||||
int i,
|
int i,
|
||||||
ntups;
|
ntups;
|
||||||
|
@ -3835,29 +3833,23 @@ getPublications(Archive *fout, int *numPublications)
|
||||||
resetPQExpBuffer(query);
|
resetPQExpBuffer(query);
|
||||||
|
|
||||||
/* Get the publications. */
|
/* Get the publications. */
|
||||||
if (fout->remoteVersion >= 150000)
|
if (fout->remoteVersion >= 130000)
|
||||||
appendPQExpBuffer(query,
|
appendPQExpBuffer(query,
|
||||||
"SELECT p.tableoid, p.oid, p.pubname, "
|
"SELECT p.tableoid, p.oid, p.pubname, "
|
||||||
"p.pubowner, "
|
"p.pubowner, "
|
||||||
"p.puballtables, p.puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubsequence, p.pubviaroot "
|
"p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot "
|
||||||
"FROM pg_publication p");
|
|
||||||
else if (fout->remoteVersion >= 130000)
|
|
||||||
appendPQExpBuffer(query,
|
|
||||||
"SELECT p.tableoid, p.oid, p.pubname, "
|
|
||||||
"p.pubowner, "
|
|
||||||
"p.puballtables, false AS puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubsequence, p.pubviaroot "
|
|
||||||
"FROM pg_publication p");
|
"FROM pg_publication p");
|
||||||
else if (fout->remoteVersion >= 110000)
|
else if (fout->remoteVersion >= 110000)
|
||||||
appendPQExpBuffer(query,
|
appendPQExpBuffer(query,
|
||||||
"SELECT p.tableoid, p.oid, p.pubname, "
|
"SELECT p.tableoid, p.oid, p.pubname, "
|
||||||
"p.pubowner, "
|
"p.pubowner, "
|
||||||
"p.puballtables, false AS puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubsequence, false AS pubviaroot "
|
"p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot "
|
||||||
"FROM pg_publication p");
|
"FROM pg_publication p");
|
||||||
else
|
else
|
||||||
appendPQExpBuffer(query,
|
appendPQExpBuffer(query,
|
||||||
"SELECT p.tableoid, p.oid, p.pubname, "
|
"SELECT p.tableoid, p.oid, p.pubname, "
|
||||||
"p.pubowner, "
|
"p.pubowner, "
|
||||||
"p.puballtables, false AS puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubsequence, false AS pubviaroot "
|
"p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot "
|
||||||
"FROM pg_publication p");
|
"FROM pg_publication p");
|
||||||
|
|
||||||
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
||||||
|
@ -3869,12 +3861,10 @@ getPublications(Archive *fout, int *numPublications)
|
||||||
i_pubname = PQfnumber(res, "pubname");
|
i_pubname = PQfnumber(res, "pubname");
|
||||||
i_pubowner = PQfnumber(res, "pubowner");
|
i_pubowner = PQfnumber(res, "pubowner");
|
||||||
i_puballtables = PQfnumber(res, "puballtables");
|
i_puballtables = PQfnumber(res, "puballtables");
|
||||||
i_puballsequences = PQfnumber(res, "puballsequences");
|
|
||||||
i_pubinsert = PQfnumber(res, "pubinsert");
|
i_pubinsert = PQfnumber(res, "pubinsert");
|
||||||
i_pubupdate = PQfnumber(res, "pubupdate");
|
i_pubupdate = PQfnumber(res, "pubupdate");
|
||||||
i_pubdelete = PQfnumber(res, "pubdelete");
|
i_pubdelete = PQfnumber(res, "pubdelete");
|
||||||
i_pubtruncate = PQfnumber(res, "pubtruncate");
|
i_pubtruncate = PQfnumber(res, "pubtruncate");
|
||||||
i_pubsequence = PQfnumber(res, "pubsequence");
|
|
||||||
i_pubviaroot = PQfnumber(res, "pubviaroot");
|
i_pubviaroot = PQfnumber(res, "pubviaroot");
|
||||||
|
|
||||||
pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
|
pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
|
||||||
|
@ -3890,8 +3880,6 @@ getPublications(Archive *fout, int *numPublications)
|
||||||
pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
|
pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
|
||||||
pubinfo[i].puballtables =
|
pubinfo[i].puballtables =
|
||||||
(strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
|
(strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
|
||||||
pubinfo[i].puballsequences =
|
|
||||||
(strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
|
|
||||||
pubinfo[i].pubinsert =
|
pubinfo[i].pubinsert =
|
||||||
(strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
|
(strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
|
||||||
pubinfo[i].pubupdate =
|
pubinfo[i].pubupdate =
|
||||||
|
@ -3900,8 +3888,6 @@ getPublications(Archive *fout, int *numPublications)
|
||||||
(strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
|
(strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
|
||||||
pubinfo[i].pubtruncate =
|
pubinfo[i].pubtruncate =
|
||||||
(strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
|
(strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
|
||||||
pubinfo[i].pubsequence =
|
|
||||||
(strcmp(PQgetvalue(res, i, i_pubsequence), "t") == 0);
|
|
||||||
pubinfo[i].pubviaroot =
|
pubinfo[i].pubviaroot =
|
||||||
(strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
|
(strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
|
||||||
|
|
||||||
|
@ -3947,9 +3933,6 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
|
||||||
if (pubinfo->puballtables)
|
if (pubinfo->puballtables)
|
||||||
appendPQExpBufferStr(query, " FOR ALL TABLES");
|
appendPQExpBufferStr(query, " FOR ALL TABLES");
|
||||||
|
|
||||||
if (pubinfo->puballsequences)
|
|
||||||
appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
|
|
||||||
|
|
||||||
appendPQExpBufferStr(query, " WITH (publish = '");
|
appendPQExpBufferStr(query, " WITH (publish = '");
|
||||||
if (pubinfo->pubinsert)
|
if (pubinfo->pubinsert)
|
||||||
{
|
{
|
||||||
|
@ -3984,15 +3967,6 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pubinfo->pubsequence)
|
|
||||||
{
|
|
||||||
if (!first)
|
|
||||||
appendPQExpBufferStr(query, ", ");
|
|
||||||
|
|
||||||
appendPQExpBufferStr(query, "sequence");
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
appendPQExpBufferStr(query, "'");
|
appendPQExpBufferStr(query, "'");
|
||||||
|
|
||||||
if (pubinfo->pubviaroot)
|
if (pubinfo->pubviaroot)
|
||||||
|
@ -4039,7 +4013,6 @@ getPublicationNamespaces(Archive *fout)
|
||||||
int i_oid;
|
int i_oid;
|
||||||
int i_pnpubid;
|
int i_pnpubid;
|
||||||
int i_pnnspid;
|
int i_pnnspid;
|
||||||
int i_pntype;
|
|
||||||
int i,
|
int i,
|
||||||
j,
|
j,
|
||||||
ntups;
|
ntups;
|
||||||
|
@ -4051,7 +4024,7 @@ getPublicationNamespaces(Archive *fout)
|
||||||
|
|
||||||
/* Collect all publication membership info. */
|
/* Collect all publication membership info. */
|
||||||
appendPQExpBufferStr(query,
|
appendPQExpBufferStr(query,
|
||||||
"SELECT tableoid, oid, pnpubid, pnnspid, pntype "
|
"SELECT tableoid, oid, pnpubid, pnnspid "
|
||||||
"FROM pg_catalog.pg_publication_namespace");
|
"FROM pg_catalog.pg_publication_namespace");
|
||||||
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
||||||
|
|
||||||
|
@ -4061,7 +4034,6 @@ getPublicationNamespaces(Archive *fout)
|
||||||
i_oid = PQfnumber(res, "oid");
|
i_oid = PQfnumber(res, "oid");
|
||||||
i_pnpubid = PQfnumber(res, "pnpubid");
|
i_pnpubid = PQfnumber(res, "pnpubid");
|
||||||
i_pnnspid = PQfnumber(res, "pnnspid");
|
i_pnnspid = PQfnumber(res, "pnnspid");
|
||||||
i_pntype = PQfnumber(res, "pntype");
|
|
||||||
|
|
||||||
/* this allocation may be more than we need */
|
/* this allocation may be more than we need */
|
||||||
pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
|
pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
|
||||||
|
@ -4071,7 +4043,6 @@ getPublicationNamespaces(Archive *fout)
|
||||||
{
|
{
|
||||||
Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
|
Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
|
||||||
Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
|
Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
|
||||||
char pntype = PQgetvalue(res, i, i_pntype)[0];
|
|
||||||
PublicationInfo *pubinfo;
|
PublicationInfo *pubinfo;
|
||||||
NamespaceInfo *nspinfo;
|
NamespaceInfo *nspinfo;
|
||||||
|
|
||||||
|
@ -4103,7 +4074,6 @@ getPublicationNamespaces(Archive *fout)
|
||||||
pubsinfo[j].dobj.name = nspinfo->dobj.name;
|
pubsinfo[j].dobj.name = nspinfo->dobj.name;
|
||||||
pubsinfo[j].publication = pubinfo;
|
pubsinfo[j].publication = pubinfo;
|
||||||
pubsinfo[j].pubschema = nspinfo;
|
pubsinfo[j].pubschema = nspinfo;
|
||||||
pubsinfo[j].pubtype = pntype;
|
|
||||||
|
|
||||||
/* Decide whether we want to dump it */
|
/* Decide whether we want to dump it */
|
||||||
selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
|
selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
|
||||||
|
@ -4269,11 +4239,7 @@ dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
|
||||||
query = createPQExpBuffer();
|
query = createPQExpBuffer();
|
||||||
|
|
||||||
appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
|
appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
|
||||||
|
appendPQExpBuffer(query, "ADD ALL TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
|
||||||
if (pubsinfo->pubtype == 't')
|
|
||||||
appendPQExpBuffer(query, "ADD ALL TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
|
|
||||||
else
|
|
||||||
appendPQExpBuffer(query, "ADD ALL SEQUENCES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There is no point in creating drop query as the drop is done by schema
|
* There is no point in creating drop query as the drop is done by schema
|
||||||
|
@ -4306,7 +4272,6 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
|
||||||
TableInfo *tbinfo = pubrinfo->pubtable;
|
TableInfo *tbinfo = pubrinfo->pubtable;
|
||||||
PQExpBuffer query;
|
PQExpBuffer query;
|
||||||
char *tag;
|
char *tag;
|
||||||
char *description;
|
|
||||||
|
|
||||||
/* Do nothing in data-only dump */
|
/* Do nothing in data-only dump */
|
||||||
if (dopt->dataOnly)
|
if (dopt->dataOnly)
|
||||||
|
@ -4316,19 +4281,8 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
|
||||||
|
|
||||||
query = createPQExpBuffer();
|
query = createPQExpBuffer();
|
||||||
|
|
||||||
if (tbinfo->relkind == RELKIND_SEQUENCE)
|
appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
|
||||||
{
|
fmtId(pubinfo->dobj.name));
|
||||||
appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD SEQUENCE",
|
|
||||||
fmtId(pubinfo->dobj.name));
|
|
||||||
description = "PUBLICATION SEQUENCE";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
|
|
||||||
fmtId(pubinfo->dobj.name));
|
|
||||||
description = "PUBLICATION TABLE";
|
|
||||||
}
|
|
||||||
|
|
||||||
appendPQExpBuffer(query, " %s",
|
appendPQExpBuffer(query, " %s",
|
||||||
fmtQualifiedDumpable(tbinfo));
|
fmtQualifiedDumpable(tbinfo));
|
||||||
|
|
||||||
|
@ -4357,7 +4311,7 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
|
||||||
ARCHIVE_OPTS(.tag = tag,
|
ARCHIVE_OPTS(.tag = tag,
|
||||||
.namespace = tbinfo->dobj.namespace->dobj.name,
|
.namespace = tbinfo->dobj.namespace->dobj.name,
|
||||||
.owner = pubinfo->rolname,
|
.owner = pubinfo->rolname,
|
||||||
.description = description,
|
.description = "PUBLICATION TABLE",
|
||||||
.section = SECTION_POST_DATA,
|
.section = SECTION_POST_DATA,
|
||||||
.createStmt = query->data));
|
.createStmt = query->data));
|
||||||
|
|
||||||
|
|
|
@ -615,12 +615,10 @@ typedef struct _PublicationInfo
|
||||||
DumpableObject dobj;
|
DumpableObject dobj;
|
||||||
const char *rolname;
|
const char *rolname;
|
||||||
bool puballtables;
|
bool puballtables;
|
||||||
bool puballsequences;
|
|
||||||
bool pubinsert;
|
bool pubinsert;
|
||||||
bool pubupdate;
|
bool pubupdate;
|
||||||
bool pubdelete;
|
bool pubdelete;
|
||||||
bool pubtruncate;
|
bool pubtruncate;
|
||||||
bool pubsequence;
|
|
||||||
bool pubviaroot;
|
bool pubviaroot;
|
||||||
} PublicationInfo;
|
} PublicationInfo;
|
||||||
|
|
||||||
|
@ -646,7 +644,6 @@ typedef struct _PublicationSchemaInfo
|
||||||
DumpableObject dobj;
|
DumpableObject dobj;
|
||||||
PublicationInfo *publication;
|
PublicationInfo *publication;
|
||||||
NamespaceInfo *pubschema;
|
NamespaceInfo *pubschema;
|
||||||
char pubtype;
|
|
||||||
} PublicationSchemaInfo;
|
} PublicationSchemaInfo;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -2420,7 +2420,7 @@ my %tests = (
|
||||||
create_order => 50,
|
create_order => 50,
|
||||||
create_sql => 'CREATE PUBLICATION pub1;',
|
create_sql => 'CREATE PUBLICATION pub1;',
|
||||||
regexp => qr/^
|
regexp => qr/^
|
||||||
\QCREATE PUBLICATION pub1 WITH (publish = 'insert, update, delete, truncate, sequence');\E
|
\QCREATE PUBLICATION pub1 WITH (publish = 'insert, update, delete, truncate');\E
|
||||||
/xm,
|
/xm,
|
||||||
like => { %full_runs, section_post_data => 1, },
|
like => { %full_runs, section_post_data => 1, },
|
||||||
},
|
},
|
||||||
|
@ -2440,27 +2440,16 @@ my %tests = (
|
||||||
create_order => 50,
|
create_order => 50,
|
||||||
create_sql => 'CREATE PUBLICATION pub3;',
|
create_sql => 'CREATE PUBLICATION pub3;',
|
||||||
regexp => qr/^
|
regexp => qr/^
|
||||||
\QCREATE PUBLICATION pub3 WITH (publish = 'insert, update, delete, truncate, sequence');\E
|
\QCREATE PUBLICATION pub3 WITH (publish = 'insert, update, delete, truncate');\E
|
||||||
/xm,
|
/xm,
|
||||||
like => { %full_runs, section_post_data => 1, },
|
like => { %full_runs, section_post_data => 1, },
|
||||||
},
|
},
|
||||||
|
|
||||||
'CREATE PUBLICATION pub4' => {
|
'CREATE PUBLICATION pub4' => {
|
||||||
create_order => 50,
|
create_order => 50,
|
||||||
create_sql => 'CREATE PUBLICATION pub4
|
create_sql => 'CREATE PUBLICATION pub4;',
|
||||||
FOR ALL SEQUENCES
|
|
||||||
WITH (publish = \'\');',
|
|
||||||
regexp => qr/^
|
regexp => qr/^
|
||||||
\QCREATE PUBLICATION pub4 FOR ALL SEQUENCES WITH (publish = '');\E
|
\QCREATE PUBLICATION pub4 WITH (publish = 'insert, update, delete, truncate');\E
|
||||||
/xm,
|
|
||||||
like => { %full_runs, section_post_data => 1, },
|
|
||||||
},
|
|
||||||
|
|
||||||
'CREATE PUBLICATION pub5' => {
|
|
||||||
create_order => 50,
|
|
||||||
create_sql => 'CREATE PUBLICATION pub5;',
|
|
||||||
regexp => qr/^
|
|
||||||
\QCREATE PUBLICATION pub5 WITH (publish = 'insert, update, delete, truncate, sequence');\E
|
|
||||||
/xm,
|
/xm,
|
||||||
like => { %full_runs, section_post_data => 1, },
|
like => { %full_runs, section_post_data => 1, },
|
||||||
},
|
},
|
||||||
|
@ -2569,27 +2558,6 @@ my %tests = (
|
||||||
unlike => { exclude_dump_test_schema => 1, },
|
unlike => { exclude_dump_test_schema => 1, },
|
||||||
},
|
},
|
||||||
|
|
||||||
'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA dump_test' => {
|
|
||||||
create_order => 51,
|
|
||||||
create_sql =>
|
|
||||||
'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA dump_test;',
|
|
||||||
regexp => qr/^
|
|
||||||
\QALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA dump_test;\E
|
|
||||||
/xm,
|
|
||||||
like => { %full_runs, section_post_data => 1, },
|
|
||||||
unlike => { exclude_dump_test_schema => 1, },
|
|
||||||
},
|
|
||||||
|
|
||||||
'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA public' => {
|
|
||||||
create_order => 52,
|
|
||||||
create_sql =>
|
|
||||||
'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA public;',
|
|
||||||
regexp => qr/^
|
|
||||||
\QALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA public;\E
|
|
||||||
/xm,
|
|
||||||
like => { %full_runs, section_post_data => 1, },
|
|
||||||
},
|
|
||||||
|
|
||||||
'CREATE SCHEMA public' => {
|
'CREATE SCHEMA public' => {
|
||||||
regexp => qr/^CREATE SCHEMA public;/m,
|
regexp => qr/^CREATE SCHEMA public;/m,
|
||||||
|
|
||||||
|
|
|
@ -1633,19 +1633,28 @@ describeOneTableDetails(const char *schemaname,
|
||||||
if (tableinfo.relkind == RELKIND_SEQUENCE)
|
if (tableinfo.relkind == RELKIND_SEQUENCE)
|
||||||
{
|
{
|
||||||
PGresult *result = NULL;
|
PGresult *result = NULL;
|
||||||
|
printQueryOpt myopt = pset.popt;
|
||||||
|
char *footers[2] = {NULL, NULL};
|
||||||
|
|
||||||
if (pset.sversion >= 100000)
|
if (pset.sversion >= 100000)
|
||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT pg_catalog.format_type(seqtypid, NULL),\n"
|
"SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
|
||||||
" seqstart,\n"
|
" seqstart AS \"%s\",\n"
|
||||||
" seqmin,\n"
|
" seqmin AS \"%s\",\n"
|
||||||
" seqmax,\n"
|
" seqmax AS \"%s\",\n"
|
||||||
" seqincrement,\n"
|
" seqincrement AS \"%s\",\n"
|
||||||
" CASE WHEN seqcycle THEN '%s' ELSE '%s' END,\n"
|
" CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
|
||||||
" seqcache\n",
|
" seqcache AS \"%s\"\n",
|
||||||
|
gettext_noop("Type"),
|
||||||
|
gettext_noop("Start"),
|
||||||
|
gettext_noop("Minimum"),
|
||||||
|
gettext_noop("Maximum"),
|
||||||
|
gettext_noop("Increment"),
|
||||||
gettext_noop("yes"),
|
gettext_noop("yes"),
|
||||||
gettext_noop("no"));
|
gettext_noop("no"),
|
||||||
|
gettext_noop("Cycles?"),
|
||||||
|
gettext_noop("Cache"));
|
||||||
appendPQExpBuffer(&buf,
|
appendPQExpBuffer(&buf,
|
||||||
"FROM pg_catalog.pg_sequence\n"
|
"FROM pg_catalog.pg_sequence\n"
|
||||||
"WHERE seqrelid = '%s';",
|
"WHERE seqrelid = '%s';",
|
||||||
|
@ -1654,15 +1663,22 @@ describeOneTableDetails(const char *schemaname,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT 'bigint',\n"
|
"SELECT 'bigint' AS \"%s\",\n"
|
||||||
" start_value,\n"
|
" start_value AS \"%s\",\n"
|
||||||
" min_value,\n"
|
" min_value AS \"%s\",\n"
|
||||||
" max_value,\n"
|
" max_value AS \"%s\",\n"
|
||||||
" increment_by,\n"
|
" increment_by AS \"%s\",\n"
|
||||||
" CASE WHEN is_cycled THEN '%s' ELSE '%s' END,\n"
|
" CASE WHEN is_cycled THEN '%s' ELSE '%s' END AS \"%s\",\n"
|
||||||
" cache_value\n",
|
" cache_value AS \"%s\"\n",
|
||||||
|
gettext_noop("Type"),
|
||||||
|
gettext_noop("Start"),
|
||||||
|
gettext_noop("Minimum"),
|
||||||
|
gettext_noop("Maximum"),
|
||||||
|
gettext_noop("Increment"),
|
||||||
gettext_noop("yes"),
|
gettext_noop("yes"),
|
||||||
gettext_noop("no"));
|
gettext_noop("no"),
|
||||||
|
gettext_noop("Cycles?"),
|
||||||
|
gettext_noop("Cache"));
|
||||||
appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname));
|
appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname));
|
||||||
/* must be separate because fmtId isn't reentrant */
|
/* must be separate because fmtId isn't reentrant */
|
||||||
appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
|
appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
|
||||||
|
@ -1672,57 +1688,6 @@ describeOneTableDetails(const char *schemaname,
|
||||||
if (!res)
|
if (!res)
|
||||||
goto error_return;
|
goto error_return;
|
||||||
|
|
||||||
numrows = PQntuples(res);
|
|
||||||
|
|
||||||
/* XXX reset to use expanded output for sequences (maybe we should
|
|
||||||
* keep this disabled, just like for tables?) */
|
|
||||||
myopt.expanded = pset.popt.topt.expanded;
|
|
||||||
|
|
||||||
printTableInit(&cont, &myopt, title.data, 7, numrows);
|
|
||||||
printTableInitialized = true;
|
|
||||||
|
|
||||||
if (tableinfo.relpersistence == 'u')
|
|
||||||
printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
|
|
||||||
schemaname, relationname);
|
|
||||||
else
|
|
||||||
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
|
|
||||||
schemaname, relationname);
|
|
||||||
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Type"), true, 'l');
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Start"), true, 'r');
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Minimum"), true, 'r');
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Maximum"), true, 'r');
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Increment"), true, 'r');
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Cycles?"), true, 'l');
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Cache"), true, 'r');
|
|
||||||
|
|
||||||
/* Generate table cells to be printed */
|
|
||||||
for (i = 0; i < numrows; i++)
|
|
||||||
{
|
|
||||||
/* Type */
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
|
|
||||||
|
|
||||||
/* Start */
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false);
|
|
||||||
|
|
||||||
/* Minimum */
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
|
|
||||||
|
|
||||||
/* Maximum */
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
|
|
||||||
|
|
||||||
/* Increment */
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
|
|
||||||
|
|
||||||
/* Cycles? */
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
|
|
||||||
|
|
||||||
/* Cache */
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footer information about a sequence */
|
|
||||||
|
|
||||||
/* Get the column that owns this sequence */
|
/* Get the column that owns this sequence */
|
||||||
printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
|
printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
|
||||||
"\n pg_catalog.quote_ident(relname) || '.' ||"
|
"\n pg_catalog.quote_ident(relname) || '.' ||"
|
||||||
|
@ -1754,63 +1719,33 @@ describeOneTableDetails(const char *schemaname,
|
||||||
switch (PQgetvalue(result, 0, 1)[0])
|
switch (PQgetvalue(result, 0, 1)[0])
|
||||||
{
|
{
|
||||||
case 'a':
|
case 'a':
|
||||||
printTableAddFooter(&cont,
|
footers[0] = psprintf(_("Owned by: %s"),
|
||||||
psprintf(_("Owned by: %s"),
|
PQgetvalue(result, 0, 0));
|
||||||
PQgetvalue(result, 0, 0)));
|
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
printTableAddFooter(&cont,
|
footers[0] = psprintf(_("Sequence for identity column: %s"),
|
||||||
psprintf(_("Sequence for identity column: %s"),
|
PQgetvalue(result, 0, 0));
|
||||||
PQgetvalue(result, 0, 0)));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PQclear(result);
|
PQclear(result);
|
||||||
|
|
||||||
/* print any publications */
|
if (tableinfo.relpersistence == 'u')
|
||||||
if (pset.sversion >= 150000)
|
printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
|
||||||
{
|
schemaname, relationname);
|
||||||
int tuples = 0;
|
else
|
||||||
|
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
|
||||||
|
schemaname, relationname);
|
||||||
|
|
||||||
printfPQExpBuffer(&buf,
|
myopt.footers = footers;
|
||||||
"SELECT pubname\n"
|
myopt.topt.default_footer = false;
|
||||||
"FROM pg_catalog.pg_publication p\n"
|
myopt.title = title.data;
|
||||||
" JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
|
myopt.translate_header = true;
|
||||||
" JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
|
|
||||||
"WHERE pc.oid ='%s' and pn.pntype = 's' and pg_catalog.pg_relation_is_publishable('%s')\n"
|
|
||||||
"UNION\n"
|
|
||||||
"SELECT pubname\n"
|
|
||||||
"FROM pg_catalog.pg_publication p\n"
|
|
||||||
" JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
|
|
||||||
"WHERE pr.prrelid = '%s'\n"
|
|
||||||
"UNION\n"
|
|
||||||
"SELECT pubname\n"
|
|
||||||
"FROM pg_catalog.pg_publication p\n"
|
|
||||||
"WHERE p.puballsequences AND pg_catalog.pg_relation_is_publishable('%s')\n"
|
|
||||||
"ORDER BY 1;",
|
|
||||||
oid, oid, oid, oid);
|
|
||||||
|
|
||||||
result = PSQLexec(buf.data);
|
printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
|
||||||
if (!result)
|
|
||||||
goto error_return;
|
|
||||||
else
|
|
||||||
tuples = PQntuples(result);
|
|
||||||
|
|
||||||
if (tuples > 0)
|
if (footers[0])
|
||||||
printTableAddFooter(&cont, _("Publications:"));
|
free(footers[0]);
|
||||||
|
|
||||||
/* Might be an empty set - that's ok */
|
|
||||||
for (i = 0; i < tuples; i++)
|
|
||||||
{
|
|
||||||
printfPQExpBuffer(&buf, " \"%s\"",
|
|
||||||
PQgetvalue(result, i, 0));
|
|
||||||
|
|
||||||
printTableAddFooter(&cont, buf.data);
|
|
||||||
}
|
|
||||||
PQclear(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
printTable(&cont, pset.queryFout, false, pset.logfile);
|
|
||||||
|
|
||||||
retval = true;
|
retval = true;
|
||||||
goto error_return; /* not an error, just return early */
|
goto error_return; /* not an error, just return early */
|
||||||
|
@ -2037,11 +1972,6 @@ describeOneTableDetails(const char *schemaname,
|
||||||
for (i = 0; i < cols; i++)
|
for (i = 0; i < cols; i++)
|
||||||
printTableAddHeader(&cont, headers[i], true, 'l');
|
printTableAddHeader(&cont, headers[i], true, 'l');
|
||||||
|
|
||||||
res = PSQLexec(buf.data);
|
|
||||||
if (!res)
|
|
||||||
goto error_return;
|
|
||||||
numrows = PQntuples(res);
|
|
||||||
|
|
||||||
/* Generate table cells to be printed */
|
/* Generate table cells to be printed */
|
||||||
for (i = 0; i < numrows; i++)
|
for (i = 0; i < numrows; i++)
|
||||||
{
|
{
|
||||||
|
@ -2968,7 +2898,7 @@ describeOneTableDetails(const char *schemaname,
|
||||||
"FROM pg_catalog.pg_publication p\n"
|
"FROM pg_catalog.pg_publication p\n"
|
||||||
" JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
|
" JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
|
||||||
" JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
|
" JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
|
||||||
"WHERE pc.oid ='%s' and pn.pntype = 't' and pg_catalog.pg_relation_is_publishable('%s')\n"
|
"WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
|
||||||
"UNION\n"
|
"UNION\n"
|
||||||
"SELECT pubname\n"
|
"SELECT pubname\n"
|
||||||
" , pg_get_expr(pr.prqual, c.oid)\n"
|
" , pg_get_expr(pr.prqual, c.oid)\n"
|
||||||
|
@ -4872,7 +4802,7 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT pubname, (CASE WHEN pntype = 't' THEN 'tables' ELSE 'sequences' END) AS pubtype\n"
|
"SELECT pubname \n"
|
||||||
"FROM pg_catalog.pg_publication p\n"
|
"FROM pg_catalog.pg_publication p\n"
|
||||||
" JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
|
" JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
|
||||||
" JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n"
|
" JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n"
|
||||||
|
@ -4901,9 +4831,8 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
|
||||||
/* Might be an empty set - that's ok */
|
/* Might be an empty set - that's ok */
|
||||||
for (i = 0; i < pub_schema_tuples; i++)
|
for (i = 0; i < pub_schema_tuples; i++)
|
||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf, " \"%s\" (%s)",
|
printfPQExpBuffer(&buf, " \"%s\"",
|
||||||
PQgetvalue(result, i, 0),
|
PQgetvalue(result, i, 0));
|
||||||
PQgetvalue(result, i, 1));
|
|
||||||
|
|
||||||
footers[i + 1] = pg_strdup(buf.data);
|
footers[i + 1] = pg_strdup(buf.data);
|
||||||
}
|
}
|
||||||
|
@ -5908,7 +5837,7 @@ listPublications(const char *pattern)
|
||||||
PQExpBufferData buf;
|
PQExpBufferData buf;
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
printQueryOpt myopt = pset.popt;
|
printQueryOpt myopt = pset.popt;
|
||||||
static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
|
static const bool translate_columns[] = {false, false, false, false, false, false, false, false};
|
||||||
|
|
||||||
if (pset.sversion < 100000)
|
if (pset.sversion < 100000)
|
||||||
{
|
{
|
||||||
|
@ -5922,45 +5851,23 @@ listPublications(const char *pattern)
|
||||||
|
|
||||||
initPQExpBuffer(&buf);
|
initPQExpBuffer(&buf);
|
||||||
|
|
||||||
if (pset.sversion >= 150000)
|
printfPQExpBuffer(&buf,
|
||||||
printfPQExpBuffer(&buf,
|
"SELECT pubname AS \"%s\",\n"
|
||||||
"SELECT pubname AS \"%s\",\n"
|
" pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
|
||||||
" pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
|
" puballtables AS \"%s\",\n"
|
||||||
" puballtables AS \"%s\",\n"
|
" pubinsert AS \"%s\",\n"
|
||||||
" puballsequences AS \"%s\",\n"
|
" pubupdate AS \"%s\",\n"
|
||||||
" pubinsert AS \"%s\",\n"
|
" pubdelete AS \"%s\"",
|
||||||
" pubupdate AS \"%s\",\n"
|
gettext_noop("Name"),
|
||||||
" pubdelete AS \"%s\"",
|
gettext_noop("Owner"),
|
||||||
gettext_noop("Name"),
|
gettext_noop("All tables"),
|
||||||
gettext_noop("Owner"),
|
gettext_noop("Inserts"),
|
||||||
gettext_noop("All tables"),
|
gettext_noop("Updates"),
|
||||||
gettext_noop("All sequences"),
|
gettext_noop("Deletes"));
|
||||||
gettext_noop("Inserts"),
|
|
||||||
gettext_noop("Updates"),
|
|
||||||
gettext_noop("Deletes"));
|
|
||||||
else
|
|
||||||
printfPQExpBuffer(&buf,
|
|
||||||
"SELECT pubname AS \"%s\",\n"
|
|
||||||
" pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
|
|
||||||
" puballtables AS \"%s\",\n"
|
|
||||||
" pubinsert AS \"%s\",\n"
|
|
||||||
" pubupdate AS \"%s\",\n"
|
|
||||||
" pubdelete AS \"%s\"",
|
|
||||||
gettext_noop("Name"),
|
|
||||||
gettext_noop("Owner"),
|
|
||||||
gettext_noop("All tables"),
|
|
||||||
gettext_noop("Inserts"),
|
|
||||||
gettext_noop("Updates"),
|
|
||||||
gettext_noop("Deletes"));
|
|
||||||
|
|
||||||
if (pset.sversion >= 110000)
|
if (pset.sversion >= 110000)
|
||||||
appendPQExpBuffer(&buf,
|
appendPQExpBuffer(&buf,
|
||||||
",\n pubtruncate AS \"%s\"",
|
",\n pubtruncate AS \"%s\"",
|
||||||
gettext_noop("Truncates"));
|
gettext_noop("Truncates"));
|
||||||
if (pset.sversion >= 150000)
|
|
||||||
appendPQExpBuffer(&buf,
|
|
||||||
",\n pubsequence AS \"%s\"",
|
|
||||||
gettext_noop("Sequences"));
|
|
||||||
if (pset.sversion >= 130000)
|
if (pset.sversion >= 130000)
|
||||||
appendPQExpBuffer(&buf,
|
appendPQExpBuffer(&buf,
|
||||||
",\n pubviaroot AS \"%s\"",
|
",\n pubviaroot AS \"%s\"",
|
||||||
|
@ -6050,7 +5957,6 @@ describePublications(const char *pattern)
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
bool has_pubtruncate;
|
bool has_pubtruncate;
|
||||||
bool has_pubviaroot;
|
bool has_pubviaroot;
|
||||||
bool has_pubsequence;
|
|
||||||
|
|
||||||
PQExpBufferData title;
|
PQExpBufferData title;
|
||||||
printTableContent cont;
|
printTableContent cont;
|
||||||
|
@ -6067,7 +5973,6 @@ describePublications(const char *pattern)
|
||||||
|
|
||||||
has_pubtruncate = (pset.sversion >= 110000);
|
has_pubtruncate = (pset.sversion >= 110000);
|
||||||
has_pubviaroot = (pset.sversion >= 130000);
|
has_pubviaroot = (pset.sversion >= 130000);
|
||||||
has_pubsequence = (pset.sversion >= 150000);
|
|
||||||
|
|
||||||
initPQExpBuffer(&buf);
|
initPQExpBuffer(&buf);
|
||||||
|
|
||||||
|
@ -6075,17 +5980,12 @@ describePublications(const char *pattern)
|
||||||
"SELECT oid, pubname,\n"
|
"SELECT oid, pubname,\n"
|
||||||
" pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
|
" pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
|
||||||
" puballtables, pubinsert, pubupdate, pubdelete");
|
" puballtables, pubinsert, pubupdate, pubdelete");
|
||||||
|
|
||||||
if (has_pubtruncate)
|
if (has_pubtruncate)
|
||||||
appendPQExpBufferStr(&buf,
|
appendPQExpBufferStr(&buf,
|
||||||
", pubtruncate");
|
", pubtruncate");
|
||||||
if (has_pubviaroot)
|
if (has_pubviaroot)
|
||||||
appendPQExpBufferStr(&buf,
|
appendPQExpBufferStr(&buf,
|
||||||
", pubviaroot");
|
", pubviaroot");
|
||||||
if (has_pubsequence)
|
|
||||||
appendPQExpBufferStr(&buf,
|
|
||||||
", puballsequences, pubsequence");
|
|
||||||
|
|
||||||
appendPQExpBufferStr(&buf,
|
appendPQExpBufferStr(&buf,
|
||||||
"\nFROM pg_catalog.pg_publication\n");
|
"\nFROM pg_catalog.pg_publication\n");
|
||||||
|
|
||||||
|
@ -6126,7 +6026,6 @@ describePublications(const char *pattern)
|
||||||
char *pubid = PQgetvalue(res, i, 0);
|
char *pubid = PQgetvalue(res, i, 0);
|
||||||
char *pubname = PQgetvalue(res, i, 1);
|
char *pubname = PQgetvalue(res, i, 1);
|
||||||
bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
|
bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
|
||||||
bool puballsequences = strcmp(PQgetvalue(res, i, 9), "t") == 0;
|
|
||||||
printTableOpt myopt = pset.popt.topt;
|
printTableOpt myopt = pset.popt.topt;
|
||||||
|
|
||||||
if (has_pubtruncate)
|
if (has_pubtruncate)
|
||||||
|
@ -6134,43 +6033,29 @@ describePublications(const char *pattern)
|
||||||
if (has_pubviaroot)
|
if (has_pubviaroot)
|
||||||
ncols++;
|
ncols++;
|
||||||
|
|
||||||
/* sequences have two extra columns (puballsequences, pubsequences) */
|
|
||||||
if (has_pubsequence)
|
|
||||||
ncols += 2;
|
|
||||||
|
|
||||||
initPQExpBuffer(&title);
|
initPQExpBuffer(&title);
|
||||||
printfPQExpBuffer(&title, _("Publication %s"), pubname);
|
printfPQExpBuffer(&title, _("Publication %s"), pubname);
|
||||||
printTableInit(&cont, &myopt, title.data, ncols, nrows);
|
printTableInit(&cont, &myopt, title.data, ncols, nrows);
|
||||||
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
|
printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
|
||||||
printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
|
printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
|
||||||
if (has_pubsequence)
|
|
||||||
printTableAddHeader(&cont, gettext_noop("All sequences"), true, align);
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
|
printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
|
||||||
printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
|
printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
|
||||||
printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
|
printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
|
||||||
if (has_pubtruncate)
|
if (has_pubtruncate)
|
||||||
printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
|
printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
|
||||||
if (has_pubsequence)
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Sequences"), true, align);
|
|
||||||
if (has_pubviaroot)
|
if (has_pubviaroot)
|
||||||
printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
|
printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
|
||||||
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false); /* owner */
|
printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false); /* all tables */
|
printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
|
||||||
|
printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
|
||||||
if (has_pubsequence)
|
printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false); /* all sequences */
|
printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
|
||||||
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false); /* insert */
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false); /* update */
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false); /* delete */
|
|
||||||
if (has_pubtruncate)
|
if (has_pubtruncate)
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false); /* truncate */
|
printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
|
||||||
if (has_pubsequence)
|
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false); /* sequence */
|
|
||||||
if (has_pubviaroot)
|
if (has_pubviaroot)
|
||||||
printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false); /* via root */
|
printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
|
||||||
|
|
||||||
if (!puballtables)
|
if (!puballtables)
|
||||||
{
|
{
|
||||||
|
@ -6201,7 +6086,6 @@ describePublications(const char *pattern)
|
||||||
"WHERE c.relnamespace = n.oid\n"
|
"WHERE c.relnamespace = n.oid\n"
|
||||||
" AND c.oid = pr.prrelid\n"
|
" AND c.oid = pr.prrelid\n"
|
||||||
" AND pr.prpubid = '%s'\n"
|
" AND pr.prpubid = '%s'\n"
|
||||||
" AND c.relkind != 'S'\n" /* exclude sequences */
|
|
||||||
"ORDER BY 1,2", pubid);
|
"ORDER BY 1,2", pubid);
|
||||||
if (!addFooterToPublicationDesc(&buf, "Tables:", false, &cont))
|
if (!addFooterToPublicationDesc(&buf, "Tables:", false, &cont))
|
||||||
goto error_return;
|
goto error_return;
|
||||||
|
@ -6213,7 +6097,7 @@ describePublications(const char *pattern)
|
||||||
"SELECT n.nspname\n"
|
"SELECT n.nspname\n"
|
||||||
"FROM pg_catalog.pg_namespace n\n"
|
"FROM pg_catalog.pg_namespace n\n"
|
||||||
" JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
|
" JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
|
||||||
"WHERE pn.pnpubid = '%s' AND pn.pntype = 't'\n"
|
"WHERE pn.pnpubid = '%s'\n"
|
||||||
"ORDER BY 1", pubid);
|
"ORDER BY 1", pubid);
|
||||||
if (!addFooterToPublicationDesc(&buf, "Tables from schemas:",
|
if (!addFooterToPublicationDesc(&buf, "Tables from schemas:",
|
||||||
true, &cont))
|
true, &cont))
|
||||||
|
@ -6221,37 +6105,6 @@ describePublications(const char *pattern)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!puballsequences)
|
|
||||||
{
|
|
||||||
/* Get the sequences for the specified publication */
|
|
||||||
printfPQExpBuffer(&buf,
|
|
||||||
"SELECT n.nspname, c.relname, NULL, NULL\n"
|
|
||||||
"FROM pg_catalog.pg_class c,\n"
|
|
||||||
" pg_catalog.pg_namespace n,\n"
|
|
||||||
" pg_catalog.pg_publication_rel pr\n"
|
|
||||||
"WHERE c.relnamespace = n.oid\n"
|
|
||||||
" AND c.oid = pr.prrelid\n"
|
|
||||||
" AND pr.prpubid = '%s'\n"
|
|
||||||
" AND c.relkind = 'S'\n" /* only sequences */
|
|
||||||
"ORDER BY 1,2", pubid);
|
|
||||||
if (!addFooterToPublicationDesc(&buf, "Sequences:", false, &cont))
|
|
||||||
goto error_return;
|
|
||||||
|
|
||||||
if (pset.sversion >= 150000)
|
|
||||||
{
|
|
||||||
/* Get the schemas for the specified publication */
|
|
||||||
printfPQExpBuffer(&buf,
|
|
||||||
"SELECT n.nspname\n"
|
|
||||||
"FROM pg_catalog.pg_namespace n\n"
|
|
||||||
" JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
|
|
||||||
"WHERE pn.pnpubid = '%s' AND pn.pntype = 's'\n"
|
|
||||||
"ORDER BY 1", pubid);
|
|
||||||
if (!addFooterToPublicationDesc(&buf, "Sequences from schemas:",
|
|
||||||
true, &cont))
|
|
||||||
goto error_return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printTable(&cont, pset.queryFout, false, pset.logfile);
|
printTable(&cont, pset.queryFout, false, pset.logfile);
|
||||||
printTableCleanup(&cont);
|
printTableCleanup(&cont);
|
||||||
|
|
||||||
|
|
|
@ -1827,15 +1827,11 @@ psql_completion(const char *text, int start, int end)
|
||||||
COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "SET");
|
COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "SET");
|
||||||
/* ALTER PUBLICATION <name> ADD */
|
/* ALTER PUBLICATION <name> ADD */
|
||||||
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD"))
|
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD"))
|
||||||
COMPLETE_WITH("ALL TABLES IN SCHEMA", "ALL SEQUENCES IN SCHEMA", "TABLE", "SEQUENCE");
|
COMPLETE_WITH("ALL TABLES IN SCHEMA", "TABLE");
|
||||||
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") ||
|
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") ||
|
||||||
(HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
|
(HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
|
||||||
ends_with(prev_wd, ',')))
|
ends_with(prev_wd, ',')))
|
||||||
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
|
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
|
||||||
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "SEQUENCE") ||
|
|
||||||
(HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "SEQUENCE") &&
|
|
||||||
ends_with(prev_wd, ',')))
|
|
||||||
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
|
|
||||||
/*
|
/*
|
||||||
* "ALTER PUBLICATION <name> SET TABLE <name> WHERE (" - complete with
|
* "ALTER PUBLICATION <name> SET TABLE <name> WHERE (" - complete with
|
||||||
* table attributes
|
* table attributes
|
||||||
|
@ -1854,11 +1850,11 @@ psql_completion(const char *text, int start, int end)
|
||||||
COMPLETE_WITH(",");
|
COMPLETE_WITH(",");
|
||||||
/* ALTER PUBLICATION <name> DROP */
|
/* ALTER PUBLICATION <name> DROP */
|
||||||
else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP"))
|
else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP"))
|
||||||
COMPLETE_WITH("ALL TABLES IN SCHEMA", "ALL SEQUENCES IN SCHEMA", "TABLE", "SEQUENCE");
|
COMPLETE_WITH("ALL TABLES IN SCHEMA", "TABLE");
|
||||||
/* ALTER PUBLICATION <name> SET */
|
/* ALTER PUBLICATION <name> SET */
|
||||||
else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET"))
|
else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET"))
|
||||||
COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "ALL SEQUENCES IN SCHEMA", "TABLE", "SEQUENCE");
|
COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "TABLE");
|
||||||
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES|SEQUENCES", "IN", "SCHEMA"))
|
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES", "IN", "SCHEMA"))
|
||||||
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
|
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
|
||||||
" AND nspname NOT LIKE E'pg\\\\_%%'",
|
" AND nspname NOT LIKE E'pg\\\\_%%'",
|
||||||
"CURRENT_SCHEMA");
|
"CURRENT_SCHEMA");
|
||||||
|
@ -2988,27 +2984,21 @@ psql_completion(const char *text, int start, int end)
|
||||||
|
|
||||||
/* CREATE PUBLICATION */
|
/* CREATE PUBLICATION */
|
||||||
else if (Matches("CREATE", "PUBLICATION", MatchAny))
|
else if (Matches("CREATE", "PUBLICATION", MatchAny))
|
||||||
COMPLETE_WITH("FOR TABLE", "FOR ALL TABLES", "FOR ALL TABLES IN SCHEMA",
|
COMPLETE_WITH("FOR TABLE", "FOR ALL TABLES", "FOR ALL TABLES IN SCHEMA", "WITH (");
|
||||||
"FOR SEQUENCE", "FOR ALL SEQUENCES", "FOR ALL SEQUENCES IN SCHEMA",
|
|
||||||
"WITH (");
|
|
||||||
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR"))
|
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR"))
|
||||||
COMPLETE_WITH("TABLE", "ALL TABLES", "ALL TABLES IN SCHEMA",
|
COMPLETE_WITH("TABLE", "ALL TABLES", "ALL TABLES IN SCHEMA");
|
||||||
"SEQUENCE", "ALL SEQUENCES", "ALL SEQUENCES IN SCHEMA");
|
|
||||||
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL"))
|
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL"))
|
||||||
COMPLETE_WITH("TABLES", "TABLES IN SCHEMA", "SEQUENCES", "SEQUENCES IN SCHEMA");
|
COMPLETE_WITH("TABLES", "TABLES IN SCHEMA");
|
||||||
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES|SEQUENCES"))
|
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES"))
|
||||||
COMPLETE_WITH("IN SCHEMA", "WITH (");
|
COMPLETE_WITH("IN SCHEMA", "WITH (");
|
||||||
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE|SEQUENCE", MatchAny) && !ends_with(prev_wd, ','))
|
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE", MatchAny) && !ends_with(prev_wd, ','))
|
||||||
COMPLETE_WITH("WHERE (", "WITH (");
|
COMPLETE_WITH("WHERE (", "WITH (");
|
||||||
/* Complete "CREATE PUBLICATION <name> FOR TABLE" with "<table>, ..." */
|
/* Complete "CREATE PUBLICATION <name> FOR TABLE" with "<table>, ..." */
|
||||||
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE"))
|
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE"))
|
||||||
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
|
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
|
||||||
/* Complete "CREATE PUBLICATION <name> FOR SEQUENCE" with "<sequence>, ..." */
|
|
||||||
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "SEQUENCE"))
|
|
||||||
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "CREATE PUBLICATION <name> FOR TABLE|SEQUENCE <name> WHERE (" - complete with
|
* "CREATE PUBLICATION <name> FOR TABLE <name> WHERE (" - complete with
|
||||||
* table attributes
|
* table attributes
|
||||||
*/
|
*/
|
||||||
else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE"))
|
else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE"))
|
||||||
|
@ -3019,14 +3009,14 @@ psql_completion(const char *text, int start, int end)
|
||||||
COMPLETE_WITH(" WITH (");
|
COMPLETE_WITH(" WITH (");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Complete "CREATE PUBLICATION <name> FOR ALL TABLES|SEQUENCES IN SCHEMA <schema>,
|
* Complete "CREATE PUBLICATION <name> FOR ALL TABLES IN SCHEMA <schema>,
|
||||||
* ..."
|
* ..."
|
||||||
*/
|
*/
|
||||||
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES|SEQUENCES", "IN", "SCHEMA"))
|
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "IN", "SCHEMA"))
|
||||||
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
|
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
|
||||||
" AND nspname NOT LIKE E'pg\\\\_%%'",
|
" AND nspname NOT LIKE E'pg\\\\_%%'",
|
||||||
"CURRENT_SCHEMA");
|
"CURRENT_SCHEMA");
|
||||||
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES|SEQUENCES", "IN", "SCHEMA", MatchAny) && (!ends_with(prev_wd, ',')))
|
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "IN", "SCHEMA", MatchAny) && (!ends_with(prev_wd, ',')))
|
||||||
COMPLETE_WITH("WITH (");
|
COMPLETE_WITH("WITH (");
|
||||||
/* Complete "CREATE PUBLICATION <name> [...] WITH" */
|
/* Complete "CREATE PUBLICATION <name> [...] WITH" */
|
||||||
else if (HeadMatches("CREATE", "PUBLICATION") && TailMatches("WITH", "("))
|
else if (HeadMatches("CREATE", "PUBLICATION") && TailMatches("WITH", "("))
|
||||||
|
|
|
@ -40,7 +40,7 @@ PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, btree_xlog
|
||||||
PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL)
|
PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL)
|
||||||
PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL)
|
PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL)
|
||||||
PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL)
|
PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL)
|
||||||
PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, sequence_decode)
|
PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, NULL)
|
||||||
PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL)
|
PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL)
|
||||||
PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL)
|
PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL)
|
||||||
PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL)
|
PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL)
|
||||||
|
|
|
@ -53,6 +53,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202204074
|
#define CATALOG_VERSION_NO 202204075
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -11658,11 +11658,6 @@
|
||||||
provolatile => 's', prorettype => 'oid', proargtypes => 'text',
|
provolatile => 's', prorettype => 'oid', proargtypes => 'text',
|
||||||
proallargtypes => '{text,oid}', proargmodes => '{i,o}',
|
proallargtypes => '{text,oid}', proargmodes => '{i,o}',
|
||||||
proargnames => '{pubname,relid}', prosrc => 'pg_get_publication_tables' },
|
proargnames => '{pubname,relid}', prosrc => 'pg_get_publication_tables' },
|
||||||
{ oid => '8000', descr => 'get OIDs of sequences in a publication',
|
|
||||||
proname => 'pg_get_publication_sequences', prorows => '1000', proretset => 't',
|
|
||||||
provolatile => 's', prorettype => 'oid', proargtypes => 'text',
|
|
||||||
proallargtypes => '{text,oid}', proargmodes => '{i,o}',
|
|
||||||
proargnames => '{pubname,relid}', prosrc => 'pg_get_publication_sequences' },
|
|
||||||
{ oid => '6121',
|
{ oid => '6121',
|
||||||
descr => 'returns whether a relation can be part of a publication',
|
descr => 'returns whether a relation can be part of a publication',
|
||||||
proname => 'pg_relation_is_publishable', provolatile => 's',
|
proname => 'pg_relation_is_publishable', provolatile => 's',
|
||||||
|
|
|
@ -40,12 +40,6 @@ CATALOG(pg_publication,6104,PublicationRelationId)
|
||||||
*/
|
*/
|
||||||
bool puballtables;
|
bool puballtables;
|
||||||
|
|
||||||
/*
|
|
||||||
* indicates that this is special publication which should encompass all
|
|
||||||
* sequences in the database (except for the unlogged and temp ones)
|
|
||||||
*/
|
|
||||||
bool puballsequences;
|
|
||||||
|
|
||||||
/* true if inserts are published */
|
/* true if inserts are published */
|
||||||
bool pubinsert;
|
bool pubinsert;
|
||||||
|
|
||||||
|
@ -58,9 +52,6 @@ CATALOG(pg_publication,6104,PublicationRelationId)
|
||||||
/* true if truncates are published */
|
/* true if truncates are published */
|
||||||
bool pubtruncate;
|
bool pubtruncate;
|
||||||
|
|
||||||
/* true if sequences are published */
|
|
||||||
bool pubsequence;
|
|
||||||
|
|
||||||
/* true if partition changes are published using root schema */
|
/* true if partition changes are published using root schema */
|
||||||
bool pubviaroot;
|
bool pubviaroot;
|
||||||
} FormData_pg_publication;
|
} FormData_pg_publication;
|
||||||
|
@ -81,7 +72,6 @@ typedef struct PublicationActions
|
||||||
bool pubupdate;
|
bool pubupdate;
|
||||||
bool pubdelete;
|
bool pubdelete;
|
||||||
bool pubtruncate;
|
bool pubtruncate;
|
||||||
bool pubsequence;
|
|
||||||
} PublicationActions;
|
} PublicationActions;
|
||||||
|
|
||||||
typedef struct PublicationDesc
|
typedef struct PublicationDesc
|
||||||
|
@ -109,7 +99,6 @@ typedef struct Publication
|
||||||
Oid oid;
|
Oid oid;
|
||||||
char *name;
|
char *name;
|
||||||
bool alltables;
|
bool alltables;
|
||||||
bool allsequences;
|
|
||||||
bool pubviaroot;
|
bool pubviaroot;
|
||||||
PublicationActions pubactions;
|
PublicationActions pubactions;
|
||||||
} Publication;
|
} Publication;
|
||||||
|
@ -141,16 +130,14 @@ typedef enum PublicationPartOpt
|
||||||
PUBLICATION_PART_ALL,
|
PUBLICATION_PART_ALL,
|
||||||
} PublicationPartOpt;
|
} PublicationPartOpt;
|
||||||
|
|
||||||
extern List *GetPublicationRelations(Oid pubid, char objectType,
|
extern List *GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt);
|
||||||
PublicationPartOpt pub_partopt);
|
|
||||||
extern List *GetAllTablesPublications(void);
|
extern List *GetAllTablesPublications(void);
|
||||||
extern List *GetAllTablesPublicationRelations(bool pubviaroot);
|
extern List *GetAllTablesPublicationRelations(bool pubviaroot);
|
||||||
extern void GetActionsInPublication(Oid pubid, PublicationActions *actions);
|
extern List *GetPublicationSchemas(Oid pubid);
|
||||||
extern List *GetPublicationSchemas(Oid pubid, char objectType);
|
extern List *GetSchemaPublications(Oid schemaid);
|
||||||
extern List *GetSchemaPublications(Oid schemaid, char objectType);
|
extern List *GetSchemaPublicationRelations(Oid schemaid,
|
||||||
extern List *GetSchemaPublicationRelations(Oid schemaid, char objectType,
|
|
||||||
PublicationPartOpt pub_partopt);
|
PublicationPartOpt pub_partopt);
|
||||||
extern List *GetAllSchemaPublicationRelations(Oid puboid, char objectType,
|
extern List *GetAllSchemaPublicationRelations(Oid puboid,
|
||||||
PublicationPartOpt pub_partopt);
|
PublicationPartOpt pub_partopt);
|
||||||
extern List *GetPubPartitionOptionRelations(List *result,
|
extern List *GetPubPartitionOptionRelations(List *result,
|
||||||
PublicationPartOpt pub_partopt,
|
PublicationPartOpt pub_partopt,
|
||||||
|
@ -158,15 +145,11 @@ extern List *GetPubPartitionOptionRelations(List *result,
|
||||||
extern Oid GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
|
extern Oid GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
|
||||||
int *ancestor_level);
|
int *ancestor_level);
|
||||||
|
|
||||||
extern List *GetAllSequencesPublications(void);
|
|
||||||
extern List *GetAllSequencesPublicationRelations(void);
|
|
||||||
|
|
||||||
extern bool is_publishable_relation(Relation rel);
|
extern bool is_publishable_relation(Relation rel);
|
||||||
extern bool is_schema_publication(Oid pubid);
|
extern bool is_schema_publication(Oid pubid);
|
||||||
extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri,
|
extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri,
|
||||||
bool if_not_exists);
|
bool if_not_exists);
|
||||||
extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid,
|
extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid,
|
||||||
char objectType,
|
|
||||||
bool if_not_exists);
|
bool if_not_exists);
|
||||||
|
|
||||||
extern Bitmapset *pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols,
|
extern Bitmapset *pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols,
|
||||||
|
|
|
@ -32,7 +32,6 @@ CATALOG(pg_publication_namespace,8901,PublicationNamespaceRelationId)
|
||||||
Oid oid; /* oid */
|
Oid oid; /* oid */
|
||||||
Oid pnpubid BKI_LOOKUP(pg_publication); /* Oid of the publication */
|
Oid pnpubid BKI_LOOKUP(pg_publication); /* Oid of the publication */
|
||||||
Oid pnnspid BKI_LOOKUP(pg_namespace); /* Oid of the schema */
|
Oid pnnspid BKI_LOOKUP(pg_namespace); /* Oid of the schema */
|
||||||
char pntype; /* object type to include */
|
|
||||||
} FormData_pg_publication_namespace;
|
} FormData_pg_publication_namespace;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -43,13 +42,6 @@ CATALOG(pg_publication_namespace,8901,PublicationNamespaceRelationId)
|
||||||
typedef FormData_pg_publication_namespace *Form_pg_publication_namespace;
|
typedef FormData_pg_publication_namespace *Form_pg_publication_namespace;
|
||||||
|
|
||||||
DECLARE_UNIQUE_INDEX_PKEY(pg_publication_namespace_oid_index, 8902, PublicationNamespaceObjectIndexId, on pg_publication_namespace using btree(oid oid_ops));
|
DECLARE_UNIQUE_INDEX_PKEY(pg_publication_namespace_oid_index, 8902, PublicationNamespaceObjectIndexId, on pg_publication_namespace using btree(oid oid_ops));
|
||||||
DECLARE_UNIQUE_INDEX(pg_publication_namespace_pnnspid_pnpubid_pntype_index, 8903, PublicationNamespacePnnspidPnpubidPntypeIndexId, on pg_publication_namespace using btree(pnnspid oid_ops, pnpubid oid_ops, pntype char_ops));
|
DECLARE_UNIQUE_INDEX(pg_publication_namespace_pnnspid_pnpubid_index, 8903, PublicationNamespacePnnspidPnpubidIndexId, on pg_publication_namespace using btree(pnnspid oid_ops, pnpubid oid_ops));
|
||||||
|
|
||||||
/* object type to include from a schema, maps to relkind */
|
|
||||||
#define PUB_OBJTYPE_TABLE 't' /* table (regular or partitioned) */
|
|
||||||
#define PUB_OBJTYPE_SEQUENCE 's' /* sequence object */
|
|
||||||
#define PUB_OBJTYPE_UNSUPPORTED 'u' /* used for non-replicated types */
|
|
||||||
|
|
||||||
extern char pub_get_object_type_for_relkind(char relkind);
|
|
||||||
|
|
||||||
#endif /* PG_PUBLICATION_NAMESPACE_H */
|
#endif /* PG_PUBLICATION_NAMESPACE_H */
|
||||||
|
|
|
@ -48,7 +48,6 @@ typedef FormData_pg_sequence_data *Form_pg_sequence_data;
|
||||||
typedef struct xl_seq_rec
|
typedef struct xl_seq_rec
|
||||||
{
|
{
|
||||||
RelFileNode node;
|
RelFileNode node;
|
||||||
bool created; /* creates a new relfilenode (CREATE/ALTER) */
|
|
||||||
/* SEQUENCE TUPLE DATA FOLLOWS AT THE END */
|
/* SEQUENCE TUPLE DATA FOLLOWS AT THE END */
|
||||||
} xl_seq_rec;
|
} xl_seq_rec;
|
||||||
|
|
||||||
|
@ -61,7 +60,6 @@ extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
|
||||||
extern void SequenceChangePersistence(Oid relid, char newrelpersistence);
|
extern void SequenceChangePersistence(Oid relid, char newrelpersistence);
|
||||||
extern void DeleteSequenceTuple(Oid relid);
|
extern void DeleteSequenceTuple(Oid relid);
|
||||||
extern void ResetSequence(Oid seq_relid);
|
extern void ResetSequence(Oid seq_relid);
|
||||||
extern void SetSequence(Oid seq_relid, bool transactional, int64 last_value, int64 log_cnt, bool is_called);
|
|
||||||
extern void ResetSequenceCaches(void);
|
extern void ResetSequenceCaches(void);
|
||||||
|
|
||||||
extern void seq_redo(XLogReaderState *rptr);
|
extern void seq_redo(XLogReaderState *rptr);
|
||||||
|
|
|
@ -4013,10 +4013,6 @@ typedef enum PublicationObjSpecType
|
||||||
PUBLICATIONOBJ_TABLES_IN_SCHEMA, /* All tables in schema */
|
PUBLICATIONOBJ_TABLES_IN_SCHEMA, /* All tables in schema */
|
||||||
PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA, /* All tables in first element of
|
PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA, /* All tables in first element of
|
||||||
* search_path */
|
* search_path */
|
||||||
PUBLICATIONOBJ_SEQUENCE, /* Sequence type */
|
|
||||||
PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA, /* Sequences in schema type */
|
|
||||||
PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA, /* Get the first element of
|
|
||||||
* search_path */
|
|
||||||
PUBLICATIONOBJ_CONTINUATION /* Continuation of previous type */
|
PUBLICATIONOBJ_CONTINUATION /* Continuation of previous type */
|
||||||
} PublicationObjSpecType;
|
} PublicationObjSpecType;
|
||||||
|
|
||||||
|
@ -4035,7 +4031,7 @@ typedef struct CreatePublicationStmt
|
||||||
char *pubname; /* Name of the publication */
|
char *pubname; /* Name of the publication */
|
||||||
List *options; /* List of DefElem nodes */
|
List *options; /* List of DefElem nodes */
|
||||||
List *pubobjects; /* Optional list of publication objects */
|
List *pubobjects; /* Optional list of publication objects */
|
||||||
List *for_all_objects; /* Special publication for all objects in db */
|
bool for_all_tables; /* Special publication for all tables in db */
|
||||||
} CreatePublicationStmt;
|
} CreatePublicationStmt;
|
||||||
|
|
||||||
typedef enum AlterPublicationAction
|
typedef enum AlterPublicationAction
|
||||||
|
@ -4058,7 +4054,7 @@ typedef struct AlterPublicationStmt
|
||||||
* objects.
|
* objects.
|
||||||
*/
|
*/
|
||||||
List *pubobjects; /* Optional list of publication objects */
|
List *pubobjects; /* Optional list of publication objects */
|
||||||
List *for_all_objects; /* Special publication for all objects in db */
|
bool for_all_tables; /* Special publication for all tables in db */
|
||||||
AlterPublicationAction action; /* What action to perform with the given
|
AlterPublicationAction action; /* What action to perform with the given
|
||||||
* objects */
|
* objects */
|
||||||
} AlterPublicationStmt;
|
} AlterPublicationStmt;
|
||||||
|
|
|
@ -27,7 +27,6 @@ extern void heap2_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
|
||||||
extern void xact_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
|
extern void xact_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
|
||||||
extern void standby_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
|
extern void standby_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
|
||||||
extern void logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
|
extern void logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
|
||||||
extern void sequence_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
|
|
||||||
|
|
||||||
extern void LogicalDecodingProcessRecord(LogicalDecodingContext *ctx,
|
extern void LogicalDecodingProcessRecord(LogicalDecodingContext *ctx,
|
||||||
XLogReaderState *record);
|
XLogReaderState *record);
|
||||||
|
|
|
@ -61,7 +61,6 @@ typedef enum LogicalRepMsgType
|
||||||
LOGICAL_REP_MSG_RELATION = 'R',
|
LOGICAL_REP_MSG_RELATION = 'R',
|
||||||
LOGICAL_REP_MSG_TYPE = 'Y',
|
LOGICAL_REP_MSG_TYPE = 'Y',
|
||||||
LOGICAL_REP_MSG_MESSAGE = 'M',
|
LOGICAL_REP_MSG_MESSAGE = 'M',
|
||||||
LOGICAL_REP_MSG_SEQUENCE = 'Q',
|
|
||||||
LOGICAL_REP_MSG_BEGIN_PREPARE = 'b',
|
LOGICAL_REP_MSG_BEGIN_PREPARE = 'b',
|
||||||
LOGICAL_REP_MSG_PREPARE = 'P',
|
LOGICAL_REP_MSG_PREPARE = 'P',
|
||||||
LOGICAL_REP_MSG_COMMIT_PREPARED = 'K',
|
LOGICAL_REP_MSG_COMMIT_PREPARED = 'K',
|
||||||
|
@ -119,18 +118,6 @@ typedef struct LogicalRepTyp
|
||||||
char *typname; /* name of the remote type */
|
char *typname; /* name of the remote type */
|
||||||
} LogicalRepTyp;
|
} LogicalRepTyp;
|
||||||
|
|
||||||
/* Sequence info */
|
|
||||||
typedef struct LogicalRepSequence
|
|
||||||
{
|
|
||||||
Oid remoteid; /* unique id of the remote sequence */
|
|
||||||
char *nspname; /* schema name of remote sequence */
|
|
||||||
char *seqname; /* name of the remote sequence */
|
|
||||||
bool transactional;
|
|
||||||
int64 last_value;
|
|
||||||
int64 log_cnt;
|
|
||||||
bool is_called;
|
|
||||||
} LogicalRepSequence;
|
|
||||||
|
|
||||||
/* Transaction info */
|
/* Transaction info */
|
||||||
typedef struct LogicalRepBeginData
|
typedef struct LogicalRepBeginData
|
||||||
{
|
{
|
||||||
|
@ -243,12 +230,6 @@ extern List *logicalrep_read_truncate(StringInfo in,
|
||||||
bool *cascade, bool *restart_seqs);
|
bool *cascade, bool *restart_seqs);
|
||||||
extern void logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
|
extern void logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
|
||||||
bool transactional, const char *prefix, Size sz, const char *message);
|
bool transactional, const char *prefix, Size sz, const char *message);
|
||||||
extern void logicalrep_write_sequence(StringInfo out, Relation rel,
|
|
||||||
TransactionId xid, XLogRecPtr lsn,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt,
|
|
||||||
bool is_called);
|
|
||||||
extern void logicalrep_read_sequence(StringInfo in, LogicalRepSequence *seqdata);
|
|
||||||
extern void logicalrep_write_rel(StringInfo out, TransactionId xid,
|
extern void logicalrep_write_rel(StringInfo out, TransactionId xid,
|
||||||
Relation rel, Bitmapset *columns);
|
Relation rel, Bitmapset *columns);
|
||||||
extern LogicalRepRelation *logicalrep_read_rel(StringInfo in);
|
extern LogicalRepRelation *logicalrep_read_rel(StringInfo in);
|
||||||
|
|
|
@ -88,18 +88,6 @@ typedef void (*LogicalDecodeMessageCB) (struct LogicalDecodingContext *ctx,
|
||||||
Size message_size,
|
Size message_size,
|
||||||
const char *message);
|
const char *message);
|
||||||
|
|
||||||
/*
|
|
||||||
* Called for the generic logical decoding sequences.
|
|
||||||
*/
|
|
||||||
typedef void (*LogicalDecodeSequenceCB) (struct LogicalDecodingContext *ctx,
|
|
||||||
ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn,
|
|
||||||
Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value,
|
|
||||||
int64 log_cnt,
|
|
||||||
bool is_called);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Filter changes by origin.
|
* Filter changes by origin.
|
||||||
*/
|
*/
|
||||||
|
@ -211,19 +199,6 @@ typedef void (*LogicalDecodeStreamMessageCB) (struct LogicalDecodingContext *ctx
|
||||||
Size message_size,
|
Size message_size,
|
||||||
const char *message);
|
const char *message);
|
||||||
|
|
||||||
/*
|
|
||||||
* Called for the streaming generic logical decoding sequences from in-progress
|
|
||||||
* transactions.
|
|
||||||
*/
|
|
||||||
typedef void (*LogicalDecodeStreamSequenceCB) (struct LogicalDecodingContext *ctx,
|
|
||||||
ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn,
|
|
||||||
Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value,
|
|
||||||
int64 log_cnt,
|
|
||||||
bool is_called);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback for streaming truncates from in-progress transactions.
|
* Callback for streaming truncates from in-progress transactions.
|
||||||
*/
|
*/
|
||||||
|
@ -244,7 +219,6 @@ typedef struct OutputPluginCallbacks
|
||||||
LogicalDecodeTruncateCB truncate_cb;
|
LogicalDecodeTruncateCB truncate_cb;
|
||||||
LogicalDecodeCommitCB commit_cb;
|
LogicalDecodeCommitCB commit_cb;
|
||||||
LogicalDecodeMessageCB message_cb;
|
LogicalDecodeMessageCB message_cb;
|
||||||
LogicalDecodeSequenceCB sequence_cb;
|
|
||||||
LogicalDecodeFilterByOriginCB filter_by_origin_cb;
|
LogicalDecodeFilterByOriginCB filter_by_origin_cb;
|
||||||
LogicalDecodeShutdownCB shutdown_cb;
|
LogicalDecodeShutdownCB shutdown_cb;
|
||||||
|
|
||||||
|
@ -263,7 +237,6 @@ typedef struct OutputPluginCallbacks
|
||||||
LogicalDecodeStreamCommitCB stream_commit_cb;
|
LogicalDecodeStreamCommitCB stream_commit_cb;
|
||||||
LogicalDecodeStreamChangeCB stream_change_cb;
|
LogicalDecodeStreamChangeCB stream_change_cb;
|
||||||
LogicalDecodeStreamMessageCB stream_message_cb;
|
LogicalDecodeStreamMessageCB stream_message_cb;
|
||||||
LogicalDecodeStreamSequenceCB stream_sequence_cb;
|
|
||||||
LogicalDecodeStreamTruncateCB stream_truncate_cb;
|
LogicalDecodeStreamTruncateCB stream_truncate_cb;
|
||||||
} OutputPluginCallbacks;
|
} OutputPluginCallbacks;
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ typedef struct PGOutputData
|
||||||
bool streaming;
|
bool streaming;
|
||||||
bool messages;
|
bool messages;
|
||||||
bool two_phase;
|
bool two_phase;
|
||||||
bool sequences;
|
|
||||||
} PGOutputData;
|
} PGOutputData;
|
||||||
|
|
||||||
#endif /* PGOUTPUT_H */
|
#endif /* PGOUTPUT_H */
|
||||||
|
|
|
@ -64,8 +64,7 @@ typedef enum ReorderBufferChangeType
|
||||||
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT,
|
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT,
|
||||||
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM,
|
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM,
|
||||||
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT,
|
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT,
|
||||||
REORDER_BUFFER_CHANGE_TRUNCATE,
|
REORDER_BUFFER_CHANGE_TRUNCATE
|
||||||
REORDER_BUFFER_CHANGE_SEQUENCE
|
|
||||||
} ReorderBufferChangeType;
|
} ReorderBufferChangeType;
|
||||||
|
|
||||||
/* forward declaration */
|
/* forward declaration */
|
||||||
|
@ -159,13 +158,6 @@ typedef struct ReorderBufferChange
|
||||||
uint32 ninvalidations; /* Number of messages */
|
uint32 ninvalidations; /* Number of messages */
|
||||||
SharedInvalidationMessage *invalidations; /* invalidation message */
|
SharedInvalidationMessage *invalidations; /* invalidation message */
|
||||||
} inval;
|
} inval;
|
||||||
|
|
||||||
/* Context data for Sequence changes */
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
RelFileNode relnode;
|
|
||||||
ReorderBufferTupleBuf *tuple;
|
|
||||||
} sequence;
|
|
||||||
} data;
|
} data;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -438,15 +430,6 @@ typedef void (*ReorderBufferMessageCB) (ReorderBuffer *rb,
|
||||||
const char *prefix, Size sz,
|
const char *prefix, Size sz,
|
||||||
const char *message);
|
const char *message);
|
||||||
|
|
||||||
/* sequence callback signature */
|
|
||||||
typedef void (*ReorderBufferSequenceCB) (ReorderBuffer *rb,
|
|
||||||
ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn,
|
|
||||||
Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt,
|
|
||||||
bool is_called);
|
|
||||||
|
|
||||||
/* begin prepare callback signature */
|
/* begin prepare callback signature */
|
||||||
typedef void (*ReorderBufferBeginPrepareCB) (ReorderBuffer *rb,
|
typedef void (*ReorderBufferBeginPrepareCB) (ReorderBuffer *rb,
|
||||||
ReorderBufferTXN *txn);
|
ReorderBufferTXN *txn);
|
||||||
|
@ -513,15 +496,6 @@ typedef void (*ReorderBufferStreamMessageCB) (
|
||||||
const char *prefix, Size sz,
|
const char *prefix, Size sz,
|
||||||
const char *message);
|
const char *message);
|
||||||
|
|
||||||
/* stream sequence callback signature */
|
|
||||||
typedef void (*ReorderBufferStreamSequenceCB) (ReorderBuffer *rb,
|
|
||||||
ReorderBufferTXN *txn,
|
|
||||||
XLogRecPtr sequence_lsn,
|
|
||||||
Relation rel,
|
|
||||||
bool transactional,
|
|
||||||
int64 last_value, int64 log_cnt,
|
|
||||||
bool is_called);
|
|
||||||
|
|
||||||
/* stream truncate callback signature */
|
/* stream truncate callback signature */
|
||||||
typedef void (*ReorderBufferStreamTruncateCB) (
|
typedef void (*ReorderBufferStreamTruncateCB) (
|
||||||
ReorderBuffer *rb,
|
ReorderBuffer *rb,
|
||||||
|
@ -537,12 +511,6 @@ struct ReorderBuffer
|
||||||
*/
|
*/
|
||||||
HTAB *by_txn;
|
HTAB *by_txn;
|
||||||
|
|
||||||
/*
|
|
||||||
* relfilenode => XID lookup table for sequences created in a transaction
|
|
||||||
* (also includes altered sequences, which assigns new relfilenode)
|
|
||||||
*/
|
|
||||||
HTAB *sequences;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Transactions that could be a toplevel xact, ordered by LSN of the first
|
* Transactions that could be a toplevel xact, ordered by LSN of the first
|
||||||
* record bearing that xid.
|
* record bearing that xid.
|
||||||
|
@ -573,7 +541,6 @@ struct ReorderBuffer
|
||||||
ReorderBufferApplyTruncateCB apply_truncate;
|
ReorderBufferApplyTruncateCB apply_truncate;
|
||||||
ReorderBufferCommitCB commit;
|
ReorderBufferCommitCB commit;
|
||||||
ReorderBufferMessageCB message;
|
ReorderBufferMessageCB message;
|
||||||
ReorderBufferSequenceCB sequence;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callbacks to be called when streaming a transaction at prepare time.
|
* Callbacks to be called when streaming a transaction at prepare time.
|
||||||
|
@ -593,7 +560,6 @@ struct ReorderBuffer
|
||||||
ReorderBufferStreamCommitCB stream_commit;
|
ReorderBufferStreamCommitCB stream_commit;
|
||||||
ReorderBufferStreamChangeCB stream_change;
|
ReorderBufferStreamChangeCB stream_change;
|
||||||
ReorderBufferStreamMessageCB stream_message;
|
ReorderBufferStreamMessageCB stream_message;
|
||||||
ReorderBufferStreamSequenceCB stream_sequence;
|
|
||||||
ReorderBufferStreamTruncateCB stream_truncate;
|
ReorderBufferStreamTruncateCB stream_truncate;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -669,10 +635,6 @@ void ReorderBufferQueueChange(ReorderBuffer *, TransactionId,
|
||||||
void ReorderBufferQueueMessage(ReorderBuffer *, TransactionId, Snapshot snapshot, XLogRecPtr lsn,
|
void ReorderBufferQueueMessage(ReorderBuffer *, TransactionId, Snapshot snapshot, XLogRecPtr lsn,
|
||||||
bool transactional, const char *prefix,
|
bool transactional, const char *prefix,
|
||||||
Size message_size, const char *message);
|
Size message_size, const char *message);
|
||||||
void ReorderBufferQueueSequence(ReorderBuffer *rb, TransactionId xid,
|
|
||||||
Snapshot snapshot, XLogRecPtr lsn, RepOriginId origin_id,
|
|
||||||
RelFileNode rnode, bool transactional, bool created,
|
|
||||||
ReorderBufferTupleBuf *tuplebuf);
|
|
||||||
void ReorderBufferCommit(ReorderBuffer *, TransactionId,
|
void ReorderBufferCommit(ReorderBuffer *, TransactionId,
|
||||||
XLogRecPtr commit_lsn, XLogRecPtr end_lsn,
|
XLogRecPtr commit_lsn, XLogRecPtr end_lsn,
|
||||||
TimestampTz commit_time, RepOriginId origin_id, XLogRecPtr origin_lsn);
|
TimestampTz commit_time, RepOriginId origin_id, XLogRecPtr origin_lsn);
|
||||||
|
@ -720,7 +682,4 @@ void ReorderBufferSetRestartPoint(ReorderBuffer *, XLogRecPtr ptr);
|
||||||
|
|
||||||
void StartupReorderBuffer(void);
|
void StartupReorderBuffer(void);
|
||||||
|
|
||||||
bool ReorderBufferSequenceIsTransactional(ReorderBuffer *rb,
|
|
||||||
RelFileNode rnode, bool created);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -46,7 +46,6 @@ CREATE TRANSFORM FOR int LANGUAGE SQL (
|
||||||
SET client_min_messages = 'ERROR';
|
SET client_min_messages = 'ERROR';
|
||||||
CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable;
|
CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable;
|
||||||
CREATE PUBLICATION addr_pub_schema FOR ALL TABLES IN SCHEMA addr_nsp;
|
CREATE PUBLICATION addr_pub_schema FOR ALL TABLES IN SCHEMA addr_nsp;
|
||||||
CREATE PUBLICATION addr_pub_schema2 FOR ALL SEQUENCES IN SCHEMA addr_nsp;
|
|
||||||
RESET client_min_messages;
|
RESET client_min_messages;
|
||||||
CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE);
|
CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE);
|
||||||
WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables
|
WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables
|
||||||
|
@ -429,8 +428,7 @@ WITH objects (type, name, args) AS (VALUES
|
||||||
('transform', '{int}', '{sql}'),
|
('transform', '{int}', '{sql}'),
|
||||||
('access method', '{btree}', '{}'),
|
('access method', '{btree}', '{}'),
|
||||||
('publication', '{addr_pub}', '{}'),
|
('publication', '{addr_pub}', '{}'),
|
||||||
('publication namespace', '{addr_nsp}', '{addr_pub_schema,t}'),
|
('publication namespace', '{addr_nsp}', '{addr_pub_schema}'),
|
||||||
('publication namespace', '{addr_nsp}', '{addr_pub_schema2,s}'),
|
|
||||||
('publication relation', '{addr_nsp, gentable}', '{addr_pub}'),
|
('publication relation', '{addr_nsp, gentable}', '{addr_pub}'),
|
||||||
('subscription', '{regress_addr_sub}', '{}'),
|
('subscription', '{regress_addr_sub}', '{}'),
|
||||||
('statistics object', '{addr_nsp, gentable_stat}', '{}')
|
('statistics object', '{addr_nsp, gentable_stat}', '{}')
|
||||||
|
@ -494,9 +492,8 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*,
|
||||||
subscription | | regress_addr_sub | regress_addr_sub | t
|
subscription | | regress_addr_sub | regress_addr_sub | t
|
||||||
publication | | addr_pub | addr_pub | t
|
publication | | addr_pub | addr_pub | t
|
||||||
publication relation | | | addr_nsp.gentable in publication addr_pub | t
|
publication relation | | | addr_nsp.gentable in publication addr_pub | t
|
||||||
publication namespace | | | addr_nsp in publication addr_pub_schema type t | t
|
publication namespace | | | addr_nsp in publication addr_pub_schema | t
|
||||||
publication namespace | | | addr_nsp in publication addr_pub_schema2 type s | t
|
(50 rows)
|
||||||
(51 rows)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
--- Cleanup resources
|
--- Cleanup resources
|
||||||
|
@ -509,7 +506,6 @@ drop cascades to server integer
|
||||||
drop cascades to user mapping for regress_addr_user on server integer
|
drop cascades to user mapping for regress_addr_user on server integer
|
||||||
DROP PUBLICATION addr_pub;
|
DROP PUBLICATION addr_pub;
|
||||||
DROP PUBLICATION addr_pub_schema;
|
DROP PUBLICATION addr_pub_schema;
|
||||||
DROP PUBLICATION addr_pub_schema2;
|
|
||||||
DROP SUBSCRIPTION regress_addr_sub;
|
DROP SUBSCRIPTION regress_addr_sub;
|
||||||
DROP SCHEMA addr_nsp CASCADE;
|
DROP SCHEMA addr_nsp CASCADE;
|
||||||
NOTICE: drop cascades to 14 other objects
|
NOTICE: drop cascades to 14 other objects
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1435,14 +1435,6 @@ pg_prepared_xacts| SELECT p.transaction,
|
||||||
FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid)
|
FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid)
|
||||||
LEFT JOIN pg_authid u ON ((p.ownerid = u.oid)))
|
LEFT JOIN pg_authid u ON ((p.ownerid = u.oid)))
|
||||||
LEFT JOIN pg_database d ON ((p.dbid = d.oid)));
|
LEFT JOIN pg_database d ON ((p.dbid = d.oid)));
|
||||||
pg_publication_sequences| SELECT p.pubname,
|
|
||||||
n.nspname AS schemaname,
|
|
||||||
c.relname AS sequencename
|
|
||||||
FROM pg_publication p,
|
|
||||||
LATERAL pg_get_publication_sequences((p.pubname)::text) gps(relid),
|
|
||||||
(pg_class c
|
|
||||||
JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
|
|
||||||
WHERE (c.oid = gps.relid);
|
|
||||||
pg_publication_tables| SELECT p.pubname,
|
pg_publication_tables| SELECT p.pubname,
|
||||||
n.nspname AS schemaname,
|
n.nspname AS schemaname,
|
||||||
c.relname AS tablename
|
c.relname AS tablename
|
||||||
|
|
|
@ -49,7 +49,6 @@ CREATE TRANSFORM FOR int LANGUAGE SQL (
|
||||||
SET client_min_messages = 'ERROR';
|
SET client_min_messages = 'ERROR';
|
||||||
CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable;
|
CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable;
|
||||||
CREATE PUBLICATION addr_pub_schema FOR ALL TABLES IN SCHEMA addr_nsp;
|
CREATE PUBLICATION addr_pub_schema FOR ALL TABLES IN SCHEMA addr_nsp;
|
||||||
CREATE PUBLICATION addr_pub_schema2 FOR ALL SEQUENCES IN SCHEMA addr_nsp;
|
|
||||||
RESET client_min_messages;
|
RESET client_min_messages;
|
||||||
CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE);
|
CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE);
|
||||||
CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable;
|
CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable;
|
||||||
|
@ -199,8 +198,7 @@ WITH objects (type, name, args) AS (VALUES
|
||||||
('transform', '{int}', '{sql}'),
|
('transform', '{int}', '{sql}'),
|
||||||
('access method', '{btree}', '{}'),
|
('access method', '{btree}', '{}'),
|
||||||
('publication', '{addr_pub}', '{}'),
|
('publication', '{addr_pub}', '{}'),
|
||||||
('publication namespace', '{addr_nsp}', '{addr_pub_schema,t}'),
|
('publication namespace', '{addr_nsp}', '{addr_pub_schema}'),
|
||||||
('publication namespace', '{addr_nsp}', '{addr_pub_schema2,s}'),
|
|
||||||
('publication relation', '{addr_nsp, gentable}', '{addr_pub}'),
|
('publication relation', '{addr_nsp, gentable}', '{addr_pub}'),
|
||||||
('subscription', '{regress_addr_sub}', '{}'),
|
('subscription', '{regress_addr_sub}', '{}'),
|
||||||
('statistics object', '{addr_nsp, gentable_stat}', '{}')
|
('statistics object', '{addr_nsp, gentable_stat}', '{}')
|
||||||
|
@ -220,7 +218,6 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*,
|
||||||
DROP FOREIGN DATA WRAPPER addr_fdw CASCADE;
|
DROP FOREIGN DATA WRAPPER addr_fdw CASCADE;
|
||||||
DROP PUBLICATION addr_pub;
|
DROP PUBLICATION addr_pub;
|
||||||
DROP PUBLICATION addr_pub_schema;
|
DROP PUBLICATION addr_pub_schema;
|
||||||
DROP PUBLICATION addr_pub_schema2;
|
|
||||||
DROP SUBSCRIPTION regress_addr_sub;
|
DROP SUBSCRIPTION regress_addr_sub;
|
||||||
|
|
||||||
DROP SCHEMA addr_nsp CASCADE;
|
DROP SCHEMA addr_nsp CASCADE;
|
||||||
|
|
|
@ -27,7 +27,7 @@ CREATE PUBLICATION testpub_xxx WITH (publish_via_partition_root = 'true', publis
|
||||||
|
|
||||||
\dRp
|
\dRp
|
||||||
|
|
||||||
ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete, sequence');
|
ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete');
|
||||||
|
|
||||||
\dRp
|
\dRp
|
||||||
|
|
||||||
|
@ -46,8 +46,6 @@ ALTER PUBLICATION testpub_foralltables SET (publish = 'insert, update');
|
||||||
CREATE TABLE testpub_tbl2 (id serial primary key, data text);
|
CREATE TABLE testpub_tbl2 (id serial primary key, data text);
|
||||||
-- fail - can't add to for all tables publication
|
-- fail - can't add to for all tables publication
|
||||||
ALTER PUBLICATION testpub_foralltables ADD TABLE testpub_tbl2;
|
ALTER PUBLICATION testpub_foralltables ADD TABLE testpub_tbl2;
|
||||||
-- fail - can't add a table using ADD SEQUENCE command
|
|
||||||
ALTER PUBLICATION testpub_foralltables ADD SEQUENCE testpub_tbl2;
|
|
||||||
-- fail - can't drop from all tables publication
|
-- fail - can't drop from all tables publication
|
||||||
ALTER PUBLICATION testpub_foralltables DROP TABLE testpub_tbl2;
|
ALTER PUBLICATION testpub_foralltables DROP TABLE testpub_tbl2;
|
||||||
-- fail - can't add to for all tables publication
|
-- fail - can't add to for all tables publication
|
||||||
|
@ -106,199 +104,6 @@ RESET client_min_messages;
|
||||||
DROP TABLE testpub_tbl3, testpub_tbl3a;
|
DROP TABLE testpub_tbl3, testpub_tbl3a;
|
||||||
DROP PUBLICATION testpub3, testpub4;
|
DROP PUBLICATION testpub3, testpub4;
|
||||||
|
|
||||||
--- adding sequences
|
|
||||||
CREATE SEQUENCE testpub_seq0;
|
|
||||||
CREATE SEQUENCE pub_test.testpub_seq1;
|
|
||||||
|
|
||||||
SET client_min_messages = 'ERROR';
|
|
||||||
CREATE PUBLICATION testpub_forallsequences FOR ALL SEQUENCES WITH (publish = 'sequence');
|
|
||||||
RESET client_min_messages;
|
|
||||||
ALTER PUBLICATION testpub_forallsequences SET (publish = 'insert, sequence');
|
|
||||||
|
|
||||||
CREATE SEQUENCE testpub_seq2;
|
|
||||||
-- fail - can't add to for all sequences publication
|
|
||||||
ALTER PUBLICATION testpub_forallsequences ADD SEQUENCE testpub_seq2;
|
|
||||||
-- fail - can't drop from all sequences publication
|
|
||||||
ALTER PUBLICATION testpub_forallsequences DROP SEQUENCE testpub_seq2;
|
|
||||||
-- fail - can't add to for all sequences publication
|
|
||||||
ALTER PUBLICATION testpub_forallsequences SET SEQUENCE pub_test.testpub_seq1;
|
|
||||||
|
|
||||||
-- fail - can't add schema to 'FOR ALL SEQUENCES' publication
|
|
||||||
ALTER PUBLICATION testpub_forallsequences ADD ALL SEQUENCES IN SCHEMA pub_test;
|
|
||||||
-- fail - can't drop schema from 'FOR ALL SEQUENCES' publication
|
|
||||||
ALTER PUBLICATION testpub_forallsequences DROP ALL SEQUENCES IN SCHEMA pub_test;
|
|
||||||
-- fail - can't set schema to 'FOR ALL SEQUENCES' publication
|
|
||||||
ALTER PUBLICATION testpub_forallsequences SET ALL SEQUENCES IN SCHEMA pub_test;
|
|
||||||
|
|
||||||
SET client_min_messages = 'ERROR';
|
|
||||||
CREATE PUBLICATION testpub_forsequence FOR SEQUENCE testpub_seq0;
|
|
||||||
RESET client_min_messages;
|
|
||||||
-- should be able to add schema to 'FOR SEQUENCE' publication
|
|
||||||
ALTER PUBLICATION testpub_forsequence ADD ALL SEQUENCES IN SCHEMA pub_test;
|
|
||||||
\dRp+ testpub_forsequence
|
|
||||||
-- fail - can't add sequence from the schema we already added
|
|
||||||
ALTER PUBLICATION testpub_forsequence ADD SEQUENCE pub_test.testpub_seq1;
|
|
||||||
-- fail - can't add sequence using ADD TABLE command
|
|
||||||
ALTER PUBLICATION testpub_forsequence ADD TABLE pub_test.testpub_seq1;
|
|
||||||
-- should be able to drop schema from 'FOR SEQUENCE' publication
|
|
||||||
ALTER PUBLICATION testpub_forsequence DROP ALL SEQUENCES IN SCHEMA pub_test;
|
|
||||||
\dRp+ testpub_forsequence
|
|
||||||
-- should be able to set schema to 'FOR SEQUENCE' publication
|
|
||||||
ALTER PUBLICATION testpub_forsequence SET ALL SEQUENCES IN SCHEMA pub_test;
|
|
||||||
\dRp+ testpub_forsequence
|
|
||||||
|
|
||||||
SET client_min_messages = 'ERROR';
|
|
||||||
CREATE PUBLICATION testpub_forschema FOR ALL SEQUENCES IN SCHEMA pub_test;
|
|
||||||
RESET client_min_messages;
|
|
||||||
-- fail - can't create publication with schema and sequence of the same schema
|
|
||||||
CREATE PUBLICATION testpub_for_seq_schema FOR ALL SEQUENCES IN SCHEMA pub_test, SEQUENCE pub_test.testpub_seq1;
|
|
||||||
-- fail - can't add a sequence of the same schema to the schema publication
|
|
||||||
ALTER PUBLICATION testpub_forschema ADD SEQUENCE pub_test.testpub_seq1;
|
|
||||||
-- fail - can't drop a sequence from the schema publication which isn't in the
|
|
||||||
-- publication
|
|
||||||
ALTER PUBLICATION testpub_forschema DROP SEQUENCE pub_test.testpub_seq1;
|
|
||||||
-- should be able to set sequence to schema publication
|
|
||||||
ALTER PUBLICATION testpub_forschema SET SEQUENCE pub_test.testpub_seq1;
|
|
||||||
\dRp+ testpub_forschema
|
|
||||||
|
|
||||||
SELECT pubname, puballtables, puballsequences FROM pg_publication WHERE pubname = 'testpub_forallsequences';
|
|
||||||
\d+ pub_test.testpub_seq1
|
|
||||||
\dRp+ testpub_forallsequences
|
|
||||||
DROP SEQUENCE testpub_seq0, pub_test.testpub_seq1, testpub_seq2;
|
|
||||||
DROP PUBLICATION testpub_forallsequences, testpub_forsequence, testpub_forschema;
|
|
||||||
|
|
||||||
|
|
||||||
-- publication testing multiple sequences at the same time
|
|
||||||
CREATE SEQUENCE testpub_seq1;
|
|
||||||
CREATE SEQUENCE testpub_seq2;
|
|
||||||
|
|
||||||
SET client_min_messages = 'ERROR';
|
|
||||||
CREATE PUBLICATION testpub_multi FOR SEQUENCE testpub_seq1, testpub_seq2;
|
|
||||||
RESET client_min_messages;
|
|
||||||
|
|
||||||
\dRp+ testpub_multi
|
|
||||||
|
|
||||||
DROP PUBLICATION testpub_multi;
|
|
||||||
DROP SEQUENCE testpub_seq1;
|
|
||||||
DROP SEQUENCE testpub_seq2;
|
|
||||||
|
|
||||||
|
|
||||||
-- Publication mixing tables and sequences
|
|
||||||
SET client_min_messages = 'ERROR';
|
|
||||||
CREATE PUBLICATION testpub_mix;
|
|
||||||
RESET client_min_messages;
|
|
||||||
|
|
||||||
CREATE SEQUENCE testpub_seq1;
|
|
||||||
CREATE SEQUENCE pub_test.testpub_seq2;
|
|
||||||
|
|
||||||
ALTER PUBLICATION testpub_mix ADD SEQUENCE testpub_seq1, TABLE testpub_tbl1;
|
|
||||||
\dRp+ testpub_mix
|
|
||||||
|
|
||||||
ALTER PUBLICATION testpub_mix ADD ALL SEQUENCES IN SCHEMA pub_test, ALL TABLES IN SCHEMA pub_test;
|
|
||||||
\dRp+ testpub_mix
|
|
||||||
|
|
||||||
ALTER PUBLICATION testpub_mix DROP ALL SEQUENCES IN SCHEMA pub_test;
|
|
||||||
\dRp+ testpub_mix
|
|
||||||
|
|
||||||
ALTER PUBLICATION testpub_mix DROP ALL TABLES IN SCHEMA pub_test;
|
|
||||||
\dRp+ testpub_mix
|
|
||||||
|
|
||||||
DROP PUBLICATION testpub_mix;
|
|
||||||
DROP SEQUENCE testpub_seq1;
|
|
||||||
DROP SEQUENCE pub_test.testpub_seq2;
|
|
||||||
|
|
||||||
|
|
||||||
-- make sure we replicate only the correct relation type
|
|
||||||
CREATE SCHEMA pub_test1;
|
|
||||||
CREATE SEQUENCE pub_test1.test_seq1;
|
|
||||||
CREATE TABLE pub_test1.test_tbl1 (a int primary key, b int);
|
|
||||||
|
|
||||||
CREATE SCHEMA pub_test2;
|
|
||||||
CREATE SEQUENCE pub_test2.test_seq2;
|
|
||||||
CREATE TABLE pub_test2.test_tbl2 (a int primary key, b int);
|
|
||||||
|
|
||||||
SET client_min_messages = 'ERROR';
|
|
||||||
CREATE PUBLICATION testpub_schemas;
|
|
||||||
RESET client_min_messages;
|
|
||||||
|
|
||||||
-- add tables from one schema, sequences from the other
|
|
||||||
ALTER PUBLICATION testpub_schemas ADD ALL TABLES IN SCHEMA pub_test2;
|
|
||||||
ALTER PUBLICATION testpub_schemas ADD ALL SEQUENCES IN SCHEMA pub_test1;
|
|
||||||
|
|
||||||
\dRp+ testpub_schemas
|
|
||||||
|
|
||||||
\dn+ pub_test1
|
|
||||||
\dn+ pub_test2
|
|
||||||
|
|
||||||
\d+ pub_test1.test_seq1;
|
|
||||||
\d+ pub_test1.test_tbl1;
|
|
||||||
|
|
||||||
\d+ pub_test2.test_seq2;
|
|
||||||
\d+ pub_test2.test_tbl2;
|
|
||||||
|
|
||||||
-- add the other object type from each schema
|
|
||||||
ALTER PUBLICATION testpub_schemas ADD ALL TABLES IN SCHEMA pub_test1;
|
|
||||||
ALTER PUBLICATION testpub_schemas ADD ALL SEQUENCES IN SCHEMA pub_test2;
|
|
||||||
|
|
||||||
\dRp+ testpub_schemas
|
|
||||||
|
|
||||||
\dn+ pub_test1
|
|
||||||
\dn+ pub_test2
|
|
||||||
|
|
||||||
\d+ pub_test1.test_seq1;
|
|
||||||
\d+ pub_test1.test_tbl1;
|
|
||||||
|
|
||||||
\d+ pub_test2.test_seq2;
|
|
||||||
\d+ pub_test2.test_tbl2;
|
|
||||||
|
|
||||||
-- now drop the object type added first
|
|
||||||
ALTER PUBLICATION testpub_schemas DROP ALL TABLES IN SCHEMA pub_test2;
|
|
||||||
ALTER PUBLICATION testpub_schemas DROP ALL SEQUENCES IN SCHEMA pub_test1;
|
|
||||||
|
|
||||||
\dRp+ testpub_schemas
|
|
||||||
|
|
||||||
\dn+ pub_test1
|
|
||||||
\dn+ pub_test2
|
|
||||||
|
|
||||||
\d+ pub_test1.test_seq1;
|
|
||||||
\d+ pub_test1.test_tbl1;
|
|
||||||
|
|
||||||
\d+ pub_test2.test_seq2;
|
|
||||||
\d+ pub_test2.test_tbl2;
|
|
||||||
|
|
||||||
-- should fail (publication contains the whole schema)
|
|
||||||
ALTER PUBLICATION testpub_schemas ADD TABLE pub_test1.test_tbl1;
|
|
||||||
ALTER PUBLICATION testpub_schemas ADD SEQUENCE pub_test2.test_seq2;
|
|
||||||
|
|
||||||
-- should work (different schema)
|
|
||||||
ALTER PUBLICATION testpub_schemas ADD TABLE pub_test2.test_tbl2;
|
|
||||||
ALTER PUBLICATION testpub_schemas ADD SEQUENCE pub_test1.test_seq1;
|
|
||||||
|
|
||||||
\dRp+ testpub_schemas
|
|
||||||
|
|
||||||
\d+ pub_test1.test_seq1;
|
|
||||||
\d+ pub_test1.test_tbl1;
|
|
||||||
|
|
||||||
\d+ pub_test2.test_seq2;
|
|
||||||
\d+ pub_test2.test_tbl2;
|
|
||||||
|
|
||||||
-- now drop the explicitly added objects again
|
|
||||||
ALTER PUBLICATION testpub_schemas DROP TABLE pub_test2.test_tbl2;
|
|
||||||
ALTER PUBLICATION testpub_schemas DROP SEQUENCE pub_test1.test_seq1;
|
|
||||||
|
|
||||||
\dRp+ testpub_schemas
|
|
||||||
|
|
||||||
\d+ pub_test1.test_seq1;
|
|
||||||
\d+ pub_test1.test_tbl1;
|
|
||||||
|
|
||||||
\d+ pub_test2.test_seq2;
|
|
||||||
\d+ pub_test2.test_tbl2;
|
|
||||||
|
|
||||||
DROP PUBLICATION testpub_schemas;
|
|
||||||
DROP TABLE pub_test1.test_tbl1, pub_test2.test_tbl2;
|
|
||||||
DROP SEQUENCE pub_test1.test_seq1, pub_test2.test_seq2;
|
|
||||||
DROP SCHEMA pub_test1, pub_test2;
|
|
||||||
|
|
||||||
-- Tests for partitioned tables
|
-- Tests for partitioned tables
|
||||||
SET client_min_messages = 'ERROR';
|
SET client_min_messages = 'ERROR';
|
||||||
CREATE PUBLICATION testpub_forparted;
|
CREATE PUBLICATION testpub_forparted;
|
||||||
|
@ -1199,51 +1004,32 @@ CREATE SCHEMA sch1;
|
||||||
CREATE SCHEMA sch2;
|
CREATE SCHEMA sch2;
|
||||||
CREATE TABLE sch1.tbl1 (a int) PARTITION BY RANGE(a);
|
CREATE TABLE sch1.tbl1 (a int) PARTITION BY RANGE(a);
|
||||||
CREATE TABLE sch2.tbl1_part1 PARTITION OF sch1.tbl1 FOR VALUES FROM (1) to (10);
|
CREATE TABLE sch2.tbl1_part1 PARTITION OF sch1.tbl1 FOR VALUES FROM (1) to (10);
|
||||||
CREATE SEQUENCE sch1.seq1;
|
|
||||||
CREATE SEQUENCE sch2.seq2;
|
|
||||||
-- Schema publication that does not include the schema that has the parent table
|
-- Schema publication that does not include the schema that has the parent table
|
||||||
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
|
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
|
||||||
ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch2;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
SELECT * FROM pg_publication_tables;
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
DROP PUBLICATION pub;
|
DROP PUBLICATION pub;
|
||||||
-- Table publication that does not include the parent table
|
-- Table publication that does not include the parent table
|
||||||
CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
|
CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
|
||||||
ALTER PUBLICATION pub ADD SEQUENCE sch2.seq2;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
SELECT * FROM pg_publication_tables;
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
-- Table publication that includes both the parent table and the child table
|
-- Table publication that includes both the parent table and the child table
|
||||||
ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
|
ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
|
||||||
ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
SELECT * FROM pg_publication_tables;
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
DROP PUBLICATION pub;
|
DROP PUBLICATION pub;
|
||||||
-- Schema publication that does not include the schema that has the parent table
|
-- Schema publication that does not include the schema that has the parent table
|
||||||
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
|
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
|
||||||
ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
SELECT * FROM pg_publication_tables;
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
DROP PUBLICATION pub;
|
|
||||||
-- Sequence publication
|
|
||||||
CREATE PUBLICATION pub FOR SEQUENCE sch2.seq2;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
DROP PUBLICATION pub;
|
DROP PUBLICATION pub;
|
||||||
-- Table publication that does not include the parent table
|
-- Table publication that does not include the parent table
|
||||||
CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
|
CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
|
||||||
SELECT * FROM pg_publication_tables;
|
SELECT * FROM pg_publication_tables;
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
-- Table publication that includes both the parent table and the child table
|
-- Table publication that includes both the parent table and the child table
|
||||||
ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
|
ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
|
||||||
ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch2;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
SELECT * FROM pg_publication_tables;
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
DROP PUBLICATION pub;
|
DROP PUBLICATION pub;
|
||||||
DROP TABLE sch2.tbl1_part1;
|
DROP TABLE sch2.tbl1_part1;
|
||||||
|
@ -1256,36 +1042,10 @@ CREATE TABLE sch1.tbl1_part3 (a int) PARTITION BY RANGE(a);
|
||||||
ALTER TABLE sch1.tbl1 ATTACH PARTITION sch1.tbl1_part3 FOR VALUES FROM (20) to (30);
|
ALTER TABLE sch1.tbl1 ATTACH PARTITION sch1.tbl1_part3 FOR VALUES FROM (20) to (30);
|
||||||
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
|
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
|
||||||
SELECT * FROM pg_publication_tables;
|
SELECT * FROM pg_publication_tables;
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
DROP PUBLICATION pub;
|
|
||||||
-- Schema publication
|
|
||||||
CREATE PUBLICATION pub FOR SEQUENCE sch2.seq2;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
DROP PUBLICATION pub;
|
|
||||||
-- Sequence publication
|
|
||||||
CREATE PUBLICATION pub FOR ALL SEQUENCES IN SCHEMA sch2;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
ALTER PUBLICATION pub DROP SEQUENCE sch1.seq1;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch1;
|
|
||||||
SELECT * FROM pg_publication_tables;
|
|
||||||
SELECT * FROM pg_publication_sequences;
|
|
||||||
|
|
||||||
RESET client_min_messages;
|
RESET client_min_messages;
|
||||||
DROP PUBLICATION pub;
|
DROP PUBLICATION pub;
|
||||||
DROP TABLE sch1.tbl1;
|
DROP TABLE sch1.tbl1;
|
||||||
DROP SEQUENCE sch1.seq1, sch2.seq2;
|
|
||||||
DROP SCHEMA sch1 cascade;
|
DROP SCHEMA sch1 cascade;
|
||||||
DROP SCHEMA sch2 cascade;
|
DROP SCHEMA sch2 cascade;
|
||||||
|
|
||||||
|
|
|
@ -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