From 2de8ef29c335ec2e4ccdf934b52aa1baf24160f6 Mon Sep 17 00:00:00 2001 From: Marco Slot Date: Wed, 7 Nov 2018 11:16:42 +0100 Subject: [PATCH] Revoke function permissions for node metadata functions --- src/backend/distributed/Makefile | 4 +- .../distributed/citus--8.0-8--8.0-9.sql | 13 ++ src/backend/distributed/citus.control | 2 +- src/backend/distributed/utils/node_metadata.c | 4 - .../expected/multi_cluster_management.out | 124 ++++++++++++++---- .../expected/multi_mx_modifications.out | 2 +- .../multi_mx_transaction_recovery.out | 14 +- .../regress/sql/multi_cluster_management.sql | 36 ++++- .../sql/multi_mx_transaction_recovery.sql | 12 +- 9 files changed, 165 insertions(+), 46 deletions(-) create mode 100644 src/backend/distributed/citus--8.0-8--8.0-9.sql diff --git a/src/backend/distributed/Makefile b/src/backend/distributed/Makefile index 4b7f93ca5..764065655 100644 --- a/src/backend/distributed/Makefile +++ b/src/backend/distributed/Makefile @@ -17,7 +17,7 @@ EXTVERSIONS = 5.0 5.0-1 5.0-2 \ 7.3-1 7.3-2 7.3-3 \ 7.4-1 7.4-2 7.4-3 \ 7.5-1 7.5-2 7.5-3 7.5-4 7.5-5 7.5-6 7.5-7 \ - 8.0-1 8.0-2 8.0-3 8.0-4 8.0-5 8.0-6 8.0-7 8.0-8 + 8.0-1 8.0-2 8.0-3 8.0-4 8.0-5 8.0-6 8.0-7 8.0-8 8.0-9 # All citus--*.sql files in the source directory DATA = $(patsubst $(citus_abs_srcdir)/%.sql,%.sql,$(wildcard $(citus_abs_srcdir)/$(EXTENSION)--*--*.sql)) @@ -231,6 +231,8 @@ $(EXTENSION)--8.0-7.sql: $(EXTENSION)--8.0-6.sql $(EXTENSION)--8.0-6--8.0-7.sql cat $^ > $@ $(EXTENSION)--8.0-8.sql: $(EXTENSION)--8.0-7.sql $(EXTENSION)--8.0-7--8.0-8.sql cat $^ > $@ +$(EXTENSION)--8.0-9.sql: $(EXTENSION)--8.0-8.sql $(EXTENSION)--8.0-8--8.0-9.sql + cat $^ > $@ NO_PGXS = 1 diff --git a/src/backend/distributed/citus--8.0-8--8.0-9.sql b/src/backend/distributed/citus--8.0-8--8.0-9.sql new file mode 100644 index 000000000..7acd39705 --- /dev/null +++ b/src/backend/distributed/citus--8.0-8--8.0-9.sql @@ -0,0 +1,13 @@ +/* citus--8.0-8--8.0-9 */ +SET search_path = 'pg_catalog'; + +REVOKE ALL ON FUNCTION master_activate_node(text,int) FROM PUBLIC; +REVOKE ALL ON FUNCTION master_add_inactive_node(text,int,int,noderole,name) FROM PUBLIC; +REVOKE ALL ON FUNCTION master_add_node(text,int,int,noderole,name) FROM PUBLIC; +REVOKE ALL ON FUNCTION master_add_secondary_node(text,int,text,int,name) FROM PUBLIC; +REVOKE ALL ON FUNCTION master_disable_node(text,int) FROM PUBLIC; +REVOKE ALL ON FUNCTION master_initialize_node_metadata() FROM PUBLIC; +REVOKE ALL ON FUNCTION master_remove_node(text,int) FROM PUBLIC; +REVOKE ALL ON FUNCTION master_update_node(int,text,int) FROM PUBLIC; + +RESET search_path; diff --git a/src/backend/distributed/citus.control b/src/backend/distributed/citus.control index 86f89c819..12f9faa74 100644 --- a/src/backend/distributed/citus.control +++ b/src/backend/distributed/citus.control @@ -1,6 +1,6 @@ # Citus extension comment = 'Citus distributed database' -default_version = '8.0-8' +default_version = '8.0-9' module_pathname = '$libdir/citus' relocatable = false schema = pg_catalog diff --git a/src/backend/distributed/utils/node_metadata.c b/src/backend/distributed/utils/node_metadata.c index d50d746ac..1272245ee 100644 --- a/src/backend/distributed/utils/node_metadata.c +++ b/src/backend/distributed/utils/node_metadata.c @@ -257,7 +257,6 @@ master_disable_node(PG_FUNCTION_ARGS) CheckCitusVersion(ERROR); EnsureCoordinator(); - EnsureSuperUser(); /* take an exclusive lock on pg_dist_node to serialize pg_dist_node changes */ LockRelationOid(DistNodeRelationId(), ExclusiveLock); @@ -305,7 +304,6 @@ master_activate_node(PG_FUNCTION_ARGS) CheckCitusVersion(ERROR); EnsureCoordinator(); - EnsureSuperUser(); nodeRecord = ActivateNode(nodeNameString, nodePort); @@ -838,7 +836,6 @@ RemoveNodeFromCluster(char *nodeName, int32 nodePort) uint32 deletedNodeId = INVALID_PLACEMENT_ID; EnsureCoordinator(); - EnsureSuperUser(); /* take an exclusive lock on pg_dist_node to serialize pg_dist_node changes */ LockRelationOid(DistNodeRelationId(), ExclusiveLock); @@ -943,7 +940,6 @@ AddNodeMetadata(char *nodeName, int32 nodePort, int32 groupId, char *nodeRack, uint32 primariesWithMetadata = 0; EnsureCoordinator(); - EnsureSuperUser(); *nodeAlreadyExists = false; diff --git a/src/test/regress/expected/multi_cluster_management.out b/src/test/regress/expected/multi_cluster_management.out index b61abaa10..98d413f85 100644 --- a/src/test/regress/expected/multi_cluster_management.out +++ b/src/test/regress/expected/multi_cluster_management.out @@ -135,14 +135,92 @@ SELECT master_get_active_worker_nodes(); -- try to disable a node which does not exist and see that an error is thrown SELECT master_disable_node('localhost.noexist', 2345); ERROR: node at "localhost.noexist:2345" does not exist --- try to disable a node via non-super user CREATE USER non_super_user; NOTICE: not propagating CREATE ROLE/USER commands to worker nodes HINT: Connect to worker nodes directly to manually create all necessary users and roles. -\c - non_super_user - :master_port -SELECT master_disable_node('localhost', :worker_1_port); -ERROR: operation is not allowed -HINT: Run the command with a superuser. +CREATE USER node_metadata_user; +NOTICE: not propagating CREATE ROLE/USER commands to worker nodes +HINT: Connect to worker nodes directly to manually create all necessary users and roles. +GRANT EXECUTE ON FUNCTION master_activate_node(text,int) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_add_inactive_node(text,int,int,noderole,name) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_add_node(text,int,int,noderole,name) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_add_secondary_node(text,int,text,int,name) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_disable_node(text,int) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_remove_node(text,int) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_update_node(int,text,int) TO node_metadata_user; +-- try to manipulate node metadata via non-super user +SET ROLE non_super_user; +SELECT 1 FROM master_initialize_node_metadata(); +ERROR: permission denied for function master_initialize_node_metadata +SELECT 1 FROM master_add_inactive_node('localhost', :worker_2_port + 1); +ERROR: permission denied for function master_add_inactive_node +SELECT 1 FROM master_activate_node('localhost', :worker_2_port + 1); +ERROR: permission denied for function master_activate_node +SELECT 1 FROM master_disable_node('localhost', :worker_2_port + 1); +ERROR: permission denied for function master_disable_node +SELECT 1 FROM master_remove_node('localhost', :worker_2_port + 1); +ERROR: permission denied for function master_remove_node +SELECT 1 FROM master_add_node('localhost', :worker_2_port + 1); +ERROR: permission denied for function master_add_node +SELECT 1 FROM master_add_secondary_node('localhost', :worker_2_port + 2, 'localhost', :worker_2_port); +ERROR: permission denied for function master_add_secondary_node +SELECT master_update_node(nodeid, 'localhost', :worker_2_port + 3) FROM pg_dist_node WHERE nodeport = :worker_2_port; +ERROR: permission denied for function master_update_node +-- try to manipulate node metadata via privileged user +SET ROLE node_metadata_user; +BEGIN; +SELECT 1 FROM master_add_inactive_node('localhost', :worker_2_port + 1); + ?column? +---------- + 1 +(1 row) + +SELECT 1 FROM master_activate_node('localhost', :worker_2_port + 1); + ?column? +---------- + 1 +(1 row) + +SELECT 1 FROM master_disable_node('localhost', :worker_2_port + 1); + ?column? +---------- + 1 +(1 row) + +SELECT 1 FROM master_remove_node('localhost', :worker_2_port + 1); + ?column? +---------- + 1 +(1 row) + +SELECT 1 FROM master_add_node('localhost', :worker_2_port + 1); + ?column? +---------- + 1 +(1 row) + +SELECT 1 FROM master_add_secondary_node('localhost', :worker_2_port + 2, 'localhost', :worker_2_port); + ?column? +---------- + 1 +(1 row) + +SELECT master_update_node(nodeid, 'localhost', :worker_2_port + 3) FROM pg_dist_node WHERE nodeport = :worker_2_port; + master_update_node +-------------------- + +(1 row) + +SELECT nodename, nodeport, noderole FROM pg_dist_node ORDER BY nodeport; + nodename | nodeport | noderole +-----------+----------+----------- + localhost | 57637 | primary + localhost | 57639 | primary + localhost | 57640 | secondary + localhost | 57641 | primary +(4 rows) + +ABORT; \c - postgres - :master_port SELECT master_get_active_worker_nodes(); master_get_active_worker_nodes @@ -291,16 +369,16 @@ SELECT count(1) FROM pg_dist_node; SELECT master_add_node('localhost', :worker_1_port), master_add_node('localhost', :worker_2_port); - master_add_node | master_add_node ----------------------------------------------------+--------------------------------------------------- - (8,7,localhost,57637,default,f,t,primary,default) | (9,8,localhost,57638,default,f,t,primary,default) + master_add_node | master_add_node +----------------------------------------------------+----------------------------------------------------- + (11,9,localhost,57637,default,f,t,primary,default) | (12,10,localhost,57638,default,f,t,primary,default) (1 row) SELECT * FROM pg_dist_node ORDER BY nodeid; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster --------+---------+-----------+----------+----------+-------------+----------+----------+------------- - 8 | 7 | localhost | 57637 | default | f | t | primary | default - 9 | 8 | localhost | 57638 | default | f | t | primary | default + 11 | 9 | localhost | 57637 | default | f | t | primary | default + 12 | 10 | localhost | 57638 | default | f | t | primary | default (2 rows) -- check that mixed add/remove node commands work fine inside transaction @@ -454,7 +532,7 @@ ERROR: primaries must be added to the default cluster -- check that you can't add more than one primary to a group SELECT groupid AS worker_1_group FROM pg_dist_node WHERE nodeport = :worker_1_port \gset SELECT master_add_node('localhost', 9999, groupid => :worker_1_group, noderole => 'primary'); -ERROR: group 12 already has a primary node +ERROR: group 14 already has a primary node -- check that you can add secondaries and unavailable nodes to a group SELECT groupid AS worker_2_group FROM pg_dist_node WHERE nodeport = :worker_2_port \gset SELECT 1 FROM master_add_node('localhost', 9998, groupid => :worker_1_group, noderole => 'secondary'); @@ -480,13 +558,13 @@ SELECT 1 FROM master_add_inactive_node('localhost', 9996, groupid => :worker_2_g SELECT master_add_inactive_node('localhost', 9999, groupid => :worker_2_group, nodecluster => 'olap', noderole => 'secondary'); master_add_inactive_node --------------------------------------------------- - (19,14,localhost,9999,default,f,f,secondary,olap) + (22,16,localhost,9999,default,f,f,secondary,olap) (1 row) SELECT master_activate_node('localhost', 9999); master_activate_node --------------------------------------------------- - (19,14,localhost,9999,default,f,t,secondary,olap) + (22,16,localhost,9999,default,f,t,secondary,olap) (1 row) SELECT master_disable_node('localhost', 9999); @@ -514,17 +592,17 @@ CONTEXT: PL/pgSQL function citus.pg_dist_node_trigger_func() line 18 at RAISE INSERT INTO pg_dist_node (nodename, nodeport, groupid, noderole, nodecluster) VALUES ('localhost', 5000, 1000, 'primary', 'olap'); ERROR: new row for relation "pg_dist_node" violates check constraint "primaries_are_only_allowed_in_the_default_cluster" -DETAIL: Failing row contains (17, 1000, localhost, 5000, default, f, t, primary, olap). +DETAIL: Failing row contains (19, 1000, localhost, 5000, default, f, t, primary, olap). UPDATE pg_dist_node SET nodecluster = 'olap' WHERE nodeport = :worker_1_port; ERROR: new row for relation "pg_dist_node" violates check constraint "primaries_are_only_allowed_in_the_default_cluster" -DETAIL: Failing row contains (13, 12, localhost, 57637, default, f, t, primary, olap). +DETAIL: Failing row contains (16, 14, localhost, 57637, default, f, t, primary, olap). -- check that you /can/ add a secondary node to a non-default cluster SELECT groupid AS worker_2_group FROM pg_dist_node WHERE nodeport = :worker_2_port \gset SELECT master_add_node('localhost', 8888, groupid => :worker_1_group, noderole => 'secondary', nodecluster=> 'olap'); master_add_node --------------------------------------------------- - (20,12,localhost,8888,default,f,t,secondary,olap) + (23,14,localhost,8888,default,f,t,secondary,olap) (1 row) -- check that super-long cluster names are truncated @@ -537,13 +615,13 @@ SELECT master_add_node('localhost', 8887, groupid => :worker_1_group, noderole = ); master_add_node -------------------------------------------------------------------------------------------------------------- - (21,12,localhost,8887,default,f,t,secondary,thisisasixtyfourcharacterstringrepeatedfourtimestomake256chars.) + (24,14,localhost,8887,default,f,t,secondary,thisisasixtyfourcharacterstringrepeatedfourtimestomake256chars.) (1 row) SELECT * FROM pg_dist_node WHERE nodeport=8887; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster --------+---------+-----------+----------+----------+-------------+----------+-----------+----------------------------------------------------------------- - 21 | 12 | localhost | 8887 | default | f | t | secondary | thisisasixtyfourcharacterstringrepeatedfourtimestomake256chars. + 24 | 14 | localhost | 8887 | default | f | t | secondary | thisisasixtyfourcharacterstringrepeatedfourtimestomake256chars. (1 row) -- don't remove the secondary and unavailable nodes, check that no commands are sent to @@ -552,13 +630,13 @@ SELECT * FROM pg_dist_node WHERE nodeport=8887; SELECT master_add_secondary_node('localhost', 9995, 'localhost', :worker_1_port); master_add_secondary_node ------------------------------------------------------ - (22,12,localhost,9995,default,f,t,secondary,default) + (25,14,localhost,9995,default,f,t,secondary,default) (1 row) SELECT master_add_secondary_node('localhost', 9994, primaryname => 'localhost', primaryport => :worker_2_port); master_add_secondary_node ------------------------------------------------------ - (23,14,localhost,9994,default,f,t,secondary,default) + (26,16,localhost,9994,default,f,t,secondary,default) (1 row) SELECT master_add_secondary_node('localhost', 9993, 'localhost', 2000); @@ -566,7 +644,7 @@ ERROR: node at "localhost:2000" does not exist SELECT master_add_secondary_node('localhost', 9992, 'localhost', :worker_1_port, nodecluster => 'second-cluster'); master_add_secondary_node ------------------------------------------------------------- - (24,12,localhost,9992,default,f,t,secondary,second-cluster) + (27,14,localhost,9992,default,f,t,secondary,second-cluster) (1 row) SELECT nodeid AS worker_1_node FROM pg_dist_node WHERE nodeport=:worker_1_port \gset @@ -586,7 +664,7 @@ SELECT master_update_node(:worker_1_node, 'somehost', 9000); SELECT * FROM pg_dist_node WHERE nodeid = :worker_1_node; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster --------+---------+----------+----------+----------+-------------+----------+----------+------------- - 13 | 12 | somehost | 9000 | default | f | t | primary | default + 16 | 14 | somehost | 9000 | default | f | t | primary | default (1 row) -- cleanup @@ -599,6 +677,6 @@ SELECT master_update_node(:worker_1_node, 'localhost', :worker_1_port); SELECT * FROM pg_dist_node WHERE nodeid = :worker_1_node; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster --------+---------+-----------+----------+----------+-------------+----------+----------+------------- - 13 | 12 | localhost | 57637 | default | f | t | primary | default + 16 | 14 | localhost | 57637 | default | f | t | primary | default (1 row) diff --git a/src/test/regress/expected/multi_mx_modifications.out b/src/test/regress/expected/multi_mx_modifications.out index f3934e587..47a2873d9 100644 --- a/src/test/regress/expected/multi_mx_modifications.out +++ b/src/test/regress/expected/multi_mx_modifications.out @@ -467,7 +467,7 @@ INSERT INTO app_analytics_events_mx (app_id, name) VALUES (103, 'Mynt') RETURNIN SELECT setval('app_analytics_events_mx_id_seq'::regclass, :last_value); setval ------------------ - 3940649673949185 + 4503599627370497 (1 row) ALTER SEQUENCE app_analytics_events_mx_id_seq diff --git a/src/test/regress/expected/multi_mx_transaction_recovery.out b/src/test/regress/expected/multi_mx_transaction_recovery.out index 9b257681b..6b589370c 100644 --- a/src/test/regress/expected/multi_mx_transaction_recovery.out +++ b/src/test/regress/expected/multi_mx_transaction_recovery.out @@ -38,26 +38,26 @@ SELECT count(*) FROM pg_dist_transaction; SELECT * FROM pg_dist_local_group; groupid --------- - 12 + 14 (1 row) BEGIN; CREATE TABLE table_should_abort (value int); -PREPARE TRANSACTION 'citus_12_should_abort'; +PREPARE TRANSACTION 'citus_14_should_abort'; BEGIN; CREATE TABLE table_should_commit (value int); -PREPARE TRANSACTION 'citus_12_should_commit'; +PREPARE TRANSACTION 'citus_14_should_commit'; BEGIN; CREATE TABLE should_be_sorted_into_middle (value int); -PREPARE TRANSACTION 'citus_12_should_be_sorted_into_middle'; --- this node (e.g., node id 12) should not touch +PREPARE TRANSACTION 'citus_14_should_be_sorted_into_middle'; +-- this node (e.g., node id 14) should not touch -- transactions with different nodeIds in the gid BEGIN; CREATE TABLE table_should_do_nothing (value int); PREPARE TRANSACTION 'citus_122_should_do_nothing'; -- Add "fake" pg_dist_transaction records and run recovery -INSERT INTO pg_dist_transaction VALUES (12, 'citus_12_should_commit'); -INSERT INTO pg_dist_transaction VALUES (12, 'citus_12_should_be_forgotten'); +INSERT INTO pg_dist_transaction VALUES (14, 'citus_14_should_commit'); +INSERT INTO pg_dist_transaction VALUES (14, 'citus_14_should_be_forgotten'); INSERT INTO pg_dist_transaction VALUES (122, 'citus_122_should_do_nothing'); SELECT recover_prepared_transactions(); recover_prepared_transactions diff --git a/src/test/regress/sql/multi_cluster_management.sql b/src/test/regress/sql/multi_cluster_management.sql index f1d598ae2..01d43c839 100644 --- a/src/test/regress/sql/multi_cluster_management.sql +++ b/src/test/regress/sql/multi_cluster_management.sql @@ -57,10 +57,40 @@ SELECT master_get_active_worker_nodes(); -- try to disable a node which does not exist and see that an error is thrown SELECT master_disable_node('localhost.noexist', 2345); --- try to disable a node via non-super user CREATE USER non_super_user; -\c - non_super_user - :master_port -SELECT master_disable_node('localhost', :worker_1_port); +CREATE USER node_metadata_user; +GRANT EXECUTE ON FUNCTION master_activate_node(text,int) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_add_inactive_node(text,int,int,noderole,name) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_add_node(text,int,int,noderole,name) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_add_secondary_node(text,int,text,int,name) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_disable_node(text,int) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_remove_node(text,int) TO node_metadata_user; +GRANT EXECUTE ON FUNCTION master_update_node(int,text,int) TO node_metadata_user; + +-- try to manipulate node metadata via non-super user +SET ROLE non_super_user; +SELECT 1 FROM master_initialize_node_metadata(); +SELECT 1 FROM master_add_inactive_node('localhost', :worker_2_port + 1); +SELECT 1 FROM master_activate_node('localhost', :worker_2_port + 1); +SELECT 1 FROM master_disable_node('localhost', :worker_2_port + 1); +SELECT 1 FROM master_remove_node('localhost', :worker_2_port + 1); +SELECT 1 FROM master_add_node('localhost', :worker_2_port + 1); +SELECT 1 FROM master_add_secondary_node('localhost', :worker_2_port + 2, 'localhost', :worker_2_port); +SELECT master_update_node(nodeid, 'localhost', :worker_2_port + 3) FROM pg_dist_node WHERE nodeport = :worker_2_port; + +-- try to manipulate node metadata via privileged user +SET ROLE node_metadata_user; +BEGIN; +SELECT 1 FROM master_add_inactive_node('localhost', :worker_2_port + 1); +SELECT 1 FROM master_activate_node('localhost', :worker_2_port + 1); +SELECT 1 FROM master_disable_node('localhost', :worker_2_port + 1); +SELECT 1 FROM master_remove_node('localhost', :worker_2_port + 1); +SELECT 1 FROM master_add_node('localhost', :worker_2_port + 1); +SELECT 1 FROM master_add_secondary_node('localhost', :worker_2_port + 2, 'localhost', :worker_2_port); +SELECT master_update_node(nodeid, 'localhost', :worker_2_port + 3) FROM pg_dist_node WHERE nodeport = :worker_2_port; +SELECT nodename, nodeport, noderole FROM pg_dist_node ORDER BY nodeport; +ABORT; + \c - postgres - :master_port SELECT master_get_active_worker_nodes(); diff --git a/src/test/regress/sql/multi_mx_transaction_recovery.sql b/src/test/regress/sql/multi_mx_transaction_recovery.sql index ea12e40dd..5cc3a6f6b 100644 --- a/src/test/regress/sql/multi_mx_transaction_recovery.sql +++ b/src/test/regress/sql/multi_mx_transaction_recovery.sql @@ -26,25 +26,25 @@ SELECT * FROM pg_dist_local_group; BEGIN; CREATE TABLE table_should_abort (value int); -PREPARE TRANSACTION 'citus_12_should_abort'; +PREPARE TRANSACTION 'citus_14_should_abort'; BEGIN; CREATE TABLE table_should_commit (value int); -PREPARE TRANSACTION 'citus_12_should_commit'; +PREPARE TRANSACTION 'citus_14_should_commit'; BEGIN; CREATE TABLE should_be_sorted_into_middle (value int); -PREPARE TRANSACTION 'citus_12_should_be_sorted_into_middle'; +PREPARE TRANSACTION 'citus_14_should_be_sorted_into_middle'; --- this node (e.g., node id 12) should not touch +-- this node (e.g., node id 14) should not touch -- transactions with different nodeIds in the gid BEGIN; CREATE TABLE table_should_do_nothing (value int); PREPARE TRANSACTION 'citus_122_should_do_nothing'; -- Add "fake" pg_dist_transaction records and run recovery -INSERT INTO pg_dist_transaction VALUES (12, 'citus_12_should_commit'); -INSERT INTO pg_dist_transaction VALUES (12, 'citus_12_should_be_forgotten'); +INSERT INTO pg_dist_transaction VALUES (14, 'citus_14_should_commit'); +INSERT INTO pg_dist_transaction VALUES (14, 'citus_14_should_be_forgotten'); INSERT INTO pg_dist_transaction VALUES (122, 'citus_122_should_do_nothing'); SELECT recover_prepared_transactions();