mirror of
https://github.com/postgres/postgres.git
synced 2025-05-13 01:13:08 -04:00
Add option to enable two_phase commits via pg_create_logical_replication_slot.
Commit 0aa8a01d04 extends the output plugin API to allow decoding of prepared xacts and allowed the user to enable/disable the two-phase option via pg_logical_slot_get_changes(). This can lead to a problem such that the first time when it gets changes via pg_logical_slot_get_changes() without two_phase option enabled it will not get the prepared even though prepare is after consistent snapshot. Now next time during getting changes, if the two_phase option is enabled it can skip prepare because by that time start decoding point has been moved. So the user will only get commit prepared. Allow to enable/disable this option at the create slot time and default will be false. It will break the existing slots which is fine in a major release. Author: Ajin Cherian Reviewed-by: Amit Kapila and Vignesh C Discussion: https://postgr.es/m/d0f60d60-133d-bf8d-bd70-47784d8fabf3@enterprisedb.com
This commit is contained in:
parent
ee28cacf61
commit
19890a064e
@ -1,7 +1,7 @@
|
|||||||
-- Test prepared transactions. When two-phase-commit is enabled, transactions are
|
-- Test prepared transactions. When two-phase-commit is enabled, transactions are
|
||||||
-- decoded at PREPARE time rather than at COMMIT PREPARED time.
|
-- decoded at PREPARE time rather than at COMMIT PREPARED time.
|
||||||
SET synchronous_commit = on;
|
SET synchronous_commit = on;
|
||||||
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
|
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding', false, true);
|
||||||
?column?
|
?column?
|
||||||
----------
|
----------
|
||||||
init
|
init
|
||||||
@ -15,14 +15,14 @@ BEGIN;
|
|||||||
INSERT INTO test_prepared1 VALUES (1);
|
INSERT INTO test_prepared1 VALUES (1);
|
||||||
INSERT INTO test_prepared1 VALUES (2);
|
INSERT INTO test_prepared1 VALUES (2);
|
||||||
-- should show nothing because the xact has not been prepared yet.
|
-- should show nothing because the xact has not been prepared yet.
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
------
|
------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
PREPARE TRANSACTION 'test_prepared#1';
|
PREPARE TRANSACTION 'test_prepared#1';
|
||||||
-- should show both the above inserts and the PREPARE TRANSACTION.
|
-- should show both the above inserts and the PREPARE TRANSACTION.
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
@ -32,7 +32,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two
|
|||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
COMMIT PREPARED 'test_prepared#1';
|
COMMIT PREPARED 'test_prepared#1';
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
COMMIT PREPARED 'test_prepared#1'
|
COMMIT PREPARED 'test_prepared#1'
|
||||||
@ -42,7 +42,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two
|
|||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO test_prepared1 VALUES (3);
|
INSERT INTO test_prepared1 VALUES (3);
|
||||||
PREPARE TRANSACTION 'test_prepared#2';
|
PREPARE TRANSACTION 'test_prepared#2';
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
@ -51,7 +51,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two
|
|||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
ROLLBACK PREPARED 'test_prepared#2';
|
ROLLBACK PREPARED 'test_prepared#2';
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
ROLLBACK PREPARED 'test_prepared#2'
|
ROLLBACK PREPARED 'test_prepared#2'
|
||||||
@ -74,7 +74,7 @@ WHERE locktype = 'relation'
|
|||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
-- The insert should show the newly altered column but not the DDL.
|
-- The insert should show the newly altered column but not the DDL.
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
@ -89,7 +89,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two
|
|||||||
-- the ALTER will stop us inserting into the other one.
|
-- the ALTER will stop us inserting into the other one.
|
||||||
--
|
--
|
||||||
INSERT INTO test_prepared2 VALUES (5);
|
INSERT INTO test_prepared2 VALUES (5);
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
@ -98,7 +98,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two
|
|||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
COMMIT PREPARED 'test_prepared#3';
|
COMMIT PREPARED 'test_prepared#3';
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
COMMIT PREPARED 'test_prepared#3'
|
COMMIT PREPARED 'test_prepared#3'
|
||||||
@ -107,7 +107,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two
|
|||||||
-- make sure stuff still works
|
-- make sure stuff still works
|
||||||
INSERT INTO test_prepared1 VALUES (6);
|
INSERT INTO test_prepared1 VALUES (6);
|
||||||
INSERT INTO test_prepared2 VALUES (7);
|
INSERT INTO test_prepared2 VALUES (7);
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
@ -138,7 +138,7 @@ WHERE locktype = 'relation'
|
|||||||
|
|
||||||
-- The above CLUSTER command shouldn't cause a timeout on 2pc decoding.
|
-- The above CLUSTER command shouldn't cause a timeout on 2pc decoding.
|
||||||
SET statement_timeout = '180s';
|
SET statement_timeout = '180s';
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
@ -150,7 +150,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two
|
|||||||
RESET statement_timeout;
|
RESET statement_timeout;
|
||||||
COMMIT PREPARED 'test_prepared_lock';
|
COMMIT PREPARED 'test_prepared_lock';
|
||||||
-- consume the commit
|
-- consume the commit
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
COMMIT PREPARED 'test_prepared_lock'
|
COMMIT PREPARED 'test_prepared_lock'
|
||||||
@ -166,7 +166,7 @@ INSERT INTO test_prepared_savepoint VALUES (2);
|
|||||||
ROLLBACK TO SAVEPOINT test_savepoint;
|
ROLLBACK TO SAVEPOINT test_savepoint;
|
||||||
PREPARE TRANSACTION 'test_prepared_savepoint';
|
PREPARE TRANSACTION 'test_prepared_savepoint';
|
||||||
-- should show only 1, not 2
|
-- should show only 1, not 2
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
@ -176,7 +176,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two
|
|||||||
|
|
||||||
COMMIT PREPARED 'test_prepared_savepoint';
|
COMMIT PREPARED 'test_prepared_savepoint';
|
||||||
-- consume the commit
|
-- consume the commit
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
COMMIT PREPARED 'test_prepared_savepoint'
|
COMMIT PREPARED 'test_prepared_savepoint'
|
||||||
@ -187,14 +187,14 @@ BEGIN;
|
|||||||
INSERT INTO test_prepared1 VALUES (20);
|
INSERT INTO test_prepared1 VALUES (20);
|
||||||
PREPARE TRANSACTION 'test_prepared_nodecode';
|
PREPARE TRANSACTION 'test_prepared_nodecode';
|
||||||
-- should show nothing
|
-- should show nothing
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
------
|
------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
COMMIT PREPARED 'test_prepared_nodecode';
|
COMMIT PREPARED 'test_prepared_nodecode';
|
||||||
-- should be decoded now
|
-- should be decoded now
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
@ -207,7 +207,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two
|
|||||||
DROP TABLE test_prepared1;
|
DROP TABLE test_prepared1;
|
||||||
DROP TABLE test_prepared2;
|
DROP TABLE test_prepared2;
|
||||||
-- show results. There should be nothing to show
|
-- show results. There should be nothing to show
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
------
|
------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
@ -6,7 +6,7 @@ step s2txid: SELECT pg_current_xact_id() IS NULL;
|
|||||||
?column?
|
?column?
|
||||||
|
|
||||||
f
|
f
|
||||||
step s1init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding'); <waiting ...>
|
step s1init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding', false, true); <waiting ...>
|
||||||
step s3b: BEGIN;
|
step s3b: BEGIN;
|
||||||
step s3txid: SELECT pg_current_xact_id() IS NULL;
|
step s3txid: SELECT pg_current_xact_id() IS NULL;
|
||||||
?column?
|
?column?
|
||||||
@ -22,14 +22,14 @@ step s1init: <... completed>
|
|||||||
|
|
||||||
init
|
init
|
||||||
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', 'skip-empty-xacts', '1', 'two-phase-commit', '1');
|
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
table public.do_write: INSERT: id[integer]:2
|
table public.do_write: INSERT: id[integer]:2
|
||||||
COMMIT
|
COMMIT
|
||||||
step s2cp: COMMIT PREPARED 'test1';
|
step s2cp: COMMIT PREPARED 'test1';
|
||||||
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'skip-empty-xacts', '1', 'two-phase-commit', '1');
|
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'skip-empty-xacts', '1');
|
||||||
data
|
data
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
-- Test streaming of two-phase commits
|
-- Test streaming of two-phase commits
|
||||||
SET synchronous_commit = on;
|
SET synchronous_commit = on;
|
||||||
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
|
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding', false, true);
|
||||||
?column?
|
?column?
|
||||||
----------
|
----------
|
||||||
init
|
init
|
||||||
@ -28,7 +28,7 @@ ROLLBACK TO s1;
|
|||||||
INSERT INTO stream_test SELECT repeat('a', 10) || g.i FROM generate_series(1, 20) g(i);
|
INSERT INTO stream_test SELECT repeat('a', 10) || g.i FROM generate_series(1, 20) g(i);
|
||||||
PREPARE TRANSACTION 'test1';
|
PREPARE TRANSACTION 'test1';
|
||||||
-- should show the inserts after a ROLLBACK
|
-- should show the inserts after a ROLLBACK
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
||||||
data
|
data
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
streaming message: transactional: 1 prefix: test, sz: 50
|
streaming message: transactional: 1 prefix: test, sz: 50
|
||||||
@ -59,7 +59,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'two-
|
|||||||
|
|
||||||
COMMIT PREPARED 'test1';
|
COMMIT PREPARED 'test1';
|
||||||
--should show the COMMIT PREPARED and the other changes in the transaction
|
--should show the COMMIT PREPARED and the other changes in the transaction
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
||||||
data
|
data
|
||||||
-------------------------
|
-------------------------
|
||||||
COMMIT PREPARED 'test1'
|
COMMIT PREPARED 'test1'
|
||||||
@ -81,7 +81,7 @@ ROLLBACK to s1;
|
|||||||
INSERT INTO stream_test SELECT repeat('a', 10) || g.i FROM generate_series(1, 20) g(i);
|
INSERT INTO stream_test SELECT repeat('a', 10) || g.i FROM generate_series(1, 20) g(i);
|
||||||
PREPARE TRANSACTION 'test1_nodecode';
|
PREPARE TRANSACTION 'test1_nodecode';
|
||||||
-- should NOT show inserts after a ROLLBACK
|
-- should NOT show inserts after a ROLLBACK
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
||||||
data
|
data
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
streaming message: transactional: 1 prefix: test, sz: 50
|
streaming message: transactional: 1 prefix: test, sz: 50
|
||||||
@ -89,7 +89,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'two-
|
|||||||
|
|
||||||
COMMIT PREPARED 'test1_nodecode';
|
COMMIT PREPARED 'test1_nodecode';
|
||||||
-- should show the inserts but not show a COMMIT PREPARED but a COMMIT
|
-- should show the inserts but not show a COMMIT PREPARED but a COMMIT
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
||||||
data
|
data
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
BEGIN
|
BEGIN
|
||||||
|
@ -15,8 +15,8 @@ teardown
|
|||||||
session "s1"
|
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', false, true);}
|
||||||
step "s1start" {SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'skip-empty-xacts', '1', 'two-phase-commit', '1');}
|
step "s1start" {SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'skip-empty-xacts', '1');}
|
||||||
step "s1insert" { INSERT INTO do_write DEFAULT VALUES; }
|
step "s1insert" { INSERT INTO do_write DEFAULT VALUES; }
|
||||||
|
|
||||||
session "s2"
|
session "s2"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
-- Test prepared transactions. When two-phase-commit is enabled, transactions are
|
-- Test prepared transactions. When two-phase-commit is enabled, transactions are
|
||||||
-- decoded at PREPARE time rather than at COMMIT PREPARED time.
|
-- decoded at PREPARE time rather than at COMMIT PREPARED time.
|
||||||
SET synchronous_commit = on;
|
SET synchronous_commit = on;
|
||||||
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
|
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding', false, true);
|
||||||
|
|
||||||
CREATE TABLE test_prepared1(id integer primary key);
|
CREATE TABLE test_prepared1(id integer primary key);
|
||||||
CREATE TABLE test_prepared2(id integer primary key);
|
CREATE TABLE test_prepared2(id integer primary key);
|
||||||
@ -12,20 +12,20 @@ BEGIN;
|
|||||||
INSERT INTO test_prepared1 VALUES (1);
|
INSERT INTO test_prepared1 VALUES (1);
|
||||||
INSERT INTO test_prepared1 VALUES (2);
|
INSERT INTO test_prepared1 VALUES (2);
|
||||||
-- should show nothing because the xact has not been prepared yet.
|
-- should show nothing because the xact has not been prepared yet.
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
PREPARE TRANSACTION 'test_prepared#1';
|
PREPARE TRANSACTION 'test_prepared#1';
|
||||||
-- should show both the above inserts and the PREPARE TRANSACTION.
|
-- should show both the above inserts and the PREPARE TRANSACTION.
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
COMMIT PREPARED 'test_prepared#1';
|
COMMIT PREPARED 'test_prepared#1';
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- Test that rollback of a prepared xact is decoded.
|
-- Test that rollback of a prepared xact is decoded.
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO test_prepared1 VALUES (3);
|
INSERT INTO test_prepared1 VALUES (3);
|
||||||
PREPARE TRANSACTION 'test_prepared#2';
|
PREPARE TRANSACTION 'test_prepared#2';
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
ROLLBACK PREPARED 'test_prepared#2';
|
ROLLBACK PREPARED 'test_prepared#2';
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- Test prepare of a xact containing ddl. Leaving xact uncommitted for next test.
|
-- Test prepare of a xact containing ddl. Leaving xact uncommitted for next test.
|
||||||
BEGIN;
|
BEGIN;
|
||||||
@ -38,7 +38,7 @@ FROM pg_locks
|
|||||||
WHERE locktype = 'relation'
|
WHERE locktype = 'relation'
|
||||||
AND relation = 'test_prepared1'::regclass;
|
AND relation = 'test_prepared1'::regclass;
|
||||||
-- The insert should show the newly altered column but not the DDL.
|
-- The insert should show the newly altered column but not the DDL.
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- Test that we decode correctly while an uncommitted prepared xact
|
-- Test that we decode correctly while an uncommitted prepared xact
|
||||||
-- with ddl exists.
|
-- with ddl exists.
|
||||||
@ -47,14 +47,14 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two
|
|||||||
-- the ALTER will stop us inserting into the other one.
|
-- the ALTER will stop us inserting into the other one.
|
||||||
--
|
--
|
||||||
INSERT INTO test_prepared2 VALUES (5);
|
INSERT INTO test_prepared2 VALUES (5);
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
COMMIT PREPARED 'test_prepared#3';
|
COMMIT PREPARED 'test_prepared#3';
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
-- make sure stuff still works
|
-- make sure stuff still works
|
||||||
INSERT INTO test_prepared1 VALUES (6);
|
INSERT INTO test_prepared1 VALUES (6);
|
||||||
INSERT INTO test_prepared2 VALUES (7);
|
INSERT INTO test_prepared2 VALUES (7);
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- Check 'CLUSTER' (as operation that hold exclusive lock) doesn't block
|
-- Check 'CLUSTER' (as operation that hold exclusive lock) doesn't block
|
||||||
-- logical decoding.
|
-- logical decoding.
|
||||||
@ -70,11 +70,11 @@ WHERE locktype = 'relation'
|
|||||||
AND relation = 'test_prepared1'::regclass;
|
AND relation = 'test_prepared1'::regclass;
|
||||||
-- The above CLUSTER command shouldn't cause a timeout on 2pc decoding.
|
-- The above CLUSTER command shouldn't cause a timeout on 2pc decoding.
|
||||||
SET statement_timeout = '180s';
|
SET statement_timeout = '180s';
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
RESET statement_timeout;
|
RESET statement_timeout;
|
||||||
COMMIT PREPARED 'test_prepared_lock';
|
COMMIT PREPARED 'test_prepared_lock';
|
||||||
-- consume the commit
|
-- consume the commit
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- Test savepoints and sub-xacts. Creating savepoints will create
|
-- Test savepoints and sub-xacts. Creating savepoints will create
|
||||||
-- sub-xacts implicitly.
|
-- sub-xacts implicitly.
|
||||||
@ -86,26 +86,26 @@ INSERT INTO test_prepared_savepoint VALUES (2);
|
|||||||
ROLLBACK TO SAVEPOINT test_savepoint;
|
ROLLBACK TO SAVEPOINT test_savepoint;
|
||||||
PREPARE TRANSACTION 'test_prepared_savepoint';
|
PREPARE TRANSACTION 'test_prepared_savepoint';
|
||||||
-- should show only 1, not 2
|
-- should show only 1, not 2
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
COMMIT PREPARED 'test_prepared_savepoint';
|
COMMIT PREPARED 'test_prepared_savepoint';
|
||||||
-- consume the commit
|
-- consume the commit
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- Test that a GID containing "_nodecode" gets decoded at commit prepared time.
|
-- Test that a GID containing "_nodecode" gets decoded at commit prepared time.
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO test_prepared1 VALUES (20);
|
INSERT INTO test_prepared1 VALUES (20);
|
||||||
PREPARE TRANSACTION 'test_prepared_nodecode';
|
PREPARE TRANSACTION 'test_prepared_nodecode';
|
||||||
-- should show nothing
|
-- should show nothing
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
COMMIT PREPARED 'test_prepared_nodecode';
|
COMMIT PREPARED 'test_prepared_nodecode';
|
||||||
-- should be decoded now
|
-- should be decoded now
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||||
|
|
||||||
-- Test 8:
|
-- Test 8:
|
||||||
-- cleanup and make sure results are also empty
|
-- cleanup and make sure results are also empty
|
||||||
DROP TABLE test_prepared1;
|
DROP TABLE test_prepared1;
|
||||||
DROP TABLE test_prepared2;
|
DROP TABLE test_prepared2;
|
||||||
-- show results. There should be nothing to show
|
-- show results. There should be nothing to show
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1');
|
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');
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
-- Test streaming of two-phase commits
|
-- Test streaming of two-phase commits
|
||||||
|
|
||||||
SET synchronous_commit = on;
|
SET synchronous_commit = on;
|
||||||
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
|
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding', false, true);
|
||||||
|
|
||||||
CREATE TABLE stream_test(data text);
|
CREATE TABLE stream_test(data text);
|
||||||
|
|
||||||
@ -18,11 +18,11 @@ ROLLBACK TO s1;
|
|||||||
INSERT INTO stream_test SELECT repeat('a', 10) || g.i FROM generate_series(1, 20) g(i);
|
INSERT INTO stream_test SELECT repeat('a', 10) || g.i FROM generate_series(1, 20) g(i);
|
||||||
PREPARE TRANSACTION 'test1';
|
PREPARE TRANSACTION 'test1';
|
||||||
-- should show the inserts after a ROLLBACK
|
-- should show the inserts after a ROLLBACK
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
||||||
|
|
||||||
COMMIT PREPARED 'test1';
|
COMMIT PREPARED 'test1';
|
||||||
--should show the COMMIT PREPARED and the other changes in the transaction
|
--should show the COMMIT PREPARED and the other changes in the transaction
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
||||||
|
|
||||||
-- streaming test with sub-transaction and PREPARE/COMMIT PREPARED but with
|
-- streaming test with sub-transaction and PREPARE/COMMIT PREPARED but with
|
||||||
-- filtered gid. gids with '_nodecode' will not be decoded at prepare time.
|
-- filtered gid. gids with '_nodecode' will not be decoded at prepare time.
|
||||||
@ -35,11 +35,11 @@ ROLLBACK to s1;
|
|||||||
INSERT INTO stream_test SELECT repeat('a', 10) || g.i FROM generate_series(1, 20) g(i);
|
INSERT INTO stream_test SELECT repeat('a', 10) || g.i FROM generate_series(1, 20) g(i);
|
||||||
PREPARE TRANSACTION 'test1_nodecode';
|
PREPARE TRANSACTION 'test1_nodecode';
|
||||||
-- should NOT show inserts after a ROLLBACK
|
-- should NOT show inserts after a ROLLBACK
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
||||||
|
|
||||||
COMMIT PREPARED 'test1_nodecode';
|
COMMIT PREPARED 'test1_nodecode';
|
||||||
-- should show the inserts but not show a COMMIT PREPARED but a COMMIT
|
-- should show the inserts but not show a COMMIT PREPARED but a COMMIT
|
||||||
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'two-phase-commit', '1', 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
|
||||||
|
|
||||||
DROP TABLE stream_test;
|
DROP TABLE stream_test;
|
||||||
SELECT pg_drop_replication_slot('regression_slot');
|
SELECT pg_drop_replication_slot('regression_slot');
|
||||||
|
@ -164,7 +164,6 @@ pg_decode_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
|
|||||||
ListCell *option;
|
ListCell *option;
|
||||||
TestDecodingData *data;
|
TestDecodingData *data;
|
||||||
bool enable_streaming = false;
|
bool enable_streaming = false;
|
||||||
bool enable_twophase = false;
|
|
||||||
|
|
||||||
data = palloc0(sizeof(TestDecodingData));
|
data = palloc0(sizeof(TestDecodingData));
|
||||||
data->context = AllocSetContextCreate(ctx->context,
|
data->context = AllocSetContextCreate(ctx->context,
|
||||||
@ -265,16 +264,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, "two-phase-commit") == 0)
|
|
||||||
{
|
|
||||||
if (elem->arg == NULL)
|
|
||||||
continue;
|
|
||||||
else if (!parse_bool(strVal(elem->arg), &enable_twophase))
|
|
||||||
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,
|
||||||
@ -286,7 +275,6 @@ pg_decode_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx->streaming &= enable_streaming;
|
ctx->streaming &= enable_streaming;
|
||||||
ctx->twophase &= enable_twophase;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cleanup this plugin's resources */
|
/* cleanup this plugin's resources */
|
||||||
|
@ -11529,6 +11529,16 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
|
|||||||
is <literal>-1</literal>.
|
is <literal>-1</literal>.
|
||||||
</para></entry>
|
</para></entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry role="catalog_table_entry"><para role="column_definition">
|
||||||
|
<structfield>two_phase</structfield> <type>bool</type>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
True if the slot is enabled for decoding prepared transactions. Always
|
||||||
|
false for physical slots.
|
||||||
|
</para></entry>
|
||||||
|
</row>
|
||||||
</tbody>
|
</tbody>
|
||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</table>
|
||||||
|
@ -25559,7 +25559,7 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
|
|||||||
<indexterm>
|
<indexterm>
|
||||||
<primary>pg_create_logical_replication_slot</primary>
|
<primary>pg_create_logical_replication_slot</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
<function>pg_create_logical_replication_slot</function> ( <parameter>slot_name</parameter> <type>name</type>, <parameter>plugin</parameter> <type>name</type> <optional>, <parameter>temporary</parameter> <type>boolean</type> </optional> )
|
<function>pg_create_logical_replication_slot</function> ( <parameter>slot_name</parameter> <type>name</type>, <parameter>plugin</parameter> <type>name</type> <optional>, <parameter>temporary</parameter> <type>boolean</type>, <parameter>two_phase</parameter> <type>boolean</type> </optional> )
|
||||||
<returnvalue>record</returnvalue>
|
<returnvalue>record</returnvalue>
|
||||||
( <parameter>slot_name</parameter> <type>name</type>,
|
( <parameter>slot_name</parameter> <type>name</type>,
|
||||||
<parameter>lsn</parameter> <type>pg_lsn</type> )
|
<parameter>lsn</parameter> <type>pg_lsn</type> )
|
||||||
@ -25571,9 +25571,11 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
|
|||||||
parameter, <parameter>temporary</parameter>, when set to true, specifies that
|
parameter, <parameter>temporary</parameter>, when set to true, specifies that
|
||||||
the slot should not be permanently stored to disk and is only meant
|
the slot should not be permanently stored to disk and is only meant
|
||||||
for use by the current session. Temporary slots are also
|
for use by the current session. Temporary slots are also
|
||||||
released upon any error. A call to this function has the same
|
released upon any error. The optional fourth parameter,
|
||||||
effect as the replication protocol command
|
<parameter>two_phase</parameter>, when set to true, specifies
|
||||||
<literal>CREATE_REPLICATION_SLOT ... LOGICAL</literal>.
|
that the decoding of prepared transactions is enabled for this
|
||||||
|
slot. A call to this function has the same effect as the replication
|
||||||
|
protocol command <literal>CREATE_REPLICATION_SLOT ... LOGICAL</literal>.
|
||||||
</para></entry>
|
</para></entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
postgres=# -- Create a slot named 'regression_slot' using the output plugin 'test_decoding'
|
postgres=# -- Create a slot named 'regression_slot' using the output plugin 'test_decoding'
|
||||||
postgres=# SELECT * FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
|
postgres=# SELECT * FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding', false, true);
|
||||||
slot_name | lsn
|
slot_name | lsn
|
||||||
-----------------+-----------
|
-----------------+-----------
|
||||||
regression_slot | 0/16B1970
|
regression_slot | 0/16B1970
|
||||||
@ -169,17 +169,18 @@ $ pg_recvlogical -d postgres --slot=test --drop-slot
|
|||||||
<para>
|
<para>
|
||||||
The following example shows SQL interface that can be used to decode prepared
|
The following example shows SQL interface that can be used to decode prepared
|
||||||
transactions. Before you use two-phase commit commands, you must set
|
transactions. Before you use two-phase commit commands, you must set
|
||||||
<varname>max_prepared_transactions</varname> to at least 1. You must also set
|
<varname>max_prepared_transactions</varname> to at least 1. You must also have
|
||||||
the option 'two-phase-commit' to 1 while calling
|
set the two-phase parameter as 'true' while creating the slot using
|
||||||
<function>pg_logical_slot_get_changes</function>. Note that we will stream
|
<function>pg_create_logical_replication_slot</function>
|
||||||
the entire transaction after the commit if it is not already decoded.
|
Note that we will stream the entire transaction after the commit if it
|
||||||
|
is not already decoded.
|
||||||
</para>
|
</para>
|
||||||
<programlisting>
|
<programlisting>
|
||||||
postgres=# BEGIN;
|
postgres=# BEGIN;
|
||||||
postgres=*# INSERT INTO data(data) VALUES('5');
|
postgres=*# INSERT INTO data(data) VALUES('5');
|
||||||
postgres=*# PREPARE TRANSACTION 'test_prepared1';
|
postgres=*# PREPARE TRANSACTION 'test_prepared1';
|
||||||
|
|
||||||
postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1');
|
postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL);
|
||||||
lsn | xid | data
|
lsn | xid | data
|
||||||
-----------+-----+---------------------------------------------------------
|
-----------+-----+---------------------------------------------------------
|
||||||
0/1689DC0 | 529 | BEGIN 529
|
0/1689DC0 | 529 | BEGIN 529
|
||||||
@ -188,7 +189,7 @@ postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NU
|
|||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
postgres=# COMMIT PREPARED 'test_prepared1';
|
postgres=# COMMIT PREPARED 'test_prepared1';
|
||||||
postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1');
|
postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NULL);
|
||||||
lsn | xid | data
|
lsn | xid | data
|
||||||
-----------+-----+--------------------------------------------
|
-----------+-----+--------------------------------------------
|
||||||
0/168A060 | 529 | COMMIT PREPARED 'test_prepared1', txid 529
|
0/168A060 | 529 | COMMIT PREPARED 'test_prepared1', txid 529
|
||||||
@ -198,7 +199,7 @@ postgres=#-- you can also rollback a prepared transaction
|
|||||||
postgres=# BEGIN;
|
postgres=# BEGIN;
|
||||||
postgres=*# INSERT INTO data(data) VALUES('6');
|
postgres=*# INSERT INTO data(data) VALUES('6');
|
||||||
postgres=*# PREPARE TRANSACTION 'test_prepared2';
|
postgres=*# PREPARE TRANSACTION 'test_prepared2';
|
||||||
postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1');
|
postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NULL);
|
||||||
lsn | xid | data
|
lsn | xid | data
|
||||||
-----------+-----+---------------------------------------------------------
|
-----------+-----+---------------------------------------------------------
|
||||||
0/168A180 | 530 | BEGIN 530
|
0/168A180 | 530 | BEGIN 530
|
||||||
@ -207,7 +208,7 @@ postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NU
|
|||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
postgres=# ROLLBACK PREPARED 'test_prepared2';
|
postgres=# ROLLBACK PREPARED 'test_prepared2';
|
||||||
postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1');
|
postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NULL);
|
||||||
lsn | xid | data
|
lsn | xid | data
|
||||||
-----------+-----+----------------------------------------------
|
-----------+-----+----------------------------------------------
|
||||||
0/168A4B8 | 530 | ROLLBACK PREPARED 'test_prepared2', txid 530
|
0/168A4B8 | 530 | ROLLBACK PREPARED 'test_prepared2', txid 530
|
||||||
|
@ -894,7 +894,8 @@ CREATE VIEW pg_replication_slots AS
|
|||||||
L.restart_lsn,
|
L.restart_lsn,
|
||||||
L.confirmed_flush_lsn,
|
L.confirmed_flush_lsn,
|
||||||
L.wal_status,
|
L.wal_status,
|
||||||
L.safe_wal_size
|
L.safe_wal_size,
|
||||||
|
L.two_phase
|
||||||
FROM pg_get_replication_slots() AS L
|
FROM pg_get_replication_slots() AS L
|
||||||
LEFT JOIN pg_database D ON (L.datoid = D.oid);
|
LEFT JOIN pg_database D ON (L.datoid = D.oid);
|
||||||
|
|
||||||
@ -1318,6 +1319,7 @@ AS 'pg_create_physical_replication_slot';
|
|||||||
CREATE OR REPLACE FUNCTION pg_create_logical_replication_slot(
|
CREATE OR REPLACE FUNCTION pg_create_logical_replication_slot(
|
||||||
IN slot_name name, IN plugin name,
|
IN slot_name name, IN plugin name,
|
||||||
IN temporary boolean DEFAULT false,
|
IN temporary boolean DEFAULT false,
|
||||||
|
IN twophase boolean DEFAULT false,
|
||||||
OUT slot_name name, OUT lsn pg_lsn)
|
OUT slot_name name, OUT lsn pg_lsn)
|
||||||
RETURNS RECORD
|
RETURNS RECORD
|
||||||
LANGUAGE INTERNAL
|
LANGUAGE INTERNAL
|
||||||
|
@ -431,6 +431,12 @@ CreateInitDecodingContext(const char *plugin,
|
|||||||
startup_cb_wrapper(ctx, &ctx->options, true);
|
startup_cb_wrapper(ctx, &ctx->options, true);
|
||||||
MemoryContextSwitchTo(old_context);
|
MemoryContextSwitchTo(old_context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We allow decoding of prepared transactions iff the two_phase option is
|
||||||
|
* enabled at the time of slot creation.
|
||||||
|
*/
|
||||||
|
ctx->twophase &= MyReplicationSlot->data.two_phase;
|
||||||
|
|
||||||
ctx->reorder->output_rewrites = ctx->options.receive_rewrites;
|
ctx->reorder->output_rewrites = ctx->options.receive_rewrites;
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
@ -531,6 +537,12 @@ CreateDecodingContext(XLogRecPtr start_lsn,
|
|||||||
startup_cb_wrapper(ctx, &ctx->options, false);
|
startup_cb_wrapper(ctx, &ctx->options, false);
|
||||||
MemoryContextSwitchTo(old_context);
|
MemoryContextSwitchTo(old_context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We allow decoding of prepared transactions iff the two_phase option is
|
||||||
|
* enabled at the time of slot creation.
|
||||||
|
*/
|
||||||
|
ctx->twophase &= MyReplicationSlot->data.two_phase;
|
||||||
|
|
||||||
ctx->reorder->output_rewrites = ctx->options.receive_rewrites;
|
ctx->reorder->output_rewrites = ctx->options.receive_rewrites;
|
||||||
|
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
|
@ -216,10 +216,17 @@ ReplicationSlotValidateName(const char *name, int elevel)
|
|||||||
* name: Name of the slot
|
* name: Name of the slot
|
||||||
* db_specific: logical decoding is db specific; if the slot is going to
|
* db_specific: logical decoding is db specific; if the slot is going to
|
||||||
* be used for that pass true, otherwise false.
|
* be used for that pass true, otherwise false.
|
||||||
|
* two_phase: Allows decoding of prepared transactions. We allow this option
|
||||||
|
* to be enabled only at the slot creation time. If we allow this option
|
||||||
|
* to be changed during decoding then it is quite possible that we skip
|
||||||
|
* prepare first time because this option was not enabled. Now next time
|
||||||
|
* during getting changes, if the two_phase option is enabled it can skip
|
||||||
|
* prepare because by that time start decoding point has been moved. So the
|
||||||
|
* user will only get commit prepared.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ReplicationSlotCreate(const char *name, bool db_specific,
|
ReplicationSlotCreate(const char *name, bool db_specific,
|
||||||
ReplicationSlotPersistency persistency)
|
ReplicationSlotPersistency persistency, bool two_phase)
|
||||||
{
|
{
|
||||||
ReplicationSlot *slot = NULL;
|
ReplicationSlot *slot = NULL;
|
||||||
int i;
|
int i;
|
||||||
@ -277,6 +284,7 @@ ReplicationSlotCreate(const char *name, bool db_specific,
|
|||||||
namestrcpy(&slot->data.name, name);
|
namestrcpy(&slot->data.name, name);
|
||||||
slot->data.database = db_specific ? MyDatabaseId : InvalidOid;
|
slot->data.database = db_specific ? MyDatabaseId : InvalidOid;
|
||||||
slot->data.persistency = persistency;
|
slot->data.persistency = persistency;
|
||||||
|
slot->data.two_phase = two_phase;
|
||||||
|
|
||||||
/* and then data only present in shared memory */
|
/* and then data only present in shared memory */
|
||||||
slot->just_dirtied = false;
|
slot->just_dirtied = false;
|
||||||
|
@ -50,7 +50,7 @@ create_physical_replication_slot(char *name, bool immediately_reserve,
|
|||||||
|
|
||||||
/* acquire replication slot, this will check for conflicting names */
|
/* acquire replication slot, this will check for conflicting names */
|
||||||
ReplicationSlotCreate(name, false,
|
ReplicationSlotCreate(name, false,
|
||||||
temporary ? RS_TEMPORARY : RS_PERSISTENT);
|
temporary ? RS_TEMPORARY : RS_PERSISTENT, false);
|
||||||
|
|
||||||
if (immediately_reserve)
|
if (immediately_reserve)
|
||||||
{
|
{
|
||||||
@ -124,7 +124,8 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
create_logical_replication_slot(char *name, char *plugin,
|
create_logical_replication_slot(char *name, char *plugin,
|
||||||
bool temporary, XLogRecPtr restart_lsn,
|
bool temporary, bool two_phase,
|
||||||
|
XLogRecPtr restart_lsn,
|
||||||
bool find_startpoint)
|
bool find_startpoint)
|
||||||
{
|
{
|
||||||
LogicalDecodingContext *ctx = NULL;
|
LogicalDecodingContext *ctx = NULL;
|
||||||
@ -140,7 +141,7 @@ create_logical_replication_slot(char *name, char *plugin,
|
|||||||
* error as well.
|
* error as well.
|
||||||
*/
|
*/
|
||||||
ReplicationSlotCreate(name, true,
|
ReplicationSlotCreate(name, true,
|
||||||
temporary ? RS_TEMPORARY : RS_EPHEMERAL);
|
temporary ? RS_TEMPORARY : RS_EPHEMERAL, two_phase);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create logical decoding context to find start point or, if we don't
|
* Create logical decoding context to find start point or, if we don't
|
||||||
@ -177,6 +178,7 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
|
|||||||
Name name = PG_GETARG_NAME(0);
|
Name name = PG_GETARG_NAME(0);
|
||||||
Name plugin = PG_GETARG_NAME(1);
|
Name plugin = PG_GETARG_NAME(1);
|
||||||
bool temporary = PG_GETARG_BOOL(2);
|
bool temporary = PG_GETARG_BOOL(2);
|
||||||
|
bool two_phase = PG_GETARG_BOOL(3);
|
||||||
Datum result;
|
Datum result;
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
@ -193,6 +195,7 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
|
|||||||
create_logical_replication_slot(NameStr(*name),
|
create_logical_replication_slot(NameStr(*name),
|
||||||
NameStr(*plugin),
|
NameStr(*plugin),
|
||||||
temporary,
|
temporary,
|
||||||
|
two_phase,
|
||||||
InvalidXLogRecPtr,
|
InvalidXLogRecPtr,
|
||||||
true);
|
true);
|
||||||
|
|
||||||
@ -236,7 +239,7 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
|
|||||||
Datum
|
Datum
|
||||||
pg_get_replication_slots(PG_FUNCTION_ARGS)
|
pg_get_replication_slots(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
#define PG_GET_REPLICATION_SLOTS_COLS 13
|
#define PG_GET_REPLICATION_SLOTS_COLS 14
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
Tuplestorestate *tupstore;
|
Tuplestorestate *tupstore;
|
||||||
@ -432,6 +435,8 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
|
|||||||
values[i++] = Int64GetDatum(failLSN - currlsn);
|
values[i++] = Int64GetDatum(failLSN - currlsn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
values[i++] = BoolGetDatum(slot_contents.data.two_phase);
|
||||||
|
|
||||||
Assert(i == PG_GET_REPLICATION_SLOTS_COLS);
|
Assert(i == PG_GET_REPLICATION_SLOTS_COLS);
|
||||||
|
|
||||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||||
@ -796,6 +801,7 @@ copy_replication_slot(FunctionCallInfo fcinfo, bool logical_slot)
|
|||||||
create_logical_replication_slot(NameStr(*dst_name),
|
create_logical_replication_slot(NameStr(*dst_name),
|
||||||
plugin,
|
plugin,
|
||||||
temporary,
|
temporary,
|
||||||
|
false,
|
||||||
src_restart_lsn,
|
src_restart_lsn,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
@ -938,7 +938,8 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
|
|||||||
if (cmd->kind == REPLICATION_KIND_PHYSICAL)
|
if (cmd->kind == REPLICATION_KIND_PHYSICAL)
|
||||||
{
|
{
|
||||||
ReplicationSlotCreate(cmd->slotname, false,
|
ReplicationSlotCreate(cmd->slotname, false,
|
||||||
cmd->temporary ? RS_TEMPORARY : RS_PERSISTENT);
|
cmd->temporary ? RS_TEMPORARY : RS_PERSISTENT,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -952,7 +953,8 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
|
|||||||
* they get dropped on error as well.
|
* they get dropped on error as well.
|
||||||
*/
|
*/
|
||||||
ReplicationSlotCreate(cmd->slotname, true,
|
ReplicationSlotCreate(cmd->slotname, true,
|
||||||
cmd->temporary ? RS_TEMPORARY : RS_EPHEMERAL);
|
cmd->temporary ? RS_TEMPORARY : RS_EPHEMERAL,
|
||||||
|
cmd->two_phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->kind == REPLICATION_KIND_LOGICAL)
|
if (cmd->kind == REPLICATION_KIND_LOGICAL)
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202102191
|
#define CATALOG_VERSION_NO 202103031
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -10496,16 +10496,16 @@
|
|||||||
proname => 'pg_get_replication_slots', prorows => '10', proisstrict => 'f',
|
proname => 'pg_get_replication_slots', prorows => '10', proisstrict => 'f',
|
||||||
proretset => 't', provolatile => 's', prorettype => 'record',
|
proretset => 't', provolatile => 's', prorettype => 'record',
|
||||||
proargtypes => '',
|
proargtypes => '',
|
||||||
proallargtypes => '{name,name,text,oid,bool,bool,int4,xid,xid,pg_lsn,pg_lsn,text,int8}',
|
proallargtypes => '{name,name,text,oid,bool,bool,int4,xid,xid,pg_lsn,pg_lsn,text,int8,bool}',
|
||||||
proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o}',
|
proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
|
||||||
proargnames => '{slot_name,plugin,slot_type,datoid,temporary,active,active_pid,xmin,catalog_xmin,restart_lsn,confirmed_flush_lsn,wal_status,safe_wal_size}',
|
proargnames => '{slot_name,plugin,slot_type,datoid,temporary,active,active_pid,xmin,catalog_xmin,restart_lsn,confirmed_flush_lsn,wal_status,safe_wal_size,two_phase}',
|
||||||
prosrc => 'pg_get_replication_slots' },
|
prosrc => 'pg_get_replication_slots' },
|
||||||
{ oid => '3786', descr => 'set up a logical replication slot',
|
{ oid => '3786', descr => 'set up a logical replication slot',
|
||||||
proname => 'pg_create_logical_replication_slot', provolatile => 'v',
|
proname => 'pg_create_logical_replication_slot', provolatile => 'v',
|
||||||
proparallel => 'u', prorettype => 'record', proargtypes => 'name name bool',
|
proparallel => 'u', prorettype => 'record', proargtypes => 'name name bool bool',
|
||||||
proallargtypes => '{name,name,bool,name,pg_lsn}',
|
proallargtypes => '{name,name,bool,bool,name,pg_lsn}',
|
||||||
proargmodes => '{i,i,i,o,o}',
|
proargmodes => '{i,i,i,i,o,o}',
|
||||||
proargnames => '{slot_name,plugin,temporary,slot_name,lsn}',
|
proargnames => '{slot_name,plugin,temporary,twophase,slot_name,lsn}',
|
||||||
prosrc => 'pg_create_logical_replication_slot' },
|
prosrc => 'pg_create_logical_replication_slot' },
|
||||||
{ oid => '4222',
|
{ oid => '4222',
|
||||||
descr => 'copy a logical replication slot, changing temporality and plugin',
|
descr => 'copy a logical replication slot, changing temporality and plugin',
|
||||||
|
@ -56,6 +56,7 @@ typedef struct CreateReplicationSlotCmd
|
|||||||
ReplicationKind kind;
|
ReplicationKind kind;
|
||||||
char *plugin;
|
char *plugin;
|
||||||
bool temporary;
|
bool temporary;
|
||||||
|
bool two_phase;
|
||||||
List *options;
|
List *options;
|
||||||
} CreateReplicationSlotCmd;
|
} CreateReplicationSlotCmd;
|
||||||
|
|
||||||
|
@ -98,6 +98,11 @@ typedef struct ReplicationSlotPersistentData
|
|||||||
*/
|
*/
|
||||||
XLogRecPtr initial_consistent_point;
|
XLogRecPtr initial_consistent_point;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allow decoding of prepared transactions?
|
||||||
|
*/
|
||||||
|
bool two_phase;
|
||||||
|
|
||||||
/* plugin name */
|
/* plugin name */
|
||||||
NameData plugin;
|
NameData plugin;
|
||||||
} ReplicationSlotPersistentData;
|
} ReplicationSlotPersistentData;
|
||||||
@ -199,7 +204,7 @@ extern void ReplicationSlotsShmemInit(void);
|
|||||||
|
|
||||||
/* management of individual slots */
|
/* management of individual slots */
|
||||||
extern void ReplicationSlotCreate(const char *name, bool db_specific,
|
extern void ReplicationSlotCreate(const char *name, bool db_specific,
|
||||||
ReplicationSlotPersistency p);
|
ReplicationSlotPersistency p, bool two_phase);
|
||||||
extern void ReplicationSlotPersist(void);
|
extern void ReplicationSlotPersist(void);
|
||||||
extern void ReplicationSlotDrop(const char *name, bool nowait);
|
extern void ReplicationSlotDrop(const char *name, bool nowait);
|
||||||
|
|
||||||
|
@ -1477,8 +1477,9 @@ pg_replication_slots| SELECT l.slot_name,
|
|||||||
l.restart_lsn,
|
l.restart_lsn,
|
||||||
l.confirmed_flush_lsn,
|
l.confirmed_flush_lsn,
|
||||||
l.wal_status,
|
l.wal_status,
|
||||||
l.safe_wal_size
|
l.safe_wal_size,
|
||||||
FROM (pg_get_replication_slots() l(slot_name, plugin, slot_type, datoid, temporary, active, active_pid, xmin, catalog_xmin, restart_lsn, confirmed_flush_lsn, wal_status, safe_wal_size)
|
l.two_phase
|
||||||
|
FROM (pg_get_replication_slots() l(slot_name, plugin, slot_type, datoid, temporary, active, active_pid, xmin, catalog_xmin, restart_lsn, confirmed_flush_lsn, wal_status, safe_wal_size, two_phase)
|
||||||
LEFT JOIN pg_database d ON ((l.datoid = d.oid)));
|
LEFT JOIN pg_database d ON ((l.datoid = d.oid)));
|
||||||
pg_roles| SELECT pg_authid.rolname,
|
pg_roles| SELECT pg_authid.rolname,
|
||||||
pg_authid.rolsuper,
|
pg_authid.rolsuper,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user